-
Notifications
You must be signed in to change notification settings - Fork 580
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ghc-mod feature to coala for syntax checking for haskell files (.hs) Closes #297
- Loading branch information
1 parent
75ae1c9
commit 4ac8fde
Showing
3 changed files
with
131 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
from coalib.bearlib.abstractions.Linter import linter | ||
from dependency_management.requirements.CabalRequirement import ( | ||
CabalRequirement) | ||
|
||
|
||
@linter(executable='ghc-mod', | ||
output_format='regex', | ||
output_regex=r'.+:(?P<line>\d+):(?P<column>\d+):' | ||
r'(?P<message>.+)') | ||
class GhcModBear: | ||
""" | ||
Syntax checking with ``ghc`` for Haskell files. | ||
See <https://hackage.haskell.org/package/ghc-mod> for more information! | ||
""" | ||
|
||
LANGUAGES = {'Haskell'} | ||
REQUIREMENTS = {CabalRequirement(package='ghc-mod', version='5.6.0')} | ||
AUTHORS = {'The coala developers'} | ||
AUTHORS_EMAILS = {'coala-devel@googlegroups.com'} | ||
LICENSE = 'AGPL-3.0' | ||
ASCIINEMA_URL = 'https://asciinema.org/a/98873' | ||
CAN_DETECT = {'Syntax'} | ||
|
||
@staticmethod | ||
def create_arguments(filename, file, config_file): | ||
# -b '. ' is the argument given for ghc-mod for seperation of messages | ||
return '-b', '. ', 'check', filename |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
from queue import Queue | ||
|
||
from bears.haskell.GhcModBear import GhcModBear | ||
from coalib.testing.LocalBearTestHelper import LocalBearTestHelper | ||
from coalib.testing.LocalBearTestHelper import verify_local_bear | ||
from coalib.testing.BearTestHelper import generate_skip_decorator | ||
from coalib.settings.Section import Section | ||
from coalib.settings.Setting import Setting | ||
|
||
|
||
# A simple hello world program! | ||
hello_world_file = """ | ||
main :: IO() | ||
main = putStrLn "Hello World!" | ||
""".splitlines() | ||
|
||
# No instance for (Num String) in the first argument of ‘putStrLn’ | ||
not_string_3 = """ | ||
main :: IO() | ||
main = putStrLn 3 | ||
""".splitlines() | ||
|
||
# Takes a file and prints the number of lines | ||
count_lines = """ | ||
main :: IO() | ||
main = interact lineC | ||
where lineC input = show (length(lines input)) | ||
""".splitlines() | ||
|
||
# Parse Error (mismatched brackets) | ||
count_lines_bracket_missing = """ | ||
main :: IO() | ||
main = interact lineC | ||
where lineC input = show (length(lines input) | ||
""".splitlines() | ||
|
||
# Prints "asd" which is the last but one element in the list | ||
last_but_one_list = """ | ||
lastButOne :: [a] -> a | ||
lastButOne xs | (length xs >= 2) = xs !! ((length xs) - 2) | ||
| otherwise = error "Not possible" | ||
main :: IO() | ||
main = do | ||
print(lastButOne(["asd", "ASd"])) | ||
""".splitlines() | ||
|
||
# Couldn't match expected type ‘[a]’ with actual type ‘a’ | ||
last_but_one_list_type_error = """ | ||
lastButOne :: a -> a | ||
lastButOne xs | (length xs >= 2) = xs !! ((length xs) - 2) | ||
| otherwise = error "Not possible" | ||
main :: IO() | ||
main = do | ||
print(lastButOne(["asd", "ASd"])) | ||
""".splitlines() | ||
|
||
|
||
@generate_skip_decorator(GhcModBear) | ||
class GhcModBearTest(LocalBearTestHelper): | ||
|
||
def setUp(self): | ||
self.section = Section('name') | ||
self.uut = GhcModBear(self.section, Queue()) | ||
|
||
def test_valid(self): | ||
self.check_validity(self.uut, hello_world_file, valid=True, | ||
tempfile_kwargs={'suffix': '.hs'}) | ||
self.check_validity(self.uut, count_lines, valid=True, | ||
tempfile_kwargs={'suffix': '.hs'}) | ||
self.check_validity(self.uut, last_but_one_list, | ||
valid=True, | ||
tempfile_kwargs={'suffix': '.hs'}) | ||
|
||
def test_invalid(self): | ||
results = self.check_validity(self.uut, not_string_3, | ||
valid=False, | ||
tempfile_kwargs={'suffix': '.hs'}) | ||
self.assertEqual(len(results), 1, str(results)) | ||
# to check for the seperator | ||
self.assertEqual(len(str(results[0].message).split('.')), 4, | ||
str(results)) | ||
self.assertIn('No instance for (Num String) arising from the literal ', | ||
results[0].message) | ||
results = self.check_validity(self.uut, | ||
count_lines_bracket_missing, | ||
valid=False, | ||
tempfile_kwargs={'suffix': '.hs'}) | ||
self.assertEqual(len(results), 1, str(results)) | ||
self.assertIn('parse error (possibly incorrect indentation ' | ||
'or mismatched brackets)', results[0].message) | ||
results = self.check_validity(self.uut, | ||
last_but_one_list_type_error, | ||
valid=False, | ||
tempfile_kwargs={'suffix': '.hs'}) | ||
self.assertEqual(len(results), 1, str(results)) | ||
self.assertIn("Couldn't match expected type", | ||
results[0].message) |