Skip to content

Commit 029df27

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

File tree

5 files changed

+153
-0
lines changed

5 files changed

+153
-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: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import re
2+
3+
from sarge import run, Capture
4+
5+
from bears.general.AnnotationBear import AnnotationBear
6+
from coalib.bears.LocalBear import LocalBear
7+
from dependency_management.requirements.PipRequirement import PipRequirement
8+
9+
10+
class RegexLintBear(LocalBear):
11+
LANGUAGES = {'All'}
12+
REQUIREMENTS = {PipRequirement('regexlint', '1.6')}
13+
AUTHORS = {'The coala developers'}
14+
AUTHORS_EMAILS = {'[email protected]'}
15+
LICENSE = 'AGPL-3.0'
16+
CAN_DETECT = {'Formatting'}
17+
BEAR_DEPS = {AnnotationBear}
18+
19+
def run(self, filename, file, dependency_results):
20+
"""
21+
Bear for linting regex through regexlint.
22+
23+
:param dependency_results:
24+
Results given by the AnnotationBear.
25+
"""
26+
annotation_dict = dependency_results[AnnotationBear.name][0].contents
27+
for src_range in annotation_dict['strings']:
28+
src_line = src_range.affected_source({filename: file})[0]
29+
regex = src_line[src_range.start.column:src_range.end.column-1]
30+
try:
31+
re.compile(regex)
32+
except re.error:
33+
continue
34+
out = run('regexlint --regex "{}"'.format(regex),
35+
stdout=Capture()).stdout.text
36+
if out[-3:-1] == 'OK':
37+
continue
38+
yield Result.from_values(
39+
origin=self,
40+
message=out,
41+
file=filename,
42+
line=src_range.start.line,
43+
column=src_range.start.column,
44+
end_line=src_range.end.line,
45+
end_column=src_range.end.column,
46+
)

tests/general/RegexLintBearTest.py

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

0 commit comments

Comments
 (0)