forked from oschwald/SublimeLinter-contrib-rustc
-
Notifications
You must be signed in to change notification settings - Fork 1
/
linter.py
188 lines (149 loc) · 6.78 KB
/
linter.py
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#
# linter.py
# Linter for SublimeLinter3, a code checking framework for Sublime Text 3
#
# Written by Gregory Oschwald
# Copyright (c) 2014 Gregory Oschwald
#
# License: MIT
#
"""This module exports the Rustc plugin class."""
import os
from SublimeLinter.lint import Linter, util, persist
class Rust(Linter):
"""Provides an interface to Rust."""
defaults = {
'use-cargo': False,
'use-cargo-check': False,
'use-crate-root': False,
'crate-root': None,
}
cmd = ['rustc']
syntax = ('rust', 'rustenhanced')
tempfile_suffix = 'rs'
regex = r'''(?xi)
^(?:(?P<error>(error|fatal error))|(?P<warning>warning)).*?:\s+(?P<message>.+)\s*\r?
-->\s+(?P<file>.+?):(?P<line>\d+):(?P<col>\d+)$
'''
multiline = True
use_cargo = False
use_cargo_check = False
use_crate_root = False
cargo_config = None
crate_root = None
def run(self, cmd, code):
"""
Return a list with the command to execute.
The command chosen is resolved as follows:
If the `use-cargo` option is set, lint using a `cargo build`.
If cargo is not used, and the `use-crate-root` option is set, lint
the crate root. Finally, if the crate root cannot be determined, or the
`use-crate-root` option is not set, lint the current file.
Linting the crate (either through cargo or rustc) means that if
errors are caught in other files, errors on the current file might
not show up until these other errors are resolved.
Linting a single file means that any imports from higher in the
module hierarchy will probably cause an error and prevent proper
linting in the rest of the file.
"""
self.use_cargo = self.get_view_settings().get('use-cargo', False)
self.use_cargo_check = self.get_view_settings().get('use-cargo-check',
False)
self.use_crate_root = self.get_view_settings().get('use-crate-root',
False)
if self.use_cargo or self.use_cargo_check:
cargo_cmd = ['check'] if self.use_cargo_check else self.cmd
current_dir = os.path.dirname(self.filename)
self.cargo_config = util.find_file(current_dir, 'Cargo.toml')
if self.cargo_config:
self.tempfile_suffix = '-'
old_cwd = os.getcwd()
os.chdir(os.path.dirname(self.cargo_config))
try:
return util.communicate(
['cargo'] + cargo_cmd + ['--manifest-path',
self.cargo_config],
code=None,
output_stream=self.error_stream,
env=self.env)
finally:
os.chdir(old_cwd)
if self.use_crate_root:
self.crate_root = self.locate_crate_root()
if self.crate_root:
cmd.append(self.crate_root)
self.tempfile_suffix = '-'
return util.communicate(cmd,
code=None,
output_stream=self.error_stream,
env=self.env)
self.tempfile_suffix = 'rs'
return self.tmpfile(cmd, code)
def split_match(self, match):
"""
Return the components of the match.
We override this because Cargo lints all referenced files,
and we only want errors from the linted file. The same applies
when linting from the crate root. Of course when linting a single
file only, all the errors will be from that file because it is
in a temporary directory.
The matched file path is considered in the context of a working directory.
If it is an absolute path, the working directory will be ignored. This
working directory is not the same as the current Sublime Text process
working directory -- it is the working directory of an external command.
For Cargo, the working directory is the directory of Cargo.toml.
When working with a crate root, the working directory is the directory of the
crate root source file.
"""
# if match:
# if os.path.basename(self.filename) != os.path.basename(match.group('file')):
# match = None
matched_file = match.group('file') if match else None
if matched_file:
if self.use_cargo:
path = self.cargo_config
elif self.use_crate_root:
path = self.crate_root
else:
path = False
if path:
working_dir = os.path.dirname(path)
if not self.is_current_file(working_dir, matched_file):
match = None
return super().split_match(match)
def is_current_file(self, working_dir, matched_file):
"""
Return true if `matched_file` is logically the same file as `self.filename`.
Cargo example demonstrating how matching is done:
- os.getcwd() = '/Applications/Sublime Text.app/Contents/MacOS'
- `working_dir` = '/path/to/project'
- `matched_file` = 'src/foo.rs'
- `self.filename` = '/path/to/project/src/foo.rs'
The current OS directory is not considered at all -- comparison is only done
relative to where Cargo.toml was found. `os.path.realpath` is used to
normalize the filenames so that they can be directly compared after manipulation.
"""
abs_matched_file = os.path.join(working_dir, matched_file)
persist.debug('Sublime Text cwd: ', os.getcwd())
persist.debug('Build cwd: ', working_dir)
persist.debug('Current filename: ', self.filename)
persist.debug('Matched filename: ', matched_file)
persist.debug('Compared filename: ', abs_matched_file)
return os.path.realpath(self.filename) == os.path.realpath(
abs_matched_file)
def locate_crate_root(self):
"""
Return the filename of the crate root.
The filename may be manually set in a configuration file (highest priority),
or it is located by convention.
When no configuration is set, main.rs will take preference over lib.rs.
If neither main.rs or lib.rs are found, give up.
"""
crate_root = self.get_view_settings().get('crate-root', None)
if not crate_root:
crate_root = util.find_file(
os.path.dirname(self.filename), 'main.rs')
if not crate_root:
crate_root = util.find_file(
os.path.dirname(self.filename), 'lib.rs')
return crate_root