Skip to content

Commit

Permalink
Magic comments to turn off specific detections (#17)
Browse files Browse the repository at this point in the history
Added magic comment functionality to turn off specific detections.
  • Loading branch information
faculerena authored Jul 8, 2024
1 parent b1a14f9 commit be22867
Show file tree
Hide file tree
Showing 20 changed files with 235 additions and 146 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ test_ci: venv
@echo -e "${GREEN}======== Installing Stacy for Clarity ========${NC}"
./venv/bin/pip install $(REPO_ROOT)
@echo -e "${GREEN}======== Testing detectors ========${NC}"
cd tests/ && ../venv/bin/python3 -m unittest test_module1 > $(GITHUB_WORKSPACE)/test.out 2>&1 && cd ..
cd tests && ../venv/bin/python3 -m unittest test_module1 > $(GITHUB_WORKSPACE)/test.out 2>&1 && cd ..

unittest: venv
./venv/bin/pip uninstall stacy-analyzer -y > /dev/null 2>&1
Expand Down
24 changes: 2 additions & 22 deletions src/stacy_analyzer/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,9 @@
import sys
from importlib.util import spec_from_file_location, module_from_spec

from stacy_analyzer.linter_runner import LinterRunner
from stacy_analyzer.print_message import TerminalColors
from stacy_analyzer.visitor import LinterRunner, Visitor, Finding


class Number(object):
def __init__(self, n):
self.value = n

def val(self):
return self.value

def add(self, n2):
self.value += n2.val()

def __add__(self, n2):
return self.__class__(self.value + n2.val())

def __str__(self):
return str(self.val())

@classmethod
def addall(cls, number_obj_iter):
cls(sum(n.val() for n in number_obj_iter))
from stacy_analyzer.visitor import Visitor, Finding


class Analyzer:
Expand Down
4 changes: 2 additions & 2 deletions src/stacy_analyzer/detectors/AssertBlockHeight.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from tree_sitter import Node

from stacy_analyzer.visitor import Visitor, NodeIterator
from stacy_analyzer.node_iterator import NodeIterator
from stacy_analyzer.visitor import Visitor


class AssertBlockHeight(Visitor):
Expand Down
3 changes: 2 additions & 1 deletion src/stacy_analyzer/detectors/CallInsideAsContract.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from tree_sitter import Node
from stacy_analyzer.visitor import Visitor
from stacy_analyzer.node_iterator import NodeIterator

from stacy_analyzer.visitor import Visitor, NodeIterator


class CallInsideAsContract(Visitor):
Expand Down
3 changes: 2 additions & 1 deletion src/stacy_analyzer/detectors/DivideBeforeMultiply.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from tree_sitter import Node
from stacy_analyzer.visitor import Visitor
from stacy_analyzer.node_iterator import NodeIterator

from stacy_analyzer.visitor import Visitor, NodeIterator


class DivideBeforeMultiply(Visitor):
Expand Down
1 change: 0 additions & 1 deletion src/stacy_analyzer/detectors/PrivateFunctionNotUsed.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from tree_sitter import Node

from stacy_analyzer.visitor import Visitor


Expand Down
1 change: 0 additions & 1 deletion src/stacy_analyzer/detectors/ToDoComment.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from tree_sitter import Node

from stacy_analyzer.visitor import Visitor


Expand Down
3 changes: 2 additions & 1 deletion src/stacy_analyzer/detectors/TxSenderInAssert.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from tree_sitter import Node
from stacy_analyzer.visitor import Visitor
from stacy_analyzer.node_iterator import NodeIterator

from stacy_analyzer.visitor import Visitor, NodeIterator


class TxSenderInAssert(Visitor):
Expand Down
3 changes: 2 additions & 1 deletion src/stacy_analyzer/detectors/UnusedArguments.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from tree_sitter import Node
from stacy_analyzer.visitor import Visitor, NodeIterator
from stacy_analyzer.visitor import Visitor
from stacy_analyzer.node_iterator import NodeIterator


class UnusedArguments(Visitor):
Expand Down
4 changes: 3 additions & 1 deletion src/stacy_analyzer/detectors/UnusedLetVariables.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from tree_sitter import Node
from stacy_analyzer.visitor import Visitor, NodeIterator
from stacy_analyzer.visitor import Visitor
from stacy_analyzer.node_iterator import NodeIterator


class UnusedLetVariables(Visitor):
Expand Down Expand Up @@ -30,3 +31,4 @@ def visit_node(self, node: Node, i):
self.HELP = ""
self.FOOTNOTE = f"Consider removing '{k}' from let function since its not used."
self.add_finding(v, v)

1 change: 0 additions & 1 deletion src/stacy_analyzer/detectors/UnwrapPanicUsage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from tree_sitter import Node

from stacy_analyzer.visitor import Visitor


Expand Down
1 change: 0 additions & 1 deletion src/stacy_analyzer/detectors/UpdatedFunctions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from tree_sitter import Node

from stacy_analyzer.visitor import Visitor


Expand Down
1 change: 0 additions & 1 deletion src/stacy_analyzer/detectors/VarCouldBeConstant.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from tree_sitter import Node

from stacy_analyzer.visitor import Visitor


Expand Down
119 changes: 119 additions & 0 deletions src/stacy_analyzer/linter_runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import re

import tree_sitter_clarity
from stacy_analyzer.print_message import TerminalColors
from tree_sitter import Language, Tree, Node, Parser

__CLARITY__ = Language(tree_sitter_clarity.language())
__TIMES__ = 3

from stacy_analyzer.visitor import Visitor
from stacy_analyzer.node_iterator import NodeIterator


class LinterRunner:
source: str
tree: Tree
root_node: Node
iterator: NodeIterator
lints: [Visitor]
round_number: int
allowed_lints: dict[str, ([int], Node)] = {}

def __init__(self, source: str, print_output: bool, src_name: str):
self.src_name = src_name
self.source = source
parser = Parser(__CLARITY__)
self.tree = parser.parse(bytes(self.source, "utf8"))
self.root_node = self.tree.root_node
self.iterator = NodeIterator(self.root_node)
self.lints = []
self.round_number = 0
self.print_output = print_output
self.allowed_lints = {}

def run_lints(self, node: Node):
for lint in self.lints:
lint.visit_node(node, self.round_number)

def add_lint(self, lint):
self.lints.append(lint)
return self

def add_lints(self, lint_classes: [Visitor]):
for lint_class in lint_classes:
lint = lint_class(self.print_output)
lint.add_source(self.source, self.src_name)
self.lints.append(lint)

def reset_cursor(self):
self.iterator = NodeIterator(self.root_node)

def run(self):
self.pre_run_checks()
for i in range(__TIMES__):
self.round_number = self.round_number + 1

for node in self.iterator:
self.run_lints(node)
self.reset_cursor()

self.post_run_checks()

return [finding for lint in self.lints
for finding in lint.get_findings()]

def pre_run_checks(self):
for node in self.iterator:
allowed: [AllowedComment] = check_comments(node)
if not allowed:
continue
for allowed_comment in allowed:
if allowed_comment.name not in self.allowed_lints:
self.allowed_lints[allowed_comment.name] = (
[allowed_comment.line_number], allowed_comment.comment_node)
else:
self.allowed_lints[allowed_comment.name][0].append(allowed_comment.line_number)

self.reset_cursor()
for lint in self.lints:
lint.set_ignores(self.allowed_lints)

def post_run_checks(self):
for lint in self.lints:
findings: dict[str, ([int], Node)] = lint.get_ignored_findings()
name = lint.Name
if name not in self.allowed_lints or findings[name][0] == []:
continue
warn_magic_comment(findings[name][0], name)


class AllowedComment:
name: str
comment_node: Node
line_number: int

def __init__(self, finding: str, comment_node: Node):
self.name = finding
self.comment_node = comment_node
self.line_number = comment_node.range.start_point.row + 1


def extract_detectors(comment_text: str) -> [str]:
allow_comment = re.search(r'#\[allow\((.*?)\)]', comment_text)
return allow_comment.group(1).split(',') if allow_comment else []


def check_comments(node) -> [AllowedComment]:
if node.grammar_name != "comment":
return []
comment_text = node.text.decode("utf-8")
detectors = extract_detectors(comment_text)

return [AllowedComment(detector.strip(), node) for detector in detectors]


def warn_magic_comment(lines: [int], name: str):
for line in lines:
print(f"{TerminalColors.ERROR}[WARNING]{TerminalColors.ENDC} "
f"Magic comment in line {line} for detector {TerminalColors.BOLD}{name}{TerminalColors.ENDC} is not used")
49 changes: 49 additions & 0 deletions src/stacy_analyzer/node_iterator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from tree_sitter import Node, TreeCursor


class NodeIterator:
root_node: Node
cursor: TreeCursor
visited = []

def __init__(self, node: Node):
self.root_node = node
self.cursor = node.walk()
self.visited = []

while self.cursor.goto_first_child():
pass

def next(self) -> Node | None:
while True:
node = self.node()

if node not in self.visited:
if self.cursor.goto_first_child():
continue
self.visited.append(node)
return node

if self.cursor.goto_next_sibling():
while self.cursor.goto_first_child():
pass
else:

if not self.cursor.goto_parent():
return None
parent_node = self.cursor.node
self.visited.append(parent_node)
return parent_node

def node(self) -> Node | None:
return self.cursor.node

def __iter__(self):
return self

def __next__(self) -> Node | None:
node = self.next()
if node is None:
raise StopIteration
return node

2 changes: 2 additions & 0 deletions src/stacy_analyzer/print_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
class TerminalColors:
HEADER = '\033[95m' if tty else ''
OKCYAN = '\033[96m' if tty else ''
ERROR = '\033[31;1;4m' if tty else ''
BOLD = '\033[1m' if tty else ''
ENDC = '\033[0m' if tty else ''


Expand Down
2 changes: 1 addition & 1 deletion src/stacy_analyzer/tree-sitter-clarity
Loading

0 comments on commit be22867

Please sign in to comment.