92 lines
2.9 KiB
Python
92 lines
2.9 KiB
Python
from collections import defaultdict
|
|
|
|
from tclint.syntax_tree import Visitor
|
|
from tclint.violations import ALL_RULES, Rule
|
|
|
|
|
|
class CommentVisitor(Visitor):
|
|
"""Scans the tree for lint waiver comments."""
|
|
|
|
def __init__(self):
|
|
# line -> [rule]
|
|
self.ignore_lines = defaultdict(set)
|
|
|
|
self._disable_regions = {
|
|
# rule -> line
|
|
}
|
|
|
|
def run(self, tree, path):
|
|
self._path = path
|
|
tree.accept(self, recurse=True)
|
|
|
|
# resolve remaining disabled regions
|
|
last_line = tree.end_pos[0]
|
|
for rule, start_line in self._disable_regions.items():
|
|
for line in range(start_line, last_line + 1):
|
|
self.ignore_lines[line].add(rule)
|
|
|
|
return self.ignore_lines
|
|
|
|
def visit_comment(self, comment):
|
|
contents = comment.value.strip()
|
|
|
|
if not contents.startswith("tclint-"):
|
|
return
|
|
|
|
split = contents.split(" ", 1)
|
|
|
|
command = split[0]
|
|
|
|
rule_strs = []
|
|
if len(split) > 1:
|
|
rest = split[-1]
|
|
rule_strs = rest.split("--", 1)[0]
|
|
rule_strs = rule_strs.replace(" ", "")
|
|
rule_strs = rule_strs.split(",")
|
|
|
|
rules = []
|
|
if not rule_strs:
|
|
# default if no rules specified is all violation types
|
|
rules = ALL_RULES
|
|
else:
|
|
for rule in rule_strs:
|
|
try:
|
|
rules.append(Rule(rule))
|
|
except ValueError:
|
|
self._warning(
|
|
f"unknown rule '{rule}' provided to '{command}'", comment.pos
|
|
)
|
|
|
|
if command == "tclint-disable":
|
|
for rule in rules:
|
|
# if in dictionary, already disabled - this has no effect
|
|
if rule not in self._disable_regions:
|
|
self._disable_regions[rule] = comment.line
|
|
elif command == "tclint-disable-line":
|
|
line = comment.line
|
|
self.ignore_lines[line].update(rules)
|
|
elif command == "tclint-disable-next-line":
|
|
line = comment.line + 1
|
|
self.ignore_lines[line].update(rules)
|
|
elif command == "tclint-enable":
|
|
for rule in rules:
|
|
if rule in self._disable_regions:
|
|
disable_start_line = self._disable_regions[rule]
|
|
disable_end_line = comment.line
|
|
|
|
for line in range(disable_start_line, disable_end_line + 1):
|
|
self.ignore_lines[line].add(rule)
|
|
|
|
del self._disable_regions[rule]
|
|
else:
|
|
self._warning(
|
|
f"comment starts with '{command}', which looks like a tclint keyword."
|
|
" Is this a typo?",
|
|
comment.pos,
|
|
)
|
|
|
|
def _warning(self, message, pos):
|
|
# TODO: formal warning mechanism
|
|
prefix = self._path if self._path is not None else "(stdin)"
|
|
print(f"Warning: {prefix}:{pos[0]}:{pos[1]}: {message}")
|