]>
Commit | Line | Data |
---|---|---|
dfeec247 XL |
1 | //! Implementation of lint checking. |
2 | //! | |
3 | //! The lint checking is mostly consolidated into one pass which runs | |
4 | //! after all other analyses. Throughout compilation, lint warnings | |
5 | //! can be added via the `add_lint` method on the Session structure. This | |
6 | //! requires a span and an ID of the node that the lint is being added to. The | |
7 | //! lint isn't actually emitted at that time because it is unknown what the | |
8 | //! actual lint level at that location is. | |
9 | //! | |
10 | //! To actually emit lint warnings/errors, a separate pass is used. | |
11 | //! A context keeps track of the current state of all lint levels. | |
12 | //! Upon entering a node of the ast which can modify the lint settings, the | |
13 | //! previous lint state is pushed onto a stack and the ast is then recursed | |
14 | //! upon. As the ast is traversed, this keeps track of the current lint level | |
15 | //! for all lint attributes. | |
16 | ||
17 | use self::TargetLint::*; | |
18 | ||
19 | use crate::levels::LintLevelsBuilder; | |
20 | use crate::passes::{EarlyLintPassObject, LateLintPassObject}; | |
3dfed10e | 21 | use rustc_ast as ast; |
dfeec247 XL |
22 | use rustc_data_structures::fx::FxHashMap; |
23 | use rustc_data_structures::sync; | |
29967ef6 | 24 | use rustc_errors::{add_elided_lifetime_in_path_suggestion, struct_span_err, Applicability}; |
dfeec247 | 25 | use rustc_hir as hir; |
f035d41b | 26 | use rustc_hir::def::Res; |
dfeec247 | 27 | use rustc_hir::def_id::{CrateNum, DefId}; |
ba9703b0 XL |
28 | use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; |
29 | use rustc_middle::lint::LintDiagnosticBuilder; | |
30 | use rustc_middle::middle::privacy::AccessLevels; | |
31 | use rustc_middle::middle::stability; | |
32 | use rustc_middle::ty::layout::{LayoutError, TyAndLayout}; | |
1b1a35ee | 33 | use rustc_middle::ty::print::with_no_trimmed_paths; |
ba9703b0 | 34 | use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt}; |
29967ef6 | 35 | use rustc_session::lint::BuiltinLintDiagnostics; |
dfeec247 XL |
36 | use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId}; |
37 | use rustc_session::Session; | |
29967ef6 | 38 | use rustc_session::SessionLintStore; |
fc512014 | 39 | use rustc_span::lev_distance::find_best_match_for_name; |
dfeec247 | 40 | use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP}; |
ba9703b0 | 41 | use rustc_target::abi::LayoutOf; |
fc512014 | 42 | use tracing::debug; |
dfeec247 | 43 | |
f035d41b | 44 | use std::cell::Cell; |
dfeec247 XL |
45 | use std::slice; |
46 | ||
47 | /// Information about the registered lints. | |
48 | /// | |
49 | /// This is basically the subset of `Context` that we can | |
50 | /// build early in the compile pipeline. | |
51 | pub struct LintStore { | |
52 | /// Registered lints. | |
53 | lints: Vec<&'static Lint>, | |
54 | ||
55 | /// Constructor functions for each variety of lint pass. | |
56 | /// | |
57 | /// These should only be called once, but since we want to avoid locks or | |
58 | /// interior mutability, we don't enforce this (and lints should, in theory, | |
59 | /// be compatible with being constructed more than once, though not | |
60 | /// necessarily in a sane manner. This is safe though.) | |
61 | pub pre_expansion_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>, | |
62 | pub early_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>, | |
63 | pub late_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>, | |
64 | /// This is unique in that we construct them per-module, so not once. | |
65 | pub late_module_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>, | |
66 | ||
67 | /// Lints indexed by name. | |
68 | by_name: FxHashMap<String, TargetLint>, | |
69 | ||
70 | /// Map of registered lint groups to what lints they expand to. | |
71 | lint_groups: FxHashMap<&'static str, LintGroup>, | |
72 | } | |
73 | ||
29967ef6 XL |
74 | impl SessionLintStore for LintStore { |
75 | fn name_to_lint(&self, lint_name: &str) -> LintId { | |
76 | let lints = self | |
77 | .find_lints(lint_name) | |
78 | .unwrap_or_else(|_| panic!("Failed to find lint with name `{}`", lint_name)); | |
79 | ||
80 | if let &[lint] = lints.as_slice() { | |
81 | return lint; | |
82 | } else { | |
83 | panic!("Found mutliple lints with name `{}`: {:?}", lint_name, lints); | |
84 | } | |
85 | } | |
86 | } | |
87 | ||
dfeec247 XL |
88 | /// The target of the `by_name` map, which accounts for renaming/deprecation. |
89 | enum TargetLint { | |
90 | /// A direct lint target | |
91 | Id(LintId), | |
92 | ||
93 | /// Temporary renaming, used for easing migration pain; see #16545 | |
94 | Renamed(String, LintId), | |
95 | ||
96 | /// Lint with this name existed previously, but has been removed/deprecated. | |
97 | /// The string argument is the reason for removal. | |
98 | Removed(String), | |
99 | } | |
100 | ||
101 | pub enum FindLintError { | |
102 | NotFound, | |
103 | Removed, | |
104 | } | |
105 | ||
106 | struct LintAlias { | |
107 | name: &'static str, | |
108 | /// Whether deprecation warnings should be suppressed for this alias. | |
109 | silent: bool, | |
110 | } | |
111 | ||
112 | struct LintGroup { | |
113 | lint_ids: Vec<LintId>, | |
114 | from_plugin: bool, | |
115 | depr: Option<LintAlias>, | |
116 | } | |
117 | ||
118 | pub enum CheckLintNameResult<'a> { | |
119 | Ok(&'a [LintId]), | |
120 | /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name. | |
121 | NoLint(Option<Symbol>), | |
122 | /// The lint is either renamed or removed. This is the warning | |
123 | /// message, and an optional new name (`None` if removed). | |
124 | Warning(String, Option<String>), | |
125 | /// The lint is from a tool. If the Option is None, then either | |
126 | /// the lint does not exist in the tool or the code was not | |
127 | /// compiled with the tool and therefore the lint was never | |
128 | /// added to the `LintStore`. Otherwise the `LintId` will be | |
129 | /// returned as if it where a rustc lint. | |
130 | Tool(Result<&'a [LintId], (Option<&'a [LintId]>, String)>), | |
131 | } | |
132 | ||
133 | impl LintStore { | |
134 | pub fn new() -> LintStore { | |
135 | LintStore { | |
136 | lints: vec![], | |
137 | pre_expansion_passes: vec![], | |
138 | early_passes: vec![], | |
139 | late_passes: vec![], | |
140 | late_module_passes: vec![], | |
141 | by_name: Default::default(), | |
142 | lint_groups: Default::default(), | |
143 | } | |
144 | } | |
145 | ||
146 | pub fn get_lints<'t>(&'t self) -> &'t [&'static Lint] { | |
147 | &self.lints | |
148 | } | |
149 | ||
150 | pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> { | |
151 | self.lint_groups | |
152 | .iter() | |
153 | .filter(|(_, LintGroup { depr, .. })| { | |
154 | // Don't display deprecated lint groups. | |
155 | depr.is_none() | |
156 | }) | |
157 | .map(|(k, LintGroup { lint_ids, from_plugin, .. })| { | |
158 | (*k, lint_ids.clone(), *from_plugin) | |
159 | }) | |
160 | .collect() | |
161 | } | |
162 | ||
163 | pub fn register_early_pass( | |
164 | &mut self, | |
165 | pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync, | |
166 | ) { | |
167 | self.early_passes.push(Box::new(pass)); | |
168 | } | |
169 | ||
170 | pub fn register_pre_expansion_pass( | |
171 | &mut self, | |
172 | pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync, | |
173 | ) { | |
174 | self.pre_expansion_passes.push(Box::new(pass)); | |
175 | } | |
176 | ||
177 | pub fn register_late_pass( | |
178 | &mut self, | |
179 | pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync, | |
180 | ) { | |
181 | self.late_passes.push(Box::new(pass)); | |
182 | } | |
183 | ||
184 | pub fn register_late_mod_pass( | |
185 | &mut self, | |
186 | pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync, | |
187 | ) { | |
188 | self.late_module_passes.push(Box::new(pass)); | |
189 | } | |
190 | ||
191 | // Helper method for register_early/late_pass | |
192 | pub fn register_lints(&mut self, lints: &[&'static Lint]) { | |
193 | for lint in lints { | |
194 | self.lints.push(lint); | |
195 | ||
196 | let id = LintId::of(lint); | |
197 | if self.by_name.insert(lint.name_lower(), Id(id)).is_some() { | |
198 | bug!("duplicate specification of lint {}", lint.name_lower()) | |
199 | } | |
200 | ||
201 | if let Some(FutureIncompatibleInfo { edition, .. }) = lint.future_incompatible { | |
202 | if let Some(edition) = edition { | |
203 | self.lint_groups | |
204 | .entry(edition.lint_name()) | |
205 | .or_insert(LintGroup { | |
206 | lint_ids: vec![], | |
207 | from_plugin: lint.is_plugin, | |
208 | depr: None, | |
209 | }) | |
210 | .lint_ids | |
211 | .push(id); | |
212 | } | |
213 | ||
214 | self.lint_groups | |
215 | .entry("future_incompatible") | |
216 | .or_insert(LintGroup { | |
217 | lint_ids: vec![], | |
218 | from_plugin: lint.is_plugin, | |
219 | depr: None, | |
220 | }) | |
221 | .lint_ids | |
222 | .push(id); | |
223 | } | |
224 | } | |
225 | } | |
226 | ||
227 | pub fn register_group_alias(&mut self, lint_name: &'static str, alias: &'static str) { | |
228 | self.lint_groups.insert( | |
229 | alias, | |
230 | LintGroup { | |
231 | lint_ids: vec![], | |
232 | from_plugin: false, | |
233 | depr: Some(LintAlias { name: lint_name, silent: true }), | |
234 | }, | |
235 | ); | |
236 | } | |
237 | ||
238 | pub fn register_group( | |
239 | &mut self, | |
240 | from_plugin: bool, | |
241 | name: &'static str, | |
242 | deprecated_name: Option<&'static str>, | |
243 | to: Vec<LintId>, | |
244 | ) { | |
245 | let new = self | |
246 | .lint_groups | |
247 | .insert(name, LintGroup { lint_ids: to, from_plugin, depr: None }) | |
248 | .is_none(); | |
249 | if let Some(deprecated) = deprecated_name { | |
250 | self.lint_groups.insert( | |
251 | deprecated, | |
252 | LintGroup { | |
253 | lint_ids: vec![], | |
254 | from_plugin, | |
255 | depr: Some(LintAlias { name, silent: false }), | |
256 | }, | |
257 | ); | |
258 | } | |
259 | ||
260 | if !new { | |
261 | bug!("duplicate specification of lint group {}", name); | |
262 | } | |
263 | } | |
264 | ||
265 | pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { | |
266 | let target = match self.by_name.get(new_name) { | |
267 | Some(&Id(lint_id)) => lint_id, | |
268 | _ => bug!("invalid lint renaming of {} to {}", old_name, new_name), | |
269 | }; | |
270 | self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target)); | |
271 | } | |
272 | ||
273 | pub fn register_removed(&mut self, name: &str, reason: &str) { | |
274 | self.by_name.insert(name.into(), Removed(reason.into())); | |
275 | } | |
276 | ||
277 | pub fn find_lints(&self, mut lint_name: &str) -> Result<Vec<LintId>, FindLintError> { | |
278 | match self.by_name.get(lint_name) { | |
279 | Some(&Id(lint_id)) => Ok(vec![lint_id]), | |
280 | Some(&Renamed(_, lint_id)) => Ok(vec![lint_id]), | |
281 | Some(&Removed(_)) => Err(FindLintError::Removed), | |
282 | None => loop { | |
283 | return match self.lint_groups.get(lint_name) { | |
284 | Some(LintGroup { lint_ids, depr, .. }) => { | |
285 | if let Some(LintAlias { name, .. }) = depr { | |
286 | lint_name = name; | |
287 | continue; | |
288 | } | |
289 | Ok(lint_ids.clone()) | |
290 | } | |
291 | None => Err(FindLintError::Removed), | |
292 | }; | |
293 | }, | |
294 | } | |
295 | } | |
296 | ||
297 | /// Checks the validity of lint names derived from the command line | |
298 | pub fn check_lint_name_cmdline(&self, sess: &Session, lint_name: &str, level: Level) { | |
299 | let db = match self.check_lint_name(lint_name, None) { | |
300 | CheckLintNameResult::Ok(_) => None, | |
301 | CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)), | |
302 | CheckLintNameResult::NoLint(suggestion) => { | |
303 | let mut err = | |
304 | struct_span_err!(sess, DUMMY_SP, E0602, "unknown lint: `{}`", lint_name); | |
305 | ||
306 | if let Some(suggestion) = suggestion { | |
307 | err.help(&format!("did you mean: `{}`", suggestion)); | |
308 | } | |
309 | ||
310 | Some(err) | |
311 | } | |
312 | CheckLintNameResult::Tool(result) => match result { | |
313 | Err((Some(_), new_name)) => Some(sess.struct_warn(&format!( | |
314 | "lint name `{}` is deprecated \ | |
315 | and does not have an effect anymore. \ | |
316 | Use: {}", | |
317 | lint_name, new_name | |
318 | ))), | |
319 | _ => None, | |
320 | }, | |
321 | }; | |
322 | ||
323 | if let Some(mut db) = db { | |
324 | let msg = format!( | |
325 | "requested on the command line with `{} {}`", | |
326 | match level { | |
327 | Level::Allow => "-A", | |
328 | Level::Warn => "-W", | |
329 | Level::Deny => "-D", | |
330 | Level::Forbid => "-F", | |
331 | }, | |
332 | lint_name | |
333 | ); | |
334 | db.note(&msg); | |
335 | db.emit(); | |
336 | } | |
337 | } | |
338 | ||
fc512014 XL |
339 | /// True if this symbol represents a lint group name. |
340 | pub fn is_lint_group(&self, lint_name: Symbol) -> bool { | |
341 | debug!( | |
342 | "is_lint_group(lint_name={:?}, lint_groups={:?})", | |
343 | lint_name, | |
344 | self.lint_groups.keys().collect::<Vec<_>>() | |
345 | ); | |
346 | let lint_name_str = &*lint_name.as_str(); | |
347 | self.lint_groups.contains_key(&lint_name_str) || { | |
348 | let warnings_name_str = crate::WARNINGS.name_lower(); | |
349 | lint_name_str == &*warnings_name_str | |
350 | } | |
351 | } | |
352 | ||
dfeec247 XL |
353 | /// Checks the name of a lint for its existence, and whether it was |
354 | /// renamed or removed. Generates a DiagnosticBuilder containing a | |
355 | /// warning for renamed and removed lints. This is over both lint | |
356 | /// names from attributes and those passed on the command line. Since | |
357 | /// it emits non-fatal warnings and there are *two* lint passes that | |
358 | /// inspect attributes, this is only run from the late pass to avoid | |
359 | /// printing duplicate warnings. | |
360 | pub fn check_lint_name( | |
361 | &self, | |
362 | lint_name: &str, | |
363 | tool_name: Option<Symbol>, | |
364 | ) -> CheckLintNameResult<'_> { | |
365 | let complete_name = if let Some(tool_name) = tool_name { | |
366 | format!("{}::{}", tool_name, lint_name) | |
367 | } else { | |
368 | lint_name.to_string() | |
369 | }; | |
370 | // If the lint was scoped with `tool::` check if the tool lint exists | |
74b04a01 | 371 | if tool_name.is_some() { |
dfeec247 XL |
372 | match self.by_name.get(&complete_name) { |
373 | None => match self.lint_groups.get(&*complete_name) { | |
374 | None => return CheckLintNameResult::Tool(Err((None, String::new()))), | |
375 | Some(LintGroup { lint_ids, .. }) => { | |
376 | return CheckLintNameResult::Tool(Ok(&lint_ids)); | |
377 | } | |
378 | }, | |
379 | Some(&Id(ref id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))), | |
380 | // If the lint was registered as removed or renamed by the lint tool, we don't need | |
381 | // to treat tool_lints and rustc lints different and can use the code below. | |
382 | _ => {} | |
383 | } | |
384 | } | |
385 | match self.by_name.get(&complete_name) { | |
386 | Some(&Renamed(ref new_name, _)) => CheckLintNameResult::Warning( | |
387 | format!("lint `{}` has been renamed to `{}`", complete_name, new_name), | |
388 | Some(new_name.to_owned()), | |
389 | ), | |
390 | Some(&Removed(ref reason)) => CheckLintNameResult::Warning( | |
391 | format!("lint `{}` has been removed: `{}`", complete_name, reason), | |
392 | None, | |
393 | ), | |
394 | None => match self.lint_groups.get(&*complete_name) { | |
395 | // If neither the lint, nor the lint group exists check if there is a `clippy::` | |
396 | // variant of this lint | |
397 | None => self.check_tool_name_for_backwards_compat(&complete_name, "clippy"), | |
398 | Some(LintGroup { lint_ids, depr, .. }) => { | |
399 | // Check if the lint group name is deprecated | |
400 | if let Some(LintAlias { name, silent }) = depr { | |
401 | let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap(); | |
402 | return if *silent { | |
403 | CheckLintNameResult::Ok(&lint_ids) | |
404 | } else { | |
74b04a01 | 405 | CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string()))) |
dfeec247 XL |
406 | }; |
407 | } | |
408 | CheckLintNameResult::Ok(&lint_ids) | |
409 | } | |
410 | }, | |
411 | Some(&Id(ref id)) => CheckLintNameResult::Ok(slice::from_ref(id)), | |
412 | } | |
413 | } | |
414 | ||
415 | fn check_tool_name_for_backwards_compat( | |
416 | &self, | |
417 | lint_name: &str, | |
418 | tool_name: &str, | |
419 | ) -> CheckLintNameResult<'_> { | |
420 | let complete_name = format!("{}::{}", tool_name, lint_name); | |
421 | match self.by_name.get(&complete_name) { | |
422 | None => match self.lint_groups.get(&*complete_name) { | |
423 | // Now we are sure, that this lint exists nowhere | |
424 | None => { | |
425 | let symbols = | |
426 | self.by_name.keys().map(|name| Symbol::intern(&name)).collect::<Vec<_>>(); | |
427 | ||
3dfed10e | 428 | let suggestion = find_best_match_for_name( |
fc512014 | 429 | &symbols, |
3dfed10e XL |
430 | Symbol::intern(&lint_name.to_lowercase()), |
431 | None, | |
432 | ); | |
dfeec247 XL |
433 | |
434 | CheckLintNameResult::NoLint(suggestion) | |
435 | } | |
436 | Some(LintGroup { lint_ids, depr, .. }) => { | |
437 | // Reaching this would be weird, but let's cover this case anyway | |
438 | if let Some(LintAlias { name, silent }) = depr { | |
439 | let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap(); | |
440 | return if *silent { | |
441 | CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name))) | |
442 | } else { | |
74b04a01 | 443 | CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string()))) |
dfeec247 XL |
444 | }; |
445 | } | |
446 | CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name))) | |
447 | } | |
448 | }, | |
449 | Some(&Id(ref id)) => { | |
450 | CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name))) | |
451 | } | |
452 | _ => CheckLintNameResult::NoLint(None), | |
453 | } | |
454 | } | |
455 | } | |
456 | ||
457 | /// Context for lint checking after type checking. | |
f035d41b | 458 | pub struct LateContext<'tcx> { |
dfeec247 XL |
459 | /// Type context we're checking in. |
460 | pub tcx: TyCtxt<'tcx>, | |
461 | ||
f035d41b XL |
462 | /// Current body, or `None` if outside a body. |
463 | pub enclosing_body: Option<hir::BodyId>, | |
464 | ||
3dfed10e XL |
465 | /// Type-checking results for the current body. Access using the `typeck_results` |
466 | /// and `maybe_typeck_results` methods, which handle querying the typeck results on demand. | |
f035d41b XL |
467 | // FIXME(eddyb) move all the code accessing internal fields like this, |
468 | // to this module, to avoid exposing it to lint logic. | |
3dfed10e | 469 | pub(super) cached_typeck_results: Cell<Option<&'tcx ty::TypeckResults<'tcx>>>, |
dfeec247 XL |
470 | |
471 | /// Parameter environment for the item we are in. | |
472 | pub param_env: ty::ParamEnv<'tcx>, | |
473 | ||
474 | /// Items accessible from the crate being checked. | |
f035d41b | 475 | pub access_levels: &'tcx AccessLevels, |
dfeec247 XL |
476 | |
477 | /// The store of registered lints and the lint levels. | |
478 | pub lint_store: &'tcx LintStore, | |
479 | ||
480 | pub last_node_with_lint_attrs: hir::HirId, | |
481 | ||
482 | /// Generic type parameters in scope for the item we are in. | |
483 | pub generics: Option<&'tcx hir::Generics<'tcx>>, | |
484 | ||
485 | /// We are only looking at one module | |
486 | pub only_module: bool, | |
487 | } | |
488 | ||
489 | /// Context for lint checking of the AST, after expansion, before lowering to | |
490 | /// HIR. | |
491 | pub struct EarlyContext<'a> { | |
492 | /// Type context we're checking in. | |
493 | pub sess: &'a Session, | |
494 | ||
495 | /// The crate being checked. | |
496 | pub krate: &'a ast::Crate, | |
497 | ||
498 | pub builder: LintLevelsBuilder<'a>, | |
499 | ||
500 | /// The store of registered lints and the lint levels. | |
501 | pub lint_store: &'a LintStore, | |
502 | ||
503 | pub buffered: LintBuffer, | |
504 | } | |
505 | ||
506 | pub trait LintPassObject: Sized {} | |
507 | ||
508 | impl LintPassObject for EarlyLintPassObject {} | |
509 | ||
510 | impl LintPassObject for LateLintPassObject {} | |
511 | ||
512 | pub trait LintContext: Sized { | |
513 | type PassObject: LintPassObject; | |
514 | ||
515 | fn sess(&self) -> &Session; | |
516 | fn lints(&self) -> &LintStore; | |
517 | ||
74b04a01 | 518 | fn lookup_with_diagnostics( |
dfeec247 XL |
519 | &self, |
520 | lint: &'static Lint, | |
74b04a01 XL |
521 | span: Option<impl Into<MultiSpan>>, |
522 | decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), | |
dfeec247 XL |
523 | diagnostic: BuiltinLintDiagnostics, |
524 | ) { | |
74b04a01 XL |
525 | self.lookup(lint, span, |lint| { |
526 | // We first generate a blank diagnostic. | |
527 | let mut db = lint.build(""); | |
528 | ||
529 | // Now, set up surrounding context. | |
530 | let sess = self.sess(); | |
531 | match diagnostic { | |
532 | BuiltinLintDiagnostics::Normal => (), | |
533 | BuiltinLintDiagnostics::BareTraitObject(span, is_global) => { | |
534 | let (sugg, app) = match sess.source_map().span_to_snippet(span) { | |
535 | Ok(s) if is_global => { | |
536 | (format!("dyn ({})", s), Applicability::MachineApplicable) | |
537 | } | |
538 | Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable), | |
539 | Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders), | |
540 | }; | |
541 | db.span_suggestion(span, "use `dyn`", sugg, app); | |
542 | } | |
543 | BuiltinLintDiagnostics::AbsPathWithModule(span) => { | |
544 | let (sugg, app) = match sess.source_map().span_to_snippet(span) { | |
545 | Ok(ref s) => { | |
546 | // FIXME(Manishearth) ideally the emitting code | |
547 | // can tell us whether or not this is global | |
548 | let opt_colon = | |
549 | if s.trim_start().starts_with("::") { "" } else { "::" }; | |
550 | ||
551 | (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable) | |
552 | } | |
553 | Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders), | |
554 | }; | |
555 | db.span_suggestion(span, "use `crate`", sugg, app); | |
556 | } | |
557 | BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => { | |
558 | db.span_label( | |
559 | span, | |
560 | "names from parent modules are not accessible without an explicit import", | |
561 | ); | |
562 | } | |
563 | BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths( | |
564 | span_def, | |
565 | ) => { | |
566 | db.span_note(span_def, "the macro is defined here"); | |
567 | } | |
568 | BuiltinLintDiagnostics::ElidedLifetimesInPaths( | |
dfeec247 XL |
569 | n, |
570 | path_span, | |
571 | incl_angl_brckt, | |
572 | insertion_span, | |
573 | anon_lts, | |
74b04a01 XL |
574 | ) => { |
575 | add_elided_lifetime_in_path_suggestion( | |
29967ef6 | 576 | sess.source_map(), |
74b04a01 XL |
577 | &mut db, |
578 | n, | |
579 | path_span, | |
580 | incl_angl_brckt, | |
581 | insertion_span, | |
582 | anon_lts, | |
dfeec247 XL |
583 | ); |
584 | } | |
74b04a01 XL |
585 | BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => { |
586 | db.span_suggestion(span, ¬e, sugg, Applicability::MaybeIncorrect); | |
587 | } | |
588 | BuiltinLintDiagnostics::UnusedImports(message, replaces) => { | |
589 | if !replaces.is_empty() { | |
590 | db.tool_only_multipart_suggestion( | |
591 | &message, | |
592 | replaces, | |
593 | Applicability::MachineApplicable, | |
594 | ); | |
595 | } | |
596 | } | |
597 | BuiltinLintDiagnostics::RedundantImport(spans, ident) => { | |
598 | for (span, is_imported) in spans { | |
599 | let introduced = if is_imported { "imported" } else { "defined" }; | |
600 | db.span_label( | |
601 | span, | |
602 | format!("the item `{}` is already {} here", ident, introduced), | |
603 | ); | |
604 | } | |
605 | } | |
606 | BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => { | |
3dfed10e | 607 | stability::deprecation_suggestion(&mut db, "macro", suggestion, span) |
74b04a01 XL |
608 | } |
609 | BuiltinLintDiagnostics::UnusedDocComment(span) => { | |
f9f354fc | 610 | db.span_label(span, "rustdoc does not generate documentation for macro invocations"); |
74b04a01 XL |
611 | db.help("to document an item produced by a macro, \ |
612 | the macro must produce the documentation as part of its expansion"); | |
dfeec247 XL |
613 | } |
614 | } | |
74b04a01 XL |
615 | // Rewrap `db`, and pass control to the user. |
616 | decorate(LintDiagnosticBuilder::new(db)); | |
617 | }); | |
dfeec247 XL |
618 | } |
619 | ||
74b04a01 XL |
620 | // FIXME: These methods should not take an Into<MultiSpan> -- instead, callers should need to |
621 | // set the span in their `decorate` function (preferably using set_span). | |
dfeec247 XL |
622 | fn lookup<S: Into<MultiSpan>>( |
623 | &self, | |
624 | lint: &'static Lint, | |
625 | span: Option<S>, | |
74b04a01 XL |
626 | decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), |
627 | ); | |
dfeec247 XL |
628 | |
629 | fn struct_span_lint<S: Into<MultiSpan>>( | |
630 | &self, | |
631 | lint: &'static Lint, | |
632 | span: S, | |
74b04a01 | 633 | decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), |
dfeec247 | 634 | ) { |
74b04a01 | 635 | self.lookup(lint, Some(span), decorate); |
dfeec247 | 636 | } |
dfeec247 | 637 | /// Emit a lint at the appropriate level, with no associated span. |
74b04a01 XL |
638 | fn lint(&self, lint: &'static Lint, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>)) { |
639 | self.lookup(lint, None as Option<Span>, decorate); | |
dfeec247 XL |
640 | } |
641 | } | |
642 | ||
643 | impl<'a> EarlyContext<'a> { | |
644 | pub fn new( | |
645 | sess: &'a Session, | |
646 | lint_store: &'a LintStore, | |
647 | krate: &'a ast::Crate, | |
648 | buffered: LintBuffer, | |
649 | warn_about_weird_lints: bool, | |
650 | ) -> EarlyContext<'a> { | |
651 | EarlyContext { | |
652 | sess, | |
653 | krate, | |
654 | lint_store, | |
655 | builder: LintLevelsBuilder::new(sess, warn_about_weird_lints, lint_store), | |
656 | buffered, | |
657 | } | |
658 | } | |
659 | } | |
660 | ||
f035d41b | 661 | impl LintContext for LateContext<'_> { |
dfeec247 XL |
662 | type PassObject = LateLintPassObject; |
663 | ||
664 | /// Gets the overall compiler `Session` object. | |
665 | fn sess(&self) -> &Session { | |
666 | &self.tcx.sess | |
667 | } | |
668 | ||
669 | fn lints(&self) -> &LintStore { | |
670 | &*self.lint_store | |
671 | } | |
672 | ||
673 | fn lookup<S: Into<MultiSpan>>( | |
674 | &self, | |
675 | lint: &'static Lint, | |
676 | span: Option<S>, | |
74b04a01 XL |
677 | decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), |
678 | ) { | |
dfeec247 XL |
679 | let hir_id = self.last_node_with_lint_attrs; |
680 | ||
681 | match span { | |
74b04a01 XL |
682 | Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, decorate), |
683 | None => self.tcx.struct_lint_node(lint, hir_id, decorate), | |
dfeec247 XL |
684 | } |
685 | } | |
686 | } | |
687 | ||
688 | impl LintContext for EarlyContext<'_> { | |
689 | type PassObject = EarlyLintPassObject; | |
690 | ||
691 | /// Gets the overall compiler `Session` object. | |
692 | fn sess(&self) -> &Session { | |
693 | &self.sess | |
694 | } | |
695 | ||
696 | fn lints(&self) -> &LintStore { | |
697 | &*self.lint_store | |
698 | } | |
699 | ||
700 | fn lookup<S: Into<MultiSpan>>( | |
701 | &self, | |
702 | lint: &'static Lint, | |
703 | span: Option<S>, | |
74b04a01 XL |
704 | decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), |
705 | ) { | |
706 | self.builder.struct_lint(lint, span.map(|s| s.into()), decorate) | |
dfeec247 XL |
707 | } |
708 | } | |
709 | ||
f035d41b | 710 | impl<'tcx> LateContext<'tcx> { |
3dfed10e | 711 | /// Gets the type-checking results for the current body, |
f035d41b | 712 | /// or `None` if outside a body. |
3dfed10e XL |
713 | pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> { |
714 | self.cached_typeck_results.get().or_else(|| { | |
f035d41b | 715 | self.enclosing_body.map(|body| { |
3dfed10e XL |
716 | let typeck_results = self.tcx.typeck_body(body); |
717 | self.cached_typeck_results.set(Some(typeck_results)); | |
718 | typeck_results | |
f035d41b XL |
719 | }) |
720 | }) | |
721 | } | |
722 | ||
3dfed10e | 723 | /// Gets the type-checking results for the current body. |
f035d41b XL |
724 | /// As this will ICE if called outside bodies, only call when working with |
725 | /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). | |
726 | #[track_caller] | |
3dfed10e XL |
727 | pub fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> { |
728 | self.maybe_typeck_results().expect("`LateContext::typeck_results` called outside of body") | |
f035d41b XL |
729 | } |
730 | ||
731 | /// Returns the final resolution of a `QPath`, or `Res::Err` if unavailable. | |
3dfed10e | 732 | /// Unlike `.typeck_results().qpath_res(qpath, id)`, this can be used even outside |
f035d41b XL |
733 | /// bodies (e.g. for paths in `hir::Ty`), without any risk of ICE-ing. |
734 | pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { | |
735 | match *qpath { | |
736 | hir::QPath::Resolved(_, ref path) => path.res, | |
3dfed10e XL |
737 | hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self |
738 | .maybe_typeck_results() | |
739 | .and_then(|typeck_results| typeck_results.type_dependent_def(id)) | |
f035d41b XL |
740 | .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), |
741 | } | |
742 | } | |
743 | ||
dfeec247 XL |
744 | /// Check if a `DefId`'s path matches the given absolute type path usage. |
745 | /// | |
746 | /// Anonymous scopes such as `extern` imports are matched with `kw::Invalid`; | |
747 | /// inherent `impl` blocks are matched with the name of the type. | |
748 | /// | |
1b1a35ee XL |
749 | /// Instead of using this method, it is often preferable to instead use |
750 | /// `rustc_diagnostic_item` or a `lang_item`. This is less prone to errors | |
751 | /// as paths get invalidated if the target definition moves. | |
752 | /// | |
dfeec247 XL |
753 | /// # Examples |
754 | /// | |
755 | /// ```rust,ignore (no context or def id available) | |
756 | /// if cx.match_def_path(def_id, &[sym::core, sym::option, sym::Option]) { | |
757 | /// // The given `def_id` is that of an `Option` type | |
758 | /// } | |
759 | /// ``` | |
760 | pub fn match_def_path(&self, def_id: DefId, path: &[Symbol]) -> bool { | |
761 | let names = self.get_def_path(def_id); | |
762 | ||
763 | names.len() == path.len() && names.into_iter().zip(path.iter()).all(|(a, &b)| a == b) | |
764 | } | |
765 | ||
766 | /// Gets the absolute path of `def_id` as a vector of `Symbol`. | |
767 | /// | |
768 | /// # Examples | |
769 | /// | |
770 | /// ```rust,ignore (no context or def id available) | |
771 | /// let def_path = cx.get_def_path(def_id); | |
772 | /// if let &[sym::core, sym::option, sym::Option] = &def_path[..] { | |
773 | /// // The given `def_id` is that of an `Option` type | |
774 | /// } | |
775 | /// ``` | |
776 | pub fn get_def_path(&self, def_id: DefId) -> Vec<Symbol> { | |
777 | pub struct AbsolutePathPrinter<'tcx> { | |
778 | pub tcx: TyCtxt<'tcx>, | |
779 | } | |
780 | ||
781 | impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { | |
782 | type Error = !; | |
783 | ||
784 | type Path = Vec<Symbol>; | |
785 | type Region = (); | |
786 | type Type = (); | |
787 | type DynExistential = (); | |
788 | type Const = (); | |
789 | ||
790 | fn tcx(&self) -> TyCtxt<'tcx> { | |
791 | self.tcx | |
792 | } | |
793 | ||
794 | fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> { | |
795 | Ok(()) | |
796 | } | |
797 | ||
798 | fn print_type(self, _ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> { | |
799 | Ok(()) | |
800 | } | |
801 | ||
802 | fn print_dyn_existential( | |
803 | self, | |
fc512014 | 804 | _predicates: &'tcx ty::List<ty::Binder<ty::ExistentialPredicate<'tcx>>>, |
dfeec247 XL |
805 | ) -> Result<Self::DynExistential, Self::Error> { |
806 | Ok(()) | |
807 | } | |
808 | ||
809 | fn print_const(self, _ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error> { | |
810 | Ok(()) | |
811 | } | |
812 | ||
813 | fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error> { | |
814 | Ok(vec![self.tcx.original_crate_name(cnum)]) | |
815 | } | |
816 | ||
817 | fn path_qualified( | |
818 | self, | |
819 | self_ty: Ty<'tcx>, | |
820 | trait_ref: Option<ty::TraitRef<'tcx>>, | |
821 | ) -> Result<Self::Path, Self::Error> { | |
822 | if trait_ref.is_none() { | |
1b1a35ee | 823 | if let ty::Adt(def, substs) = self_ty.kind() { |
dfeec247 XL |
824 | return self.print_def_path(def.did, substs); |
825 | } | |
826 | } | |
827 | ||
828 | // This shouldn't ever be needed, but just in case: | |
1b1a35ee XL |
829 | with_no_trimmed_paths(|| { |
830 | Ok(vec![match trait_ref { | |
831 | Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)), | |
832 | None => Symbol::intern(&format!("<{}>", self_ty)), | |
833 | }]) | |
834 | }) | |
dfeec247 XL |
835 | } |
836 | ||
837 | fn path_append_impl( | |
838 | self, | |
839 | print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, | |
840 | _disambiguated_data: &DisambiguatedDefPathData, | |
841 | self_ty: Ty<'tcx>, | |
842 | trait_ref: Option<ty::TraitRef<'tcx>>, | |
843 | ) -> Result<Self::Path, Self::Error> { | |
844 | let mut path = print_prefix(self)?; | |
845 | ||
846 | // This shouldn't ever be needed, but just in case: | |
847 | path.push(match trait_ref { | |
1b1a35ee XL |
848 | Some(trait_ref) => with_no_trimmed_paths(|| { |
849 | Symbol::intern(&format!( | |
850 | "<impl {} for {}>", | |
851 | trait_ref.print_only_trait_path(), | |
852 | self_ty | |
853 | )) | |
854 | }), | |
855 | None => { | |
856 | with_no_trimmed_paths(|| Symbol::intern(&format!("<impl {}>", self_ty))) | |
857 | } | |
dfeec247 XL |
858 | }); |
859 | ||
860 | Ok(path) | |
861 | } | |
862 | ||
863 | fn path_append( | |
864 | self, | |
865 | print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, | |
866 | disambiguated_data: &DisambiguatedDefPathData, | |
867 | ) -> Result<Self::Path, Self::Error> { | |
868 | let mut path = print_prefix(self)?; | |
869 | ||
870 | // Skip `::{{constructor}}` on tuple/unit structs. | |
ba9703b0 XL |
871 | if let DefPathData::Ctor = disambiguated_data.data { |
872 | return Ok(path); | |
dfeec247 XL |
873 | } |
874 | ||
1b1a35ee | 875 | path.push(Symbol::intern(&disambiguated_data.data.to_string())); |
dfeec247 XL |
876 | Ok(path) |
877 | } | |
878 | ||
879 | fn path_generic_args( | |
880 | self, | |
881 | print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, | |
882 | _args: &[GenericArg<'tcx>], | |
883 | ) -> Result<Self::Path, Self::Error> { | |
884 | print_prefix(self) | |
885 | } | |
886 | } | |
887 | ||
888 | AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]).unwrap() | |
889 | } | |
890 | } | |
891 | ||
f035d41b | 892 | impl<'tcx> LayoutOf for LateContext<'tcx> { |
dfeec247 | 893 | type Ty = Ty<'tcx>; |
ba9703b0 | 894 | type TyAndLayout = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>; |
dfeec247 | 895 | |
ba9703b0 | 896 | fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { |
dfeec247 XL |
897 | self.tcx.layout_of(self.param_env.and(ty)) |
898 | } | |
899 | } |