Page MenuHomePhabricator

D10250.id24665.diff
No OneTemporary

D10250.id24665.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -83,6 +83,8 @@
'ArcanistGitHookPreReceiveWorkflow' => 'workflow/ArcanistGitHookPreReceiveWorkflow.php',
'ArcanistGoLintLinter' => 'lint/linter/ArcanistGoLintLinter.php',
'ArcanistGoLintLinterTestCase' => 'lint/linter/__tests__/ArcanistGoLintLinterTestCase.php',
+ 'ArcanistHLintLinter' => 'lint/linter/ArcanistHLintLinter.php',
+ 'ArcanistHLintLinterTestCase' => 'lint/linter/__tests__/ArcanistHLintLinterTestCase.php',
'ArcanistHelpWorkflow' => 'workflow/ArcanistHelpWorkflow.php',
'ArcanistHgClientChannel' => 'hgdaemon/ArcanistHgClientChannel.php',
'ArcanistHgProxyClient' => 'hgdaemon/ArcanistHgProxyClient.php',
@@ -273,6 +275,8 @@
'ArcanistGitHookPreReceiveWorkflow' => 'ArcanistWorkflow',
'ArcanistGoLintLinter' => 'ArcanistExternalLinter',
'ArcanistGoLintLinterTestCase' => 'ArcanistArcanistLinterTestCase',
+ 'ArcanistHLintLinter' => 'ArcanistExternalLinter',
+ 'ArcanistHLintLinterTestCase' => 'ArcanistArcanistLinterTestCase',
'ArcanistHelpWorkflow' => 'ArcanistWorkflow',
'ArcanistHgClientChannel' => 'PhutilProtocolChannel',
'ArcanistHgServerChannel' => 'PhutilProtocolChannel',
diff --git a/src/lint/linter/ArcanistHLintLinter.php b/src/lint/linter/ArcanistHLintLinter.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/ArcanistHLintLinter.php
@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * Calls `hlint` and parses its results.
+ */
+final class ArcanistHLintLinter extends ArcanistExternalLinter {
+
+ public function getInfoName() {
+ return 'HLint';
+ }
+
+ public function getInfoURI() {
+ return 'https://github.com/ndmitchell/hlint';
+ }
+
+ public function getInfoDescription() {
+ return pht('HLint is a linter for Haskell code.');
+ }
+
+ public function getLinterName() {
+ return 'HLINT';
+ }
+
+ public function getLinterConfigurationName() {
+ return 'hlint';
+ }
+
+ public function getDefaultBinary() {
+ return 'hlint';
+ }
+
+ public function getInstallInstructions() {
+ return pht('Install hlint with `cabal install hlint`.');
+ }
+
+ public function supportsReadDataFromStdin() {
+ return true;
+ }
+
+ public function getReadDataFromStdinFilename() {
+ return '-';
+ }
+
+ public function shouldExpectCommandErrors() {
+ return true;
+ }
+
+ public function getMandatoryFlags() {
+ return array('--json');
+ }
+
+ public function getVersion() {
+ list($stdout, $stderr) = execx(
+ '%C --version', $this->getExecutableCommand());
+
+ $matches = null;
+ if (preg_match('@HLint v(.*),@', $stdout, $matches)) {
+ return $matches[1];
+ }
+
+ return null;
+ }
+
+ protected function parseLinterOutput($path, $err, $stdout, $stderr) {
+
+ $json = phutil_json_decode($stdout);
+ $messages = array();
+ foreach ($json as $fix) {
+ if ($fix === null) {
+ return;
+ }
+
+ $severity = ArcanistLintSeverity::SEVERITY_ADVICE;
+
+ $message = new ArcanistLintMessage();
+ $message->setCode($this->getLinterName());
+ $message->setPath($path);
+ $message->setLine($fix['startLine']);
+ $message->setChar($fix['startColumn']);
+ $message->setName($fix['hint']);
+ $message->setOriginalText($fix['from']);
+ $message->setReplacementText($fix['to']);
+
+ /* Some improvements may slightly change semantics, so attach
+ all necessary notes too. */
+ $notes = '';
+ foreach ($fix['note'] as $note) {
+ $notes .= phutil_console_format("\n\t**NOTE**: ".trim($note, '"'));
+ }
+
+ $message->setDescription(
+ pht(
+ 'In module `%s`, declaration `%s`.%s',
+ $fix['module'], $fix['decl'], $notes));
+
+ switch ($fix['severity']) {
+ case 'Error':
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
+ break;
+ case 'Warning':
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
+ break;
+ default:
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_ADVICE);
+ break;
+ }
+
+ $messages[] = $message;
+ }
+
+ return $messages;
+ }
+}
diff --git a/src/lint/linter/__tests__/ArcanistHLintLinterTestCase.php b/src/lint/linter/__tests__/ArcanistHLintLinterTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/ArcanistHLintLinterTestCase.php
@@ -0,0 +1,9 @@
+<?php
+
+final class ArcanistHLintLinterTestCase extends ArcanistArcanistLinterTestCase {
+ public function testHlintLinter() {
+ $this->executeTestsInDirectory(
+ dirname(__FILE__).'/hlint/',
+ new ArcanistHLintLinter());
+ }
+}
diff --git a/src/lint/linter/__tests__/hlint/01_warn_null.lint-test b/src/lint/linter/__tests__/hlint/01_warn_null.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/hlint/01_warn_null.lint-test
@@ -0,0 +1,3 @@
+f xs = length xs == 0
+~~~~~~~~~~
+warning:1:8
diff --git a/src/lint/linter/__tests__/hlint/02_err_eta.lint-test b/src/lint/linter/__tests__/hlint/02_err_eta.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/hlint/02_err_eta.lint-test
@@ -0,0 +1,5 @@
+f x = x
+
+test x = f x
+~~~~~~~~~~
+error:3:1
diff --git a/src/lint/linter/__tests__/hlint/03_no_err.lint-test b/src/lint/linter/__tests__/hlint/03_no_err.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/hlint/03_no_err.lint-test
@@ -0,0 +1,2 @@
+main = map (f . g) xs
+~~~~~~~~~~

File Metadata

Mime Type
text/plain
Expires
Fri, Oct 18, 2:52 PM (3 w, 4 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6727441
Default Alt Text
D10250.id24665.diff (5 KB)

Event Timeline