Repository: coala/coala-bears

Issue:
The bear HaskellLintBear raised an exception
I've used HaskellLintBear to linting https://github.com/wisn/elm-reactor/

Here is the log
https://travis-ci.org/wisn/elm-reactor/builds/180417562

The build result is green, but the bear HaskellLintBear raised an exception.

It seems HaskellLintBear have a problem
```
[WARNING][14:56:00] Bear HaskellLintBear failed to run. Take a look at debug messages (`-V`) for further information.
```

I've collected the traceback information:
```
Traceback (most recent call last):
  File "/coala-bears/bears/haskell/HaskellLintBear.py", line 41, in process_output
    assert issue['startLine'] == issue['endLine']
  AssertionError

  File "/coala-bears/bears/haskell/HaskellLintBear.py", line 45, in process_output
    newline = line_to_change.replace(issue['from'], issue['to'])
  TypeError: Can't convert 'NoneType' object to str implicitly
```

I think `TypeError: Can't convert 'NoneType' object to str implicitly` is the main problem.
Then, followed by `AssertionError`.

Unfortunately, I can't trace manually with `hlint` because my PC freezes when compiling (in installing) it. Hope this information will be helpful. Thanks and sorry for my bad English...


Discussion:
Thanks for the report, appreciate it :+1:

Resolving patch:
diff --git a/bears/haskell/HaskellLintBear.py b/bears/haskell/HaskellLintBear.py
--- a/bears/haskell/HaskellLintBear.py
+++ b/bears/haskell/HaskellLintBear.py
@@ -38,11 +38,15 @@ def process_output(self, output, filename, file):
         output = json.loads(output)
 
         for issue in output:
-            assert issue['startLine'] == issue['endLine']
             diff = Diff(file)
+            from_lines = issue['from'].splitlines()
+            to_lines = issue['to'].splitlines()
+            assert len(from_lines) == len(to_lines)
+            for other_lines in range(1, len(from_lines)):
+                assert from_lines[other_lines] == to_lines[other_lines]
             line_nr = issue['startLine']
             line_to_change = file[line_nr-1]
-            newline = line_to_change.replace(issue['from'], issue['to'])
+            newline = line_to_change.replace(from_lines[0], to_lines[0])
             diff.change_line(line_nr, line_to_change, newline)
 
             yield Result.from_values(
@@ -51,4 +55,7 @@ def process_output(self, output, filename, file):
                 file=filename,
                 severity=self.severity_map[issue['severity']],
                 line=issue['startLine'],
+                column=issue['startColumn'],
+                end_line=issue['endLine'],
+                end_column=issue['endColumn'],
                 diffs={filename: diff})


Test patch:
diff --git a/tests/haskell/HaskellLintBearTest.py b/tests/haskell/HaskellLintBearTest.py
--- a/tests/haskell/HaskellLintBearTest.py
+++ b/tests/haskell/HaskellLintBearTest.py
@@ -1,15 +1,71 @@
+from queue import Queue
+
 from bears.haskell.HaskellLintBear import HaskellLintBear
+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
 
-good_file = """
+good_single_line_file = """
 myconcat = (++)
-"""
+""".splitlines()
 
-bad_file = """
+bad_single_line_file = """
 myconcat a b = ((++) a b)
-"""
+""".splitlines()
+
+good_multiple_line_file = """
+import qualified Data.ByteString.Char8 as BS
+
+
+main :: IO()
+main =
+    return $ BS.concat
+        [ BS.pack "I am being tested by hlint!"
+        , "String dummy"
+        , "Another String dummy"
+        ]
+""".splitlines()
+
+bad_multiple_line_file = """
+import qualified Data.ByteString.Char8 as BS
+
+
+main :: IO()
+main =
+    return $ BS.concat $
+        [ BS.pack $ "I am being tested by hlint!"
+        , "String dummy"
+        , "Another String dummy"
+        ]
+""".splitlines()
+
+
+@generate_skip_decorator(HaskellLintBear)
+class HaskellLintBearTest(LocalBearTestHelper):
+
+    def setUp(self):
+        self.section = Section('name')
+        self.uut = HaskellLintBear(self.section, Queue())
+
+    def test_valid(self):
+        self.check_validity(self.uut, good_single_line_file, valid=True,
+                            tempfile_kwargs={'suffix': '.hs'})
+        self.check_validity(self.uut, good_multiple_line_file, valid=True,
+                            tempfile_kwargs={'suffix': '.hs'})
+
+    def test_invalid(self):
+        results = self.check_validity(self.uut, bad_single_line_file,
+                                      valid=False,
+                                      tempfile_kwargs={'suffix': '.hs'})
+        self.assertEqual(len(results), 1, str(results))
+        self.assertIn('Redundant bracket',
+                      results[0].message)
 
-HaskellLintBearTest = verify_local_bear(HaskellLintBear,
-                                        valid_files=(good_file,),
-                                        invalid_files=(bad_file,),
-                                        tempfile_kwargs={'suffix': '.hs'})
+        results = self.check_validity(self.uut, bad_multiple_line_file,
+                                      valid=False,
+                                      tempfile_kwargs={'suffix': '.hs'})
+        self.assertEqual(len(results), 2, str(results))
+        self.assertIn('Redundant $',
+                      results[0].message)