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