Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bears/general: Add RegexLintBear #2934

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions bear-languages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ RadonBear:
- Python
- Python 2
- Python 3
RegexLintBear:
RuboCopBear:
- Ruby
RubyFastererBear:
Expand Down
1 change: 1 addition & 0 deletions bear-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pylint~=1.7.2
pyroma~=2.2.0
pyyaml~=3.12
radon==1.4.0
regexlint~=1.6
restructuredtext-lint~=1.0
rstcheck~=3.1
safety~=1.8.2
Expand Down
2 changes: 2 additions & 0 deletions bear-requirements.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ pip_requirements:
version: ~=3.12
radon:
version: ==1.4.0
regexlint:
version: ~=1.6
restructuredtext-lint:
version: ~=1.0
rstcheck:
Expand Down
46 changes: 46 additions & 0 deletions bears/general/RegexLintBear.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import re

from sarge import run, Capture
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sarge is coala dependency?

Afaik, sarge comes from package_manager, if you are using it directly here, you'll need to add it to requirements.txt.
Maybe you can confirm this from @jayvdb

Copy link
Member Author

@bkhanale bkhanale Jul 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm not very sure about this since we might just create a conflict if the versions mismatch later on.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the PipRequirement , but we have some magic piece of code somewhere in .ci/generate_* which excludes dependencies which are also provided by our own downstream. e.g. this is used for requests which is provided by coala

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jayvdb About this, I wasn't sure about the version to include since on package_manager we install from hg+https://bitbucket.org/jayvdb/sarge@win-reg-lookup#egg=sarge. Will it break stuff?


from bears.general.AnnotationBear import AnnotationBear
from coalib.bears.LocalBear import LocalBear
from dependency_management.requirements.PipRequirement import PipRequirement


class RegexLintBear(LocalBear):
LANGUAGES = {'All'}
REQUIREMENTS = {PipRequirement('regexlint', '1.6')}
AUTHORS = {'The coala developers'}
AUTHORS_EMAILS = {'coala-devel@googlegroups.com'}
LICENSE = 'AGPL-3.0'
CAN_DETECT = {'Formatting'}
BEAR_DEPS = {AnnotationBear}

def run(self, filename, file, dependency_results):
"""
Bear for linting regex through regexlint.

:param dependency_results:
Results given by the AnnotationBear.
"""
annotation_dict = dependency_results[AnnotationBear.name][0].contents
for src_range in annotation_dict['strings']:
src_line = src_range.affected_source({filename: file})[0]
regex = src_line[src_range.start.column:src_range.end.column-1]
try:
re.compile(regex)
except re.error:
continue
out = run('regexlint --regex "{}"'.format(regex),
stdout=Capture()).stdout.text
if out[-3:-1] == 'OK':
continue
yield Result.from_values(
origin=self,
message=out,
file=filename,
line=src_range.start.line,
column=src_range.start.column,
end_line=src_range.end.line,
end_column=src_range.end.column,
)
103 changes: 103 additions & 0 deletions tests/general/RegexLintBearTest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from os.path import abspath
from queue import Queue

from bears.general.AnnotationBear import AnnotationBear
from bears.general.RegexLintBear import RegexLintBear
from coalib.results.Result import Result
from coalib.settings.Section import Section
from coalib.settings.Setting import Setting
from coalib.testing.LocalBearTestHelper import (
LocalBearTestHelper, execute_bear)


good_py_file = """
some_regex = r'[a-zA-Z]'
"""

bad_py_file = """
some_regex = r'(else|elseif)'
"""

good_cpp_file = """
char some_regex[] = "[a-zA-Z]";
"""

bad_cpp_file = """
char some_regex[13] = "(else|elseif)";
"""

re_error_file = """
some_regex = r'*ab' # This should be skipped
some_other_regex = r'[a-z]'
bkhanale marked this conversation as resolved.
Show resolved Hide resolved
"""

BAD_MESSAGE = """
E105:argv:root:0: Potential out of order alternation between 'else' and 'elseif'
'(else|elseif)'
^ here
""".lstrip()


class RegexLintBearTest(LocalBearTestHelper):
bkhanale marked this conversation as resolved.
Show resolved Hide resolved

def setUp(self):
self.section = Section('')
self.uut = RegexLintBear(self.section, Queue())
self.dep_uut = AnnotationBear(self.section, Queue())

def test_good_python_file(self):
valid_file = good_py_file.splitlines()
self.section.append(Setting('language', 'python'))
dep_results = {AnnotationBear.name:
list(self.dep_uut.execute('file', valid_file))}
with execute_bear(self.uut, abspath('file'), valid_file,
dependency_results=dep_results) as results:
self.assertEqual(len(results), 0)

def test_bad_python_file(self):
invalid_file = bad_py_file.splitlines()
self.section.append(Setting('language', 'python'))
dep_results = {AnnotationBear.name:
list(self.dep_uut.execute('file', invalid_file))}
with execute_bear(self.uut, abspath('file'), invalid_file,
dependency_results=dep_results) as results:
self.assertEqual(len(results), 1)
self.assertEqual(results[0],
Result.from_values(origin=self.uut,
message=BAD_MESSAGE,
file='file',
line=2, column=15,
end_line=2, end_column=29))

def test_good_cpp_file(self):
valid_file = good_cpp_file.splitlines()
self.section.append(Setting('language', 'cpp'))
dep_results = {AnnotationBear.name:
list(self.dep_uut.execute('file', valid_file))}
with execute_bear(self.uut, abspath('file'), valid_file,
dependency_results=dep_results) as results:
self.assertEqual(len(results), 0)

def test_bad_cpp_file(self):
invalid_file = bad_cpp_file.splitlines()
self.section.append(Setting('language', 'cpp'))
dep_results = {AnnotationBear.name:
list(self.dep_uut.execute('file', invalid_file))}
with execute_bear(self.uut, abspath('file'), invalid_file,
dependency_results=dep_results) as results:
self.assertEqual(len(results), 1)
self.assertEqual(results[0],
Result.from_values(origin=self.uut,
message=BAD_MESSAGE,
file='file',
line=2, column=23,
end_line=2, end_column=37))

def test_re_error_file(self):
valid_file = re_error_file.splitlines()
self.section.append(Setting('language', 'python'))
dep_results = {AnnotationBear.name:
list(self.dep_uut.execute('file', valid_file))}
with execute_bear(self.uut, abspath('file'), valid_file,
dependency_results=dep_results) as results:
self.assertEqual(len(results), 0)