Skip to content
This repository has been archived by the owner on Jul 3, 2024. It is now read-only.

Commit

Permalink
merge: #200 from feature/184-ignore-rule-comments-staging
Browse files Browse the repository at this point in the history
184 - Ignore rule comments
  • Loading branch information
0xmemorygrinder authored Dec 1, 2023
2 parents 8e32d75 + a4c0305 commit 296afcc
Show file tree
Hide file tree
Showing 12 changed files with 305 additions and 12 deletions.
6 changes: 3 additions & 3 deletions toolchains/solidity/core/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions toolchains/solidity/core/crates/linter-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
name = "solidhunter"
description = "Fast solidity linter cli"
repository = "https://github.com/astrodevs-labs/osmium"
version = "0.2.2"
version = "0.2.3"
edition = "2021"
license = "GPL-3.0-or-later"
authors = ["AstroDevs-Labs"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
solidhunter-lib = { path = "../linter-lib", version = "0.2.0" }
solidhunter-lib = { path = "../linter-lib", version = "0.2.1" }
clap = { version = "4.0.29", features = ["derive"] }
colored = "2"
serde = { version = "1.0.149", features = ["derive"] }
Expand Down
2 changes: 1 addition & 1 deletion toolchains/solidity/core/crates/linter-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "solidhunter-lib"
description = "Solidhunter/Osmium solidity linter library"
repository = "https://github.com/astrodevs-labs/osmium"
version = "0.2.0"
version = "0.2.1"
edition = "2021"
authors = ["Astrodevs Labs"]
license = "GPL-3.0-or-later"
Expand Down
92 changes: 90 additions & 2 deletions toolchains/solidity/core/crates/linter-lib/src/linter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,103 @@ impl SolidLinter {
self.parse_content(&filepath, content.as_str())
}

fn _check_is_in_disable_range(&self, diag: &LintDiag, disable_ranges: &[DisableRange]) -> bool {
let mut rules_occurrences = vec![];

let filtered_range = disable_ranges
.iter()
// we only care about ranges that start before the diag
.filter(|range| range.start_line <= diag.range.start.line)
.map(|range| {
if range.rule_ids.is_empty() {
DisableRange {
rule_ids: vec!["".to_string()], // empty rule means all rules
..range.clone()
}
} else {
range.clone()
}
})
.collect::<Vec<DisableRange>>();

for range in &filtered_range {
match range.ignore_type {
Ignore::SameLine | Ignore::NextLine => {
if range.start_line == diag.range.start.line
&& (range.rule_ids.contains(&diag.id)
|| range.rule_ids.contains(&"".to_string()))
{
return true;
}
}
Ignore::Disable => {
for rule in &range.rule_ids {
let mut found = false;
for (rule_id, occurences) in &mut rules_occurrences {
if *rule_id == rule {
*occurences += 1;
found = true;
break;
}
}
if !found {
rules_occurrences.push((rule, 1));
}
}
}
Ignore::Enable => {
for rule in &range.rule_ids {
for (rule_id, occurences) in &mut rules_occurrences {
if *rule_id == rule {
*occurences -= 1;
break;
}
}
// TODO: global disable followed by a scoped enable might not work
}
}
}
}

let disabled_rules = rules_occurrences
.iter()
.filter(|(_, occurences)| *occurences > 0)
.map(|(rule, _)| rule.to_string())
.collect::<Vec<String>>();

for rule in disabled_rules {
if rule.is_empty() || rule == diag.id {
return true;
}
}
false
}

fn _check_is_diag_ignored(&self, diag: &LintDiag, file: &SolidFile) -> bool {
let ignore_comments: Vec<IgnoreComment> = file
.content
.lines()
.enumerate()
.filter_map(|(line_number, line)| IgnoreComment::from_line(line_number + 1, line))
.collect();
let disable_ranges = build_disable_ranges(ignore_comments);

self._check_is_in_disable_range(diag, &disable_ranges)
}

pub fn parse_content(&mut self, filepath: &str, content: &str) -> LintResult {
let res = osmium_libs_solidity_ast_extractor::extract::extract_ast_from_content(content)?;

self._add_file(filepath, res, content);
let mut res: Vec<LintDiag> = Vec::new();
let mut res: Vec<_> = vec![];

for rule in &self.rules {
let mut diags = rule.diagnose(&self.files[self.files.len() - 1], &self.files);
res.append(&mut diags);
for diag in &mut diags {
if !self._check_is_diag_ignored(diag, &self.files[self.files.len() - 1]) {
res.push(diag.clone());
}
}
}
Ok(FileDiags::new(content.to_string(), res))
}
Expand Down
2 changes: 2 additions & 0 deletions toolchains/solidity/core/crates/linter-lib/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ mod lint_diag;
pub use lint_diag::LintDiag;
mod file_diags;
pub use file_diags::FileDiags;
mod ignore;
pub use ignore::*;

pub type LintResult = Result<FileDiags, SolidHunterError>;

Expand Down
127 changes: 127 additions & 0 deletions toolchains/solidity/core/crates/linter-lib/src/types/ignore.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use serde::{Deserialize, Serialize};

macro_rules! define_ignore_enum {
($name:ident, $($variant:ident => $str:expr),* $(,)?) => {
#[derive(PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Debug)]
pub enum $name {
$($variant),*
}

impl ToString for $name {
fn to_string(&self) -> String {
match self {
$(Self::$variant => $str),*
}
.to_string()
}
}

impl $name {
pub fn iter() -> impl Iterator<Item = Self> {
[$(Self::$variant),*].iter().copied()
}
}
};
}

define_ignore_enum! {
Ignore,
NextLine => "solidhunter-disable-next-line",
SameLine => "solidhunter-disable-line",
Disable => "solidhunter-disable",
Enable => "solidhunter-enable",
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IgnoreComment {
pub line_number: usize,
pub ignore_type: Ignore,
pub rule_ids: Option<Vec<String>>,
}

impl IgnoreComment {
pub fn from_line(line_number: usize, line: &str) -> Option<Self> {
for ignore in Ignore::iter() {
let ignore_str = ignore.to_string();
if line.contains(&ignore_str) {
let rule_ids_str = line.split(&ignore_str).nth(1);
let rule_ids = rule_ids_str
.map(|s| {
s.split(' ')
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
.collect::<Vec<String>>()
})
.filter(|s| !s.is_empty());
return Some(Self {
line_number,
ignore_type: ignore,
rule_ids,
});
}
}
None
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DisableRange {
pub start_line: usize,
pub end_line: usize,
pub ignore_type: Ignore,
pub rule_ids: Vec<String>,
}

pub fn build_disable_ranges(comments: Vec<IgnoreComment>) -> Vec<DisableRange> {
let mut disable_ranges = vec![];
let mut current_range: Option<DisableRange> = None;

for comment in comments {
let line_number = comment.line_number;
let ignore_type = comment.ignore_type;
let rule_ids = comment.rule_ids.unwrap_or(vec![]);

match ignore_type {
Ignore::Enable | Ignore::Disable => {
if let Some(range) = current_range {
disable_ranges.push(range);
}
current_range = Some(DisableRange {
start_line: line_number,
end_line: line_number,
ignore_type,
rule_ids,
});
}
Ignore::SameLine => {
if let Some(range) = &mut current_range {
range.end_line = line_number;
}
disable_ranges.push(DisableRange {
start_line: line_number,
end_line: line_number,
ignore_type,
rule_ids,
});
}
Ignore::NextLine => {
if let Some(range) = &mut current_range {
range.end_line = line_number;
}
disable_ranges.push(DisableRange {
start_line: line_number + 1,
end_line: line_number + 1,
ignore_type,
rule_ids,
});
}
}
}

if let Some(range) = current_range {
disable_ranges.push(range);
}

disable_ranges
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "solidhunter", "rules": [
{
"id": "avoid-tx-origin",
"severity": "WARNING"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
pragma solidity 0.8.0;

contract Test {
function awesome() public returns (address) {
// solidhunter-disable-next-line exist-with-error
return tx.origin; // solidhunter-disable-line exist-with-error
}

function awesomeLineUp() public returns (address) {
// solidhunter-disable-next-line avoid-tx-origin
return tx.origin;
}
function awesomeSameLine() public returns (address) {
return tx.origin; // solidhunter-disable-line avoid-tx-origin
}

function notAwesome() public returns (address) {
// solidhunter-disable-next-line not-exist-no-error
return msg.sender; // solidhunter-disable-line not-exist-no-error
}

function awesomeLineUpAny() public returns (address) {
// solidhunter-disable-next-line
return tx.origin;
}
function awesomeSameLineAny() public returns (address) {
return tx.origin; // solidhunter-disable-line
}

function awesomeLineUpAny() public returns (address) {
// solidhunter-disable-next-line dummy-rule avoid-tx-origin dummy-rule2
return tx.origin;
}
function awesomeSameLineAny() public returns (address) {
return tx.origin; // solidhunter-disable-line dummy-rule avoid-tx-origin dummy-rule2
}

// solidhunter-disable
function awesome() public returns (address) {
return tx.origin;
}
// solidhunter-enable


// solidhunter-disable
// solidhunter-disable
// solidhunter-enable
function awesome() public returns (address) {
return tx.origin;
}
// solidhunter-enable

// solidhunter-disable avoid-tx-origin
function awesome() public returns (address) {
return tx.origin;
}
// solidhunter-enable avoid-tx-origin

// solidhunter-disable avoid-tx-origin
// solidhunter-disable
// solidhunter-enable avoid-tx-origin
function awesome() public returns (address) {
return tx.origin;
}
// solidhunter-enable
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
avoid-tx-origin:6:15:6:24
3 changes: 2 additions & 1 deletion toolchains/solidity/core/crates/linter-lib/tests/linter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ test_directories! {
Ordering,
PrivateVarsLeadingUnderscore,
FoundryTestFunctions,
AvoidTxOrigin
AvoidTxOrigin,
SolidHunterIgnoreRule,
}

#[allow(non_snake_case)]
Expand Down
Loading

0 comments on commit 296afcc

Please sign in to comment.