Skip to content

Commit 49cdf5a

Browse files
committed
bears/general: Add RegexLintBear
Closes #1532
1 parent f9aa8c3 commit 49cdf5a

File tree

5 files changed

+140
-0
lines changed

5 files changed

+140
-0
lines changed

bear-languages.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ RadonBear:
329329
- Python
330330
- Python 2
331331
- Python 3
332+
RegexLintBear:
332333
RuboCopBear:
333334
- Ruby
334335
RubyFastererBear:

bear-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pylint~=1.7.2
3131
pyroma~=2.2.0
3232
pyyaml~=3.12
3333
radon==1.4.0
34+
regexlint~=1.6
3435
restructuredtext-lint~=1.0
3536
rstcheck~=3.1
3637
safety~=1.8.2

bear-requirements.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ pip_requirements:
6868
version: ~=3.12
6969
radon:
7070
version: ==1.4.0
71+
regexlint:
72+
version: ~=1.6
7173
restructuredtext-lint:
7274
version: ~=1.0
7375
rstcheck:

bears/general/RegexLintBear.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import re
2+
3+
from queue import Queue
4+
from sarge import run, Capture
5+
from contextlib import suppress
6+
7+
from bears.general.AnnotationBear import AnnotationBear
8+
9+
from coalib.bears.LocalBear import LocalBear
10+
from coalib.results.Result import Result
11+
from coalib.settings.Section import Section
12+
from coalib.settings.Setting import Setting
13+
from coalib.testing.LocalBearTestHelper import execute_bear
14+
15+
from dependency_management.requirements.PipRequirement import PipRequirement
16+
17+
18+
class RegexLintBear(LocalBear):
19+
LANGUAGES = {'All'}
20+
REQUIREMENTS = {PipRequirement('regexlint', '1.6')}
21+
AUTHORS = {'The coala developers'}
22+
AUTHORS_EMAILS = {'[email protected]'}
23+
LICENSE = 'AGPL-3.0'
24+
CAN_DETECT = {'Formatting'}
25+
26+
def run(self, filename, file, language: str):
27+
"""
28+
Bear for linting regex through regexlint.
29+
30+
:param language:
31+
The programming language of the file(s).
32+
"""
33+
section = Section('')
34+
section.append(Setting('language', language))
35+
bear = AnnotationBear(section, Queue())
36+
37+
with execute_bear(bear, filename, file) as result:
38+
for src_range in result[0].contents['strings']:
39+
src_line = src_range.affected_source({filename: file})[0]
40+
regex = src_line[src_range.start.column:src_range.end.column-1]
41+
with suppress(re.error):
42+
re.compile(regex)
43+
out = run('regexlint --regex "{}"'.format(regex),
44+
stdout=Capture()).stdout.text
45+
if out[-3:-1] == 'OK':
46+
continue
47+
yield Result.from_values(
48+
origin=self,
49+
message=out,
50+
file=filename,
51+
line=src_range.start.line,
52+
column=src_range.start.column,
53+
end_line=src_range.end.line,
54+
end_column=src_range.end.column,
55+
)

tests/general/RegexLintBearTest.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from os.path import abspath
2+
from queue import Queue
3+
4+
from bears.general.RegexLintBear import RegexLintBear
5+
from coalib.results.Result import Result
6+
from coalib.settings.Section import Section
7+
from coalib.settings.Setting import Setting
8+
from coalib.testing.LocalBearTestHelper import (
9+
LocalBearTestHelper, execute_bear)
10+
11+
12+
test_good_py_file = """
13+
some_regex = r'[a-zA-Z]]'
14+
"""
15+
16+
test_bad_py_file = """
17+
some_regex = r'(else|elseif)'
18+
"""
19+
20+
test_good_cpp_file = """
21+
char some_regex[] = "[a-zA-Z]]";
22+
"""
23+
24+
test_bad_cpp_file = """
25+
char some_regex[13] = "(else|elseif)";
26+
"""
27+
28+
test_re_error_file = """
29+
some_regex = r'*ab' # This should be skipped
30+
some_other_regex = r'[a-z]'
31+
"""
32+
33+
BAD_MESSAGE = """
34+
E105:argv:root:0: Potential out of order alternation between 'else' and 'elseif'
35+
'(else|elseif)'
36+
^ here
37+
""".lstrip()
38+
39+
40+
class RegexLintBearTest(LocalBearTestHelper):
41+
42+
def setUp(self):
43+
self.section = Section('')
44+
self.queue = Queue()
45+
self.uut = RegexLintBear(self.section, self.queue)
46+
47+
def test_good_python_file(self):
48+
self.section.append(Setting('language', 'python 3'))
49+
self.check_validity(self.uut, test_good_py_file.splitlines())
50+
51+
def test_bad_python_file(self):
52+
self.section.append(Setting('language', 'python 3'))
53+
with execute_bear(self.uut, abspath('bad_python_file'),
54+
test_bad_py_file.splitlines()) as results:
55+
self.assertEqual(len(results), 1)
56+
self.assertEqual(results[0],
57+
Result.from_values(origin=self.uut,
58+
message=BAD_MESSAGE,
59+
file='bad_python_file',
60+
line=2, column=15,
61+
end_line=2, end_column=29))
62+
63+
def test_good_cpp_file(self):
64+
self.section.append(Setting('language', 'cpp'))
65+
self.check_validity(self.uut, test_good_cpp_file.splitlines())
66+
67+
def test_bad_cpp_file(self):
68+
self.section.append(Setting('language', 'cpp'))
69+
with execute_bear(self.uut, abspath('bad_cpp_file'),
70+
test_bad_cpp_file.splitlines()) as results:
71+
self.assertEqual(len(results), 1)
72+
self.assertEqual(results[0],
73+
Result.from_values(origin=self.uut,
74+
message=BAD_MESSAGE,
75+
file='bad_cpp_file',
76+
line=2, column=23,
77+
end_line=2, end_column=37))
78+
79+
def test_re_error_file(self):
80+
self.section.append(Setting('language', 'cpp'))
81+
self.check_validity(self.uut, test_re_error_file.splitlines())

0 commit comments

Comments
 (0)