From 6b26f463abaaffae6f734805d67d5e0fcd310ad3 Mon Sep 17 00:00:00 2001 From: Bhushan Khanale Date: Sat, 20 Jul 2019 16:31:07 +0530 Subject: [PATCH] bears/general: Add RegexLintBear Closes https://github.com/coala/coala-bears/issues/1532 --- bears/general/RegexLintBear.py | 54 +++++++++++++++++++ tests/general/RegexLintBearTest.py | 87 ++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 bears/general/RegexLintBear.py create mode 100644 tests/general/RegexLintBearTest.py diff --git a/bears/general/RegexLintBear.py b/bears/general/RegexLintBear.py new file mode 100644 index 0000000000..d5064dc414 --- /dev/null +++ b/bears/general/RegexLintBear.py @@ -0,0 +1,54 @@ +import re + +from queue import Queue +from sarge import run, Capture +from contextlib import suppress + +from bears.general.AnnotationBear import AnnotationBear + +from coalib.bears.LocalBear import LocalBear +from coalib.results.Result import Result +from coalib.settings.Section import Section +from coalib.settings.Setting import Setting +from coalib.testing.LocalBearTestHelper import execute_bear + +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'} + + def run(self, filename, file, language: str): + """ + Bear for linting regex through regexlint. + + :param language: + The language of the files, must be supported by coala. + """ + section = Section('') + section.append(Setting('language', language)) + bear = AnnotationBear(section, Queue()) + + with execute_bear(bear, filename, file) as result: + for src_range in result[0].contents['strings']: + src_line = src_range.affected_source({filename: file})[0] + regex = src_line[src_range.start.column:src_range.end.column-1] + with suppress(re.error): + re.compile(regex) + out = run('regexlint --regex "{}"'.format(regex), + stdout=Capture()).stdout.text + if out[-3:-1] != 'OK': + 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, + ) diff --git a/tests/general/RegexLintBearTest.py b/tests/general/RegexLintBearTest.py new file mode 100644 index 0000000000..1040d011dd --- /dev/null +++ b/tests/general/RegexLintBearTest.py @@ -0,0 +1,87 @@ +from os.path import abspath +from queue import Queue + +from bears.general.RegexLintBear import RegexLintBear +from coalib.testing.LocalBearTestHelper import ( + LocalBearTestHelper, execute_bear) +from coalib.results.Result import Result +from coalib.settings.Section import Section +from coalib.settings.Setting import Setting + + +test_good_python_file = """ +some_regex = r'[a-zA-Z]]' +""" + +test_bad_python_file = """ +some_regex = r'(else|elseif)' +""" + +test_good_cpp_file = """ +char some_regex[] = "[a-zA-Z]]"; +""" + +test_bad_cpp_file = """ +char some_regex[13] = "(else|elseif)"; +""" + +test_re_error_file = """ +some_regex = r'*ab' # This should be skipped +some_other_regex = r'[a-z]' +some_other_regex = r'[a-z]' +some_other_regex = r'[a-z]' +some_other_regex = r'[a-z]' +""" + +BAD_MESSAGE = ('E105:argv:root:0: Potential out of order alternation between ' + '\'else\' and \'elseif\'\n' + ' \'(else|elseif)\'\n' + ' ^ here\n') + + +class RegexLintBearTest(LocalBearTestHelper): + + def setUp(self): + self.section = Section('') + self.queue = Queue() + self.uut = RegexLintBear(self.section, self.queue) + + def test_good_python_file(self): + self.section.append(Setting('language', 'python 3')) + with execute_bear(self.uut, abspath('good_python_file'), + test_good_python_file.splitlines(True)) as results: + self.assertEqual(results, []) + + def test_bad_python_file(self): + self.section.append(Setting('language', 'python 3')) + with execute_bear(self.uut, abspath('bad_python_file'), + test_bad_python_file.splitlines()) as results: + self.assertEqual(results[0], + Result.from_values(origin=self.uut, + message=BAD_MESSAGE, + file='bad_python_file', + line=2, column=15, + end_line=2, end_column=29)) + + def test_good_cpp_file(self): + self.section.append(Setting('language', 'cpp')) + with execute_bear(self.uut, abspath('good_cpp_file'), + test_good_cpp_file.splitlines()) as results: + self.assertEqual(results, []) + + def test_bad_cpp_file(self): + self.section.append(Setting('language', 'cpp')) + with execute_bear(self.uut, abspath('bad_cpp_file'), + test_bad_cpp_file.splitlines()) as results: + self.assertEqual(results[0], + Result.from_values(origin=self.uut, + message=BAD_MESSAGE, + file='bad_cpp_file', + line=2, column=23, + end_line=2, end_column=37)) + + def test_re_error_file(self): + self.section.append(Setting('language', 'cpp')) + with execute_bear(self.uut, abspath('re_error_file'), + test_re_error_file.splitlines()) as results: + self.assertEqual(results, [])