forked from hhvm/hhast
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHHClientLinter.hack
More file actions
137 lines (127 loc) · 3.94 KB
/
Copy pathHHClientLinter.hack
File metadata and controls
137 lines (127 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
namespace Facebook\HHAST;
use namespace Facebook\TypeAssert;
use namespace HH\Lib\{C, Str, Vec};
/**
* A linter as a proxy invoking `hh_client --lint`.
*/
final class HHClientLinter implements Linter {
use LinterTrait;
use SuppressibleTrait;
const type TConfig =
shape(?'ignore_except' => vec<int>, ?'ignore' => vec<int>, ...);
const type TErrorCode = int;
/**
* The error code that are always ignored
*/
const keyset<int> ALWAYS_IGNORE_ERRORS = keyset[
0 /* InternalError, indicating a bug in the OCaml linter, not a real lint error */,
5583 /* DontAwaitInALoop, which should have been covered by DontAwaitInALoopLinter */,
];
/**
* The error code that are ignored by default when both `ignore_except` and
* `ignore` are `null`
*/
const keyset<int> DEFAULT_IGNORE_ERRORS = keyset[
5639 /* potential co(tra)variant marker, which is highly opinionated */,
];
<<__Memoize>>
private function isErrorCodeConfiguredToIgnore(): (function(
this::TErrorCode,
): bool) {
$ignore_except = $this->config['ignore_except'] ?? null;
$ignore = $this->config['ignore'] ?? null;
if ($ignore is null) {
if ($ignore_except is null) {
return $error_code ==>
C\contains(self::DEFAULT_IGNORE_ERRORS, $error_code);
}
return $error_code ==> !C\contains($ignore_except, $error_code);
}
if ($ignore_except is null) {
return $error_code ==> C\contains($ignore, $error_code);
}
throw new \InvalidOperationException(
"Must not set both of the 'ignore_except' and 'ignore' fields in the configuration of HHClientLinter",
);
}
const type TJSONResult = shape(
'errors' => vec<HHClientLintError::TJSONError>,
'version' => string,
);
private static function blameCode(
vec<string> $file_lines,
HHClientLintError::TJSONError $error,
): string {
$line_number_1base = $error['line'];
$line_index_0base = $line_number_1base - 1;
$line_content = $file_lines[$line_index_0base];
return Str\slice(
$line_content,
$error['start'] - 1,
$error['end'] - $error['start'] + 1,
);
}
public async function getLintErrorsAsync(
): Awaitable<vec<HHClientLintError>> {
$lines = await __Private\ProcessExecutionQueues::HH_CLIENT->waitForAsync(
vec[
'--lint',
$this->getFile()->getPath(),
'--json',
'--from',
'hhast',
],
);
$hh_client_lint_result = TypeAssert\matches<this::TJSONResult>(
\json_decode(
C\firstx($lines),
/* assoc = */ true,
/* depth = */ 512,
\JSON_FB_HACK_ARRAYS,
),
);
$file_lines = Str\split($this->getFile()->getContents(), "\n");
return $hh_client_lint_result['errors']
|> Vec\map(
$$,
$error ==> new HHClientLintError(
$this->file,
$error,
$this::blameCode($file_lines, $error),
),
)
|> Vec\filter($$, $error ==> {
if ($error->getLintRule()->isSuppressedForFile($this->file)) {
return false;
}
$error_code = (int)$error->getLintRule()->getErrorCode();
if (C\contains(self::ALWAYS_IGNORE_ERRORS, $error_code)) {
return false;
}
if ($this->isErrorCodeConfiguredToIgnore()($error_code)) {
return false;
}
$range = $error->getRange();
list(list($line_number, $_), $_) = $range;
$previous_line_number = $line_number - 1;
if ($this->isSuppressedForLine($this->file, $previous_line_number)) {
return false;
}
if (
$error->getLintRule()
->isSuppressedForLine($this->file, $previous_line_number)
) {
return false;
}
return true;
});
}
}