Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
129 commits
Select commit Hold shift + click to select a range
11ee263
Create rule_disabler.py
Beakerboy Feb 20, 2024
64346a2
Update and rename src/vba_linter/rules/listeners/rule_disabler.py to …
Beakerboy Feb 20, 2024
434101a
Update rule_disabler.py
Beakerboy Feb 20, 2024
0d60276
Update rule_disabler.py
Beakerboy Feb 20, 2024
e36e107
Update rule_disabler.py
Beakerboy Feb 20, 2024
79d4074
Update rule_disabler.py
Beakerboy Feb 20, 2024
b295523
Update rule_disabler.py
Beakerboy Feb 20, 2024
4890201
Update rule_disabler.py
Beakerboy Feb 20, 2024
bd14cb8
Update rule_disabler.py
Beakerboy Feb 20, 2024
cda8cb8
Update rule_disabler.py
Beakerboy Feb 20, 2024
6338edf
Update rule_disabler.py
Beakerboy Feb 20, 2024
44f095a
Update rule_disabler.py
Beakerboy Feb 20, 2024
edb66c9
Update rule_disabler.py
Beakerboy Feb 20, 2024
7941431
Update rule_disabler.py
Beakerboy Feb 20, 2024
f59327f
Update rule_disabler.py
Beakerboy Feb 20, 2024
85289bb
Update test_main.py
Beakerboy Feb 20, 2024
367acce
Update test_main.py
Beakerboy Feb 20, 2024
6b5b1d8
Update test_main.py
Beakerboy Feb 20, 2024
334f527
Update test_main.py
Beakerboy Feb 20, 2024
d2fcd7a
Update rule_directory.py
Beakerboy Feb 20, 2024
8910830
Update test_main.py
Beakerboy Feb 20, 2024
a48d9e0
Update linter.py
Beakerboy Feb 20, 2024
d1baff2
Update linter.py
Beakerboy Feb 20, 2024
bf9a194
Update rule_disabler.py
Beakerboy Feb 20, 2024
3f4b435
Update linter.py
Beakerboy Feb 20, 2024
e39f8b9
Update linter.py
Beakerboy Feb 20, 2024
6a1f77f
Update rule_disabler.py
Beakerboy Feb 20, 2024
587d4e9
Update rule_disabler.py
Beakerboy Feb 20, 2024
b801150
Update rule_disabler.py
Beakerboy Feb 20, 2024
fd0660a
Update linter.py
Beakerboy Feb 20, 2024
efdce2e
Update rule_disabler.py
Beakerboy Feb 20, 2024
a713652
Update linter.py
Beakerboy Feb 20, 2024
350c21b
Update rule_disabler.py
Beakerboy Feb 20, 2024
47029a8
Update rule_disabler.py
Beakerboy Feb 20, 2024
3390335
Update rule_disabler.py
Beakerboy Feb 20, 2024
9065198
Update rule_disabler.py
Beakerboy Feb 20, 2024
1b0d8d7
Update linter.py
Beakerboy Feb 20, 2024
8db2c87
Update rule_disabler.py
Beakerboy Feb 20, 2024
2b62054
Update linter.py
Beakerboy Feb 20, 2024
5f4a9b9
Update linter.py
Beakerboy Feb 20, 2024
1c4c053
Update rule_directory.py
Beakerboy Feb 20, 2024
d609d08
Update rule_directory.py
Beakerboy Feb 20, 2024
9814057
Update rule_disabler.py
Beakerboy Feb 20, 2024
4c24d3f
Update rule_disabler.py
Beakerboy Feb 20, 2024
fd1e87f
Update rule_disabler.py
Beakerboy Feb 20, 2024
151ebe3
Update rule_directory.py
Beakerboy Feb 20, 2024
7d1e458
Update test_rule_directory.py
Beakerboy Feb 20, 2024
7b12441
Update linter.py
Beakerboy Feb 20, 2024
d60eda8
Update rule_disabler.py
Beakerboy Feb 20, 2024
5889668
Update rule_disabler.py
Beakerboy Feb 20, 2024
09ea685
Update rule_disabler.py
Beakerboy Feb 20, 2024
56e0b94
Update rule_disabler.py
Beakerboy Feb 20, 2024
4abaa2b
Update rule_disabler.py
Beakerboy Feb 20, 2024
5b1a5e3
Update linter.py
Beakerboy Feb 20, 2024
fd3dded
Update test_line_ending.py
Beakerboy Feb 20, 2024
a012d36
Create login.frm
Beakerboy Feb 24, 2024
8529c95
Create test_form.py
Beakerboy Feb 24, 2024
ab679d7
Update test_form.py
Beakerboy Feb 24, 2024
884ded6
Update test_form.py
Beakerboy Feb 24, 2024
386c19b
Update test_form.py
Beakerboy Feb 24, 2024
056b612
Update and rename test_form.py to test_form.py
Beakerboy Feb 24, 2024
b07b8f6
Update rule_disabler.py
Beakerboy Feb 24, 2024
4bfa410
Update test_form.py
Beakerboy Feb 24, 2024
39800fa
Update test_form.py
Beakerboy Feb 24, 2024
1dbfcdd
Update test_form.py
Beakerboy Feb 24, 2024
6d302bb
Update test_form.py
Beakerboy Feb 24, 2024
3fe8646
Update test_form.py
Beakerboy Feb 24, 2024
302759a
Update test_form.py
Beakerboy Feb 24, 2024
37f5530
Update login.frm
Beakerboy Feb 24, 2024
bb326e7
Update test_form.py
Beakerboy Feb 24, 2024
c62d8de
Update test_linter.py
Beakerboy Feb 24, 2024
add4a8e
Update test_linter.py
Beakerboy Feb 24, 2024
ae5d554
Update test_linter.py
Beakerboy Feb 24, 2024
9c6713d
Update rule_disabler.py
Beakerboy Feb 24, 2024
ddb2dc4
Update rule_directory.py
Beakerboy Feb 24, 2024
e53b885
Update README.md
Beakerboy Feb 24, 2024
56d096a
Update test_optional_let.py
Beakerboy Feb 24, 2024
7ef1e5f
Update test_missing_let.py
Beakerboy Feb 24, 2024
7fb3258
Update test_main.py
Beakerboy Feb 24, 2024
43ed609
Update rule_disabler.py
Beakerboy Feb 24, 2024
b5f72cb
Update missing_let.py
Beakerboy Feb 24, 2024
ae5b7ac
Update optional_let.py
Beakerboy Feb 24, 2024
a3a5dfa
Update test_form.py
Beakerboy Feb 24, 2024
ea9803a
Update rule_directory.py
Beakerboy Feb 24, 2024
6eb6de9
Update README.md
Beakerboy Feb 24, 2024
81f29a1
Update test_newline_eof.py
Beakerboy Feb 24, 2024
82bb2e3
Update newline_eof.py
Beakerboy Feb 24, 2024
5d09916
Update test_form.py
Beakerboy Feb 25, 2024
2309af9
Update rule_disabler.py
Beakerboy Feb 25, 2024
c4e55bf
Update test_form.py
Beakerboy Feb 25, 2024
54b5136
Update test_main.py
Beakerboy Feb 25, 2024
f718391
Update test_main.py
Beakerboy Feb 25, 2024
ecb6f8e
Update test_main.py
Beakerboy Feb 25, 2024
8f5a6f5
Update test_main.py
Beakerboy Feb 25, 2024
317e8d8
Update test_main.py
Beakerboy Feb 25, 2024
20aa0f9
Update test_main.py
Beakerboy Feb 25, 2024
a1fce07
Update test_main.py
Beakerboy Feb 25, 2024
9a5eb50
Update linter.py
Beakerboy Feb 25, 2024
637d17f
Update test_main.py
Beakerboy Feb 25, 2024
591bfce
Update test_main.py
Beakerboy Feb 25, 2024
b18f409
Update linter.py
Beakerboy Feb 25, 2024
d11f53d
Update linter.py
Beakerboy Feb 25, 2024
1decd76
Update linter.py
Beakerboy Feb 25, 2024
9451607
Update linter.py
Beakerboy Feb 25, 2024
4c37947
Update linter.py
Beakerboy Feb 25, 2024
d263bc2
Update test_main.py
Beakerboy Feb 25, 2024
2e15e72
Update linter.py
Beakerboy Feb 25, 2024
f921c3d
Update test_main.py
Beakerboy Feb 25, 2024
97c2acb
Update rule_disabler.py
Beakerboy Feb 25, 2024
2562ea6
Update test_form.py
Beakerboy Feb 25, 2024
33ae28a
Create test_ignore.py
Beakerboy Feb 25, 2024
4924ea7
Update test_ignore.py
Beakerboy Feb 25, 2024
313a947
Update test_ignore.py
Beakerboy Feb 25, 2024
1decc99
Update test_ignore.py
Beakerboy Feb 25, 2024
4cbf660
Update test_ignore.py
Beakerboy Feb 25, 2024
969e279
Update test_ignore.py
Beakerboy Feb 26, 2024
21e12fa
Update test_ignore.py
Beakerboy Feb 26, 2024
abc77fb
Update rule_disabler.py
Beakerboy Feb 26, 2024
9531816
Update rule_disabler.py
Beakerboy Feb 26, 2024
c41017a
Update rule_disabler.py
Beakerboy Feb 26, 2024
a67ddea
Update vbaListener.py
Beakerboy Feb 26, 2024
7779670
Update vbaListener.py
Beakerboy Feb 26, 2024
b1c965e
Update vbaListener.py
Beakerboy Feb 26, 2024
0764c93
Update vbaListener.py
Beakerboy Feb 26, 2024
40347fb
Update test_ignore.py
Beakerboy Feb 26, 2024
4a5cbc0
Update test_ignore.py
Beakerboy Feb 26, 2024
70d9d3f
Update test_ignore.py
Beakerboy Feb 26, 2024
1dee738
Update test_ignore.py
Beakerboy Feb 26, 2024
4f7e41d
Update test_ignore.py
Beakerboy Feb 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ Check that code parses correctly. If so, check that the formatting meets a speci
* 182 Whitespace characters after comparison operator
* 183 Tabs after comparison operator
### 200 Parameter errors
* 110 Missing Let
* 111 Optional Let
* 201 Missing Let
* 202 Optional Let
* 210 parameter naming
* 220 Keyword not capitalized
* function / sub naming
Expand Down Expand Up @@ -74,7 +74,7 @@ Check that code parses correctly. If so, check that the formatting meets a speci
### 700 Module Errors
* Blank line begining file
* blank line end of file
* missing final eol
* 701 missing final eol
* 601 missing module attributes
* 602 Missing module declarations
### 800 Documentation
Expand Down
22 changes: 20 additions & 2 deletions src/vba_linter/linter.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ def lint(self: T, dir: RuleDirectory, code: str) -> list:
token = ts.LT(1)
assert token is not None
while not token.type == Token.EOF:
# Walk the stream and test the token against
# each rule.
for key in rules:
rule = rules[key]
output.extend(rule.test(ts))
Expand All @@ -52,9 +54,25 @@ def lint(self: T, dir: RuleDirectory, code: str) -> list:
listener.listeners = dir.get_parser_rules()
ParseTreeWalker.DEFAULT.walk(listener, program)
output.extend(listener.get_output())
output.sort()
# Get the ignore list and remove violations
# that should be removed.
rule_disabler = dir.get_rule_disabler()
ignored = rule_disabler.ignored
filtered_output = []
if len(ignored) > 0:
for violation in output:
violated_rule = violation[2]
if violated_rule in ignored:
violation_line = violation[0]
if violation_line not in ignored[violated_rule]:
filtered_output.append(violation)
else:
filtered_output.append(violation)
else:
filtered_output = output
filtered_output.sort()
self.pretty = ts
return output
return filtered_output

def get_pretty_code(self: T) -> str:
code = ""
Expand Down
11 changes: 9 additions & 2 deletions src/vba_linter/rule_directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from vba_linter.rules.listeners.missing_visibility import MissingVisibility
from vba_linter.rules.listeners.missing_let import MissingLet
from vba_linter.rules.listeners.optional_let import OptionalLet
from vba_linter.rule_disabler import RuleDisabler


T = TypeVar('T', bound='RuleDirectory')
Expand All @@ -35,13 +36,19 @@ def __init__(self: T) -> None:
# load config file.
self._rules: Dict[str, RuleBase] = {}
self._parser_rules: Dict[str, ParseTreeListener] = {}
self.add_rule(RuleDisabler())

def add_rule(self: T, rule: RuleBase) -> None:
if isinstance(rule, ParseTreeListener):
self._parser_rules[rule.get_rule_name()] = rule
else:
self._rules[rule.get_rule_name()] = rule

def get_rule_disabler(self: T) -> RuleDisabler:
rule = self.get_rule("000")
assert isinstance(rule, RuleDisabler)
return rule

def remove_rule(self: T, name: str) -> None:
if name in self._rules:
del self._rules[name]
Expand All @@ -64,7 +71,7 @@ def load_standard_rules(self: T) -> None:
rule910.set_rule_name("910")
rule910.severity = 'F'
self._rules.update({
"201": NewlineEof(),
"701": NewlineEof(),
"220": KeywordCaps(),
"400": LineEnding(),
"305": TrailingWhitespace(),
Expand All @@ -82,7 +89,7 @@ def load_all_rules(self: T) -> None:
self._parser_rules.update({
'505': OptionalPublic(),
'510': MissingVisibility(),
'110': MissingLet(), '111': OptionalLet()
'201': MissingLet(), '202': OptionalLet()
})

def get_rule(self: T, rule_name: str) -> RuleBase:
Expand Down
78 changes: 78 additions & 0 deletions src/vba_linter/rule_disabler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from antlr4.tree.Tree import TerminalNode, TerminalNodeImpl
from antlr4_vba.vbaParser import vbaParser as Parser
from typing import Dict, TypeVar
from vba_linter.antlr.vbaListener import VbaListener


T = TypeVar('T', bound='RuleDisabler')


class RuleDisabler(VbaListener):
def __init__(self: T) -> None:
super().__init__()
self._rule_name = "000"
self.open_blocks: Dict[str, int] = {}

# Key: rule name
# value: list of line numbers in which the rule is ignored.
self.ignored: Dict[str, set] = {}

def enterStartRule( # noqa: N802
self: T,
ctx: Parser.StartRuleContext) -> None:
self.ignored = {}

def enterClassBeginBlock( # noqa: N802
self: T,
ctx: Parser.ClassBeginBlockContext) -> None:
start = ctx.start.line
stop = ctx.stop.line
# ignore excess whitespace after and before equals sign.
self.add_ignored_lines('151', start, stop)
self.add_ignored_lines('154', start, stop)

def enterCommentBody(self: T, # noqa: N802
ctx: Parser.CommentBodyContext) -> None:
tok = ctx.start
if tok.text[:8] == "' noqa: ":
rule = ctx.start.text[8:]
if tok.column == 0:
# ignore multiple lines
self.open_blocks[rule] = tok.line
else:
# ignore one line
self.add_ignored_line(rule, tok.line)
elif tok.text[:6] == "' qa: ":
rule = tok.text[6:]
if rule in self.open_blocks:
start_line = self.open_blocks[rule]
self.add_ignored_lines(rule, start_line, tok.line)
del self.open_blocks[rule]

def visitTerminal(self: T, # noqa: N802
node: TerminalNode) -> None:
assert isinstance(node, TerminalNodeImpl)
end_line = node.symbol.line
for rule in self.open_blocks:
start_line = self.open_blocks[rule]
self.add_ignored_lines(rule, start_line, end_line)

def add_ignored_line(self: T, rule: str, line: int) -> None:
if rule in self.ignored:
lines = self.ignored[rule]
else:
lines = set()
lines.add(line)
new = {rule: lines}
self.ignored.update(new)

def add_ignored_lines(
self: T, rule: str, start_line: int, end_line: int
) -> None:
if rule in self.ignored:
lines = self.ignored[rule]
else:
lines = set()
lines.update(range(start_line, end_line))
new = {rule: lines}
self.ignored.update(new)
2 changes: 1 addition & 1 deletion src/vba_linter/rules/listeners/missing_let.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def __init__(self: T) -> None:
super().__init__()
self.output: list = []
self._severity = 'W'
self._rule_name = "110"
self._rule_name = "201"
self._message = "Missing let"

def enterLetStatement(self: T, # noqa: N802
Expand Down
2 changes: 1 addition & 1 deletion src/vba_linter/rules/listeners/optional_let.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def __init__(self: T) -> None:
super().__init__()
self.output: list = []
self._severity = 'W'
self._rule_name = "111"
self._rule_name = "202"
self._message = "Optional let"

def enterLetStatement( # noqa: N802
Expand Down
4 changes: 2 additions & 2 deletions src/vba_linter/rules/newline_eof.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class NewlineEof(RuleBase):
def __init__(self: T) -> None:
self._rule_name = "201"
self._rule_name = "701"
self._message = "no newline at end of file"
self._severity = 'W'

Expand All @@ -21,5 +21,5 @@ def test(self: T, ts: CommonTokenStream) -> list:
token.type != vbaLexer.NEWLINE):
line = token.line
column = token.column + len(token.text) + 1
output = [(line, column, "201")]
output = [(line, column, self._rule_name)]
return output
166 changes: 166 additions & 0 deletions tests/Functional/test_ignore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import pytest
import random
import string
from pathlib import Path
from pytest_mock import MockerFixture
from _pytest.capture import CaptureFixture
from vba_linter.__main__ import main


@pytest.fixture(autouse=True)
def run_around_tests() -> None:
global files
files = []
# Code that will run before your test, for example:
# A test function will be run at this point
yield
# Code that will run after your test, for example:
for file in files:
delete_code(file)
files = []


def save_code(code: str) -> str:
file_name = create_filename(ext='.bas')
p = Path(file_name)
with p.open(mode='a') as fi:
fi.write(code)
return file_name


def create_filename(num: int = 16, ext: str = ".txt") -> str:
chars = ""
for i in range(num):
chars += random.choice(string.ascii_lowercase)
file_name = chars + ext
return "tests/Functional/" + file_name


def delete_code(file_name: str) -> None:
p = Path(file_name)
p.unlink()


function = 'Supercalifragilisticexpialidocious'


worst_practice1 = (
'Attribute VB_Name = "Foo"\r\n' +
'\' noqa: 400\n'
'Public Function ' + function +
' ( atrocious , precocious, indubitably ) \r\n' +
' \r\n' +
'\' qa: 400\r\n' +
'\n' +
'\r\n' +
'I = (2 + 1)\r\n' +
' foo_val=6\r\n'
' Let BarVal = (7 + 2) / 3\r\n'
'End Function \' noqa: 400\n' +
'\r\n' +
'sub O()\r\n' +
'End Sub \' noqa: 400\n' +
'\r\n'
)


def test_ignore(mocker: MockerFixture, capsys: CaptureFixture) -> None:
file_name = save_code(worst_practice1)
files.append(file_name)
full_path = ("/home/runner/work/VBA-Linter/VBA-Linter/" + file_name)
mocker.patch(
"sys.argv",
[
"vba_linter.py",
"tests/Functional",
],
)
with pytest.raises(SystemExit):
main()
captured = capsys.readouterr()
expected = """\
%s:3:51: E121 Excess whitespace before '('
%s:3:53: E124 Excess whitespace after '('
%s:3:63: E141 Excess whitespace before ','
%s:3:66: E144 Excess whitespace after ','
%s:3:90: E131 Excess whitespace before ')'
%s:3:92: E305 Trailing whitespace
%s:6:0: E400 incorrect line ending
%s:9:12: E150 Missing whitespace before '='
%s:9:13: E153 Missing whitespace after '='
%s:10:16: E151 Excess whitespace before '='
%s:10:19: E154 Excess whitespace after '='
%s:13:1: E220 Keyword not capitalized
12 Errors in 1 File
""".replace("%s", full_path) # noqa
mocker.patch(
"sys.argv",
[
"vba_linter.py",
"tests/Functional",
],
)
with pytest.raises(SystemExit):
main()
captured = capsys.readouterr()
assert captured.err == expected


worst_practice2 = (
'Attribute VB_Name = "Foo"\r\n' +
'\r\n'
'Public Function ' + function +
' ( atrocious , precocious, indubitably ) \' noqa: 121\r\n' +
' \r\n' +
'\' qa: 400\r\n' +
'\n' +
'\r\n' +
'I = (2 + 1)\r\n' +
' foo_val=6\r\n'
' Let BarVal = (7 + 2) / 3\r\n'
'End Function \' noqa: 400\n' +
'\r\n' +
'sub O()\r\n' +
'End Sub \' noqa: 400\n' +
'\r\n'
)


def test_ignore_single(mocker: MockerFixture, capsys: CaptureFixture) -> None:
file_name = save_code(worst_practice2)
files.append(file_name)
full_path = ("/home/runner/work/VBA-Linter/VBA-Linter/" + file_name)
mocker.patch(
"sys.argv",
[
"vba_linter.py",
"tests/Functional",
],
)
with pytest.raises(SystemExit):
main()
captured = capsys.readouterr()
expected = """\
%s:3:53: E124 Excess whitespace after '('
%s:3:63: E141 Excess whitespace before ','
%s:3:66: E144 Excess whitespace after ','
%s:3:90: E131 Excess whitespace before ')'
%s:6:0: E400 incorrect line ending
%s:9:12: E150 Missing whitespace before '='
%s:9:13: E153 Missing whitespace after '='
%s:10:16: E151 Excess whitespace before '='
%s:10:19: E154 Excess whitespace after '='
%s:13:1: E220 Keyword not capitalized
10 Errors in 1 File
""".replace("%s", full_path) # noqa
mocker.patch(
"sys.argv",
[
"vba_linter.py",
"tests/Functional",
],
)
with pytest.raises(SystemExit):
main()
captured = capsys.readouterr()
assert captured.err == expected
Loading