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
|
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import bisect
import json
import os
import subprocess
import sys
from mozlint import result
from mozlint.pathutils import expand_exclusions
def in_sorted_list(l, x):
i = bisect.bisect_left(l, x)
return i < len(l) and l[i] == x
def handle_clippy_msg(config, line, log, base_path, files):
try:
detail = json.loads(line)
if "message" in detail:
p = detail["target"]["src_path"]
detail = detail["message"]
if "level" in detail:
if (
detail["level"] == "error" or detail["level"] == "failure-note"
) and not detail["code"]:
log.debug(
"Error outside of clippy."
"This means that the build failed. Therefore, skipping this"
)
log.debug("File = {} / Detail = {}".format(p, detail))
return
# We are in a clippy warning
if len(detail["spans"]) == 0:
# For some reason, at the end of the summary, we can
# get the following line
# {'rendered': 'warning: 5 warnings emitted\n\n', 'children':
# [], 'code': None, 'level': 'warning', 'message':
# '5 warnings emitted', 'spans': []}
# if this is the case, skip it
log.debug(
"Skipping the summary line {} for file {}".format(detail, p)
)
return
l = detail["spans"][0]
if not in_sorted_list(files, p):
return
p = os.path.join(base_path, l["file_name"])
res = {
"path": p,
"level": detail["level"],
"lineno": l["line_start"],
"column": l["column_start"],
"message": detail["message"],
"hint": detail["rendered"],
"rule": detail["code"]["code"],
"lineoffset": l["line_end"] - l["line_start"],
}
return result.from_config(config, **res)
except json.decoder.JSONDecodeError:
log.debug("Could not parse the output:")
log.debug("clippy output: {}".format(line))
return
def lint(paths, config, fix=None, **lintargs):
files = list(expand_exclusions(paths, config, lintargs["root"]))
files.sort()
log = lintargs["log"]
results = []
mach_path = lintargs["root"] + "/mach"
march_cargo_process = subprocess.Popen(
[
sys.executable,
mach_path,
"--log-no-times",
"cargo",
"clippy",
"--",
"--message-format=json",
],
stdout=subprocess.PIPE,
text=True,
)
for l in march_cargo_process.stdout:
r = handle_clippy_msg(config, l, log, lintargs["root"], files)
if r is not None:
results.append(r)
march_cargo_process.wait()
if fix:
log.error("Rust linting in mach does not support --fix")
return results
|