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::errors::{
20 CheckNameDeprecated, CheckNameUnknown, CheckNameUnknownTool, CheckNameWarning, RequestedLevel,
21 UnsupportedGroup,
22 };
23 use crate::levels::LintLevelsBuilder;
24 use crate::passes::{EarlyLintPassObject, LateLintPassObject};
25 use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
26 use rustc_data_structures::fx::FxHashMap;
27 use rustc_data_structures::sync;
28 use rustc_errors::{add_elided_lifetime_in_path_suggestion, DiagnosticBuilder, DiagnosticMessage};
29 use rustc_errors::{Applicability, DecorateLint, MultiSpan, SuggestionStyle};
30 use rustc_hir as hir;
31 use rustc_hir::def::Res;
32 use rustc_hir::def_id::{CrateNum, DefId};
33 use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
34 use rustc_middle::middle::privacy::EffectiveVisibilities;
35 use rustc_middle::middle::stability;
36 use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
37 use rustc_middle::ty::print::with_no_trimmed_paths;
38 use rustc_middle::ty::{self, print::Printer, subst::GenericArg, RegisteredTools, Ty, TyCtxt};
39 use rustc_session::config::ExpectedValues;
40 use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId};
41 use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
42 use rustc_session::Session;
43 use rustc_span::edit_distance::find_best_match_for_name;
44 use rustc_span::symbol::{sym, Ident, Symbol};
45 use rustc_span::{BytePos, Span};
46 use rustc_target::abi;
47
48 use std::cell::Cell;
49 use std::iter;
50 use std::slice;
51
52 type EarlyLintPassFactory = dyn Fn() -> EarlyLintPassObject + sync::DynSend + sync::DynSync;
53 type LateLintPassFactory =
54 dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync;
55
56 /// Information about the registered lints.
57 ///
58 /// This is basically the subset of `Context` that we can
59 /// build early in the compile pipeline.
60 pub struct LintStore {
61 /// Registered lints.
62 lints: Vec<&'static Lint>,
63
64 /// Constructor functions for each variety of lint pass.
65 ///
66 /// These should only be called once, but since we want to avoid locks or
67 /// interior mutability, we don't enforce this (and lints should, in theory,
68 /// be compatible with being constructed more than once, though not
69 /// necessarily in a sane manner. This is safe though.)
70 pub pre_expansion_passes: Vec<Box<EarlyLintPassFactory>>,
71 pub early_passes: Vec<Box<EarlyLintPassFactory>>,
72 pub late_passes: Vec<Box<LateLintPassFactory>>,
73 /// This is unique in that we construct them per-module, so not once.
74 pub late_module_passes: Vec<Box<LateLintPassFactory>>,
75
76 /// Lints indexed by name.
77 by_name: FxHashMap<String, TargetLint>,
78
79 /// Map of registered lint groups to what lints they expand to.
80 lint_groups: FxHashMap<&'static str, LintGroup>,
81 }
82
83 /// The target of the `by_name` map, which accounts for renaming/deprecation.
84 #[derive(Debug)]
85 enum TargetLint {
86 /// A direct lint target
87 Id(LintId),
88
89 /// Temporary renaming, used for easing migration pain; see #16545
90 Renamed(String, LintId),
91
92 /// Lint with this name existed previously, but has been removed/deprecated.
93 /// The string argument is the reason for removal.
94 Removed(String),
95
96 /// A lint name that should give no warnings and have no effect.
97 ///
98 /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers them as tool lints.
99 Ignored,
100 }
101
102 pub enum FindLintError {
103 NotFound,
104 Removed,
105 }
106
107 struct LintAlias {
108 name: &'static str,
109 /// Whether deprecation warnings should be suppressed for this alias.
110 silent: bool,
111 }
112
113 struct LintGroup {
114 lint_ids: Vec<LintId>,
115 from_plugin: bool,
116 depr: Option<LintAlias>,
117 }
118
119 #[derive(Debug)]
120 pub enum CheckLintNameResult<'a> {
121 Ok(&'a [LintId]),
122 /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
123 NoLint(Option<Symbol>),
124 /// The lint refers to a tool that has not been registered.
125 NoTool,
126 /// The lint is either renamed or removed. This is the warning
127 /// message, and an optional new name (`None` if removed).
128 Warning(String, Option<String>),
129 /// The lint is from a tool. If the Option is None, then either
130 /// the lint does not exist in the tool or the code was not
131 /// compiled with the tool and therefore the lint was never
132 /// added to the `LintStore`. Otherwise the `LintId` will be
133 /// returned as if it where a rustc lint.
134 Tool(Result<&'a [LintId], (Option<&'a [LintId]>, String)>),
135 }
136
137 impl LintStore {
new() -> LintStore138 pub fn new() -> LintStore {
139 LintStore {
140 lints: vec![],
141 pre_expansion_passes: vec![],
142 early_passes: vec![],
143 late_passes: vec![],
144 late_module_passes: vec![],
145 by_name: Default::default(),
146 lint_groups: Default::default(),
147 }
148 }
149
get_lints<'t>(&'t self) -> &'t [&'static Lint]150 pub fn get_lints<'t>(&'t self) -> &'t [&'static Lint] {
151 &self.lints
152 }
153
get_lint_groups<'t>( &'t self, ) -> impl Iterator<Item = (&'static str, Vec<LintId>, bool)> + 't154 pub fn get_lint_groups<'t>(
155 &'t self,
156 ) -> impl Iterator<Item = (&'static str, Vec<LintId>, bool)> + 't {
157 // This function is not used in a way which observes the order of lints.
158 #[allow(rustc::potential_query_instability)]
159 self.lint_groups
160 .iter()
161 .filter(|(_, LintGroup { depr, .. })| {
162 // Don't display deprecated lint groups.
163 depr.is_none()
164 })
165 .map(|(k, LintGroup { lint_ids, from_plugin, .. })| {
166 (*k, lint_ids.clone(), *from_plugin)
167 })
168 }
169
register_early_pass( &mut self, pass: impl Fn() -> EarlyLintPassObject + 'static + sync::DynSend + sync::DynSync, )170 pub fn register_early_pass(
171 &mut self,
172 pass: impl Fn() -> EarlyLintPassObject + 'static + sync::DynSend + sync::DynSync,
173 ) {
174 self.early_passes.push(Box::new(pass));
175 }
176
177 /// This lint pass is softly deprecated. It misses expanded code and has caused a few
178 /// errors in the past. Currently, it is only used in Clippy. New implementations
179 /// should avoid using this interface, as it might be removed in the future.
180 ///
181 /// * See [rust#69838](https://github.com/rust-lang/rust/pull/69838)
182 /// * See [rust-clippy#5518](https://github.com/rust-lang/rust-clippy/pull/5518)
register_pre_expansion_pass( &mut self, pass: impl Fn() -> EarlyLintPassObject + 'static + sync::DynSend + sync::DynSync, )183 pub fn register_pre_expansion_pass(
184 &mut self,
185 pass: impl Fn() -> EarlyLintPassObject + 'static + sync::DynSend + sync::DynSync,
186 ) {
187 self.pre_expansion_passes.push(Box::new(pass));
188 }
189
register_late_pass( &mut self, pass: impl for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + 'static + sync::DynSend + sync::DynSync, )190 pub fn register_late_pass(
191 &mut self,
192 pass: impl for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx>
193 + 'static
194 + sync::DynSend
195 + sync::DynSync,
196 ) {
197 self.late_passes.push(Box::new(pass));
198 }
199
register_late_mod_pass( &mut self, pass: impl for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + 'static + sync::DynSend + sync::DynSync, )200 pub fn register_late_mod_pass(
201 &mut self,
202 pass: impl for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx>
203 + 'static
204 + sync::DynSend
205 + sync::DynSync,
206 ) {
207 self.late_module_passes.push(Box::new(pass));
208 }
209
210 /// Helper method for register_early/late_pass
register_lints(&mut self, lints: &[&'static Lint])211 pub fn register_lints(&mut self, lints: &[&'static Lint]) {
212 for lint in lints {
213 self.lints.push(lint);
214
215 let id = LintId::of(lint);
216 if self.by_name.insert(lint.name_lower(), Id(id)).is_some() {
217 bug!("duplicate specification of lint {}", lint.name_lower())
218 }
219
220 if let Some(FutureIncompatibleInfo { reason, .. }) = lint.future_incompatible {
221 if let Some(edition) = reason.edition() {
222 self.lint_groups
223 .entry(edition.lint_name())
224 .or_insert(LintGroup {
225 lint_ids: vec![],
226 from_plugin: lint.is_plugin,
227 depr: None,
228 })
229 .lint_ids
230 .push(id);
231 } else {
232 // Lints belonging to the `future_incompatible` lint group are lints where a
233 // future version of rustc will cause existing code to stop compiling.
234 // Lints tied to an edition don't count because they are opt-in.
235 self.lint_groups
236 .entry("future_incompatible")
237 .or_insert(LintGroup {
238 lint_ids: vec![],
239 from_plugin: lint.is_plugin,
240 depr: None,
241 })
242 .lint_ids
243 .push(id);
244 }
245 }
246 }
247 }
248
register_group_alias(&mut self, lint_name: &'static str, alias: &'static str)249 pub fn register_group_alias(&mut self, lint_name: &'static str, alias: &'static str) {
250 self.lint_groups.insert(
251 alias,
252 LintGroup {
253 lint_ids: vec![],
254 from_plugin: false,
255 depr: Some(LintAlias { name: lint_name, silent: true }),
256 },
257 );
258 }
259
register_group( &mut self, from_plugin: bool, name: &'static str, deprecated_name: Option<&'static str>, to: Vec<LintId>, )260 pub fn register_group(
261 &mut self,
262 from_plugin: bool,
263 name: &'static str,
264 deprecated_name: Option<&'static str>,
265 to: Vec<LintId>,
266 ) {
267 let new = self
268 .lint_groups
269 .insert(name, LintGroup { lint_ids: to, from_plugin, depr: None })
270 .is_none();
271 if let Some(deprecated) = deprecated_name {
272 self.lint_groups.insert(
273 deprecated,
274 LintGroup {
275 lint_ids: vec![],
276 from_plugin,
277 depr: Some(LintAlias { name, silent: false }),
278 },
279 );
280 }
281
282 if !new {
283 bug!("duplicate specification of lint group {}", name);
284 }
285 }
286
287 /// This lint should give no warning and have no effect.
288 ///
289 /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers them as tool lints.
290 #[track_caller]
register_ignored(&mut self, name: &str)291 pub fn register_ignored(&mut self, name: &str) {
292 if self.by_name.insert(name.to_string(), Ignored).is_some() {
293 bug!("duplicate specification of lint {}", name);
294 }
295 }
296
297 /// This lint has been renamed; warn about using the new name and apply the lint.
298 #[track_caller]
register_renamed(&mut self, old_name: &str, new_name: &str)299 pub fn register_renamed(&mut self, old_name: &str, new_name: &str) {
300 let Some(&Id(target)) = self.by_name.get(new_name) else {
301 bug!("invalid lint renaming of {} to {}", old_name, new_name);
302 };
303 self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target));
304 }
305
register_removed(&mut self, name: &str, reason: &str)306 pub fn register_removed(&mut self, name: &str, reason: &str) {
307 self.by_name.insert(name.into(), Removed(reason.into()));
308 }
309
find_lints(&self, mut lint_name: &str) -> Result<Vec<LintId>, FindLintError>310 pub fn find_lints(&self, mut lint_name: &str) -> Result<Vec<LintId>, FindLintError> {
311 match self.by_name.get(lint_name) {
312 Some(&Id(lint_id)) => Ok(vec![lint_id]),
313 Some(&Renamed(_, lint_id)) => Ok(vec![lint_id]),
314 Some(&Removed(_)) => Err(FindLintError::Removed),
315 Some(&Ignored) => Ok(vec![]),
316 None => loop {
317 return match self.lint_groups.get(lint_name) {
318 Some(LintGroup { lint_ids, depr, .. }) => {
319 if let Some(LintAlias { name, .. }) = depr {
320 lint_name = name;
321 continue;
322 }
323 Ok(lint_ids.clone())
324 }
325 None => Err(FindLintError::Removed),
326 };
327 },
328 }
329 }
330
331 /// Checks the validity of lint names derived from the command line.
check_lint_name_cmdline( &self, sess: &Session, lint_name: &str, level: Level, registered_tools: &RegisteredTools, )332 pub fn check_lint_name_cmdline(
333 &self,
334 sess: &Session,
335 lint_name: &str,
336 level: Level,
337 registered_tools: &RegisteredTools,
338 ) {
339 let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
340 if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn(_)) {
341 sess.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
342 return;
343 }
344 let lint_name = lint_name.to_string();
345 match self.check_lint_name(lint_name_only, tool_name, registered_tools) {
346 CheckLintNameResult::Warning(msg, _) => {
347 sess.emit_warning(CheckNameWarning {
348 msg,
349 sub: RequestedLevel { level, lint_name },
350 });
351 }
352 CheckLintNameResult::NoLint(suggestion) => {
353 sess.emit_err(CheckNameUnknown {
354 lint_name: lint_name.clone(),
355 suggestion,
356 sub: RequestedLevel { level, lint_name },
357 });
358 }
359 CheckLintNameResult::Tool(Err((Some(_), new_name))) => {
360 sess.emit_warning(CheckNameDeprecated {
361 lint_name: lint_name.clone(),
362 new_name,
363 sub: RequestedLevel { level, lint_name },
364 });
365 }
366 CheckLintNameResult::NoTool => {
367 sess.emit_err(CheckNameUnknownTool {
368 tool_name: tool_name.unwrap(),
369 sub: RequestedLevel { level, lint_name },
370 });
371 }
372 _ => {}
373 };
374 }
375
376 /// True if this symbol represents a lint group name.
is_lint_group(&self, lint_name: Symbol) -> bool377 pub fn is_lint_group(&self, lint_name: Symbol) -> bool {
378 debug!(
379 "is_lint_group(lint_name={:?}, lint_groups={:?})",
380 lint_name,
381 self.lint_groups.keys().collect::<Vec<_>>()
382 );
383 let lint_name_str = lint_name.as_str();
384 self.lint_groups.contains_key(lint_name_str) || {
385 let warnings_name_str = crate::WARNINGS.name_lower();
386 lint_name_str == warnings_name_str
387 }
388 }
389
390 /// Checks the name of a lint for its existence, and whether it was
391 /// renamed or removed. Generates a DiagnosticBuilder containing a
392 /// warning for renamed and removed lints. This is over both lint
393 /// names from attributes and those passed on the command line. Since
394 /// it emits non-fatal warnings and there are *two* lint passes that
395 /// inspect attributes, this is only run from the late pass to avoid
396 /// printing duplicate warnings.
check_lint_name( &self, lint_name: &str, tool_name: Option<Symbol>, registered_tools: &RegisteredTools, ) -> CheckLintNameResult<'_>397 pub fn check_lint_name(
398 &self,
399 lint_name: &str,
400 tool_name: Option<Symbol>,
401 registered_tools: &RegisteredTools,
402 ) -> CheckLintNameResult<'_> {
403 if let Some(tool_name) = tool_name {
404 // FIXME: rustc and rustdoc are considered tools for lints, but not for attributes.
405 if tool_name != sym::rustc
406 && tool_name != sym::rustdoc
407 && !registered_tools.contains(&Ident::with_dummy_span(tool_name))
408 {
409 return CheckLintNameResult::NoTool;
410 }
411 }
412
413 let complete_name = if let Some(tool_name) = tool_name {
414 format!("{}::{}", tool_name, lint_name)
415 } else {
416 lint_name.to_string()
417 };
418 // If the lint was scoped with `tool::` check if the tool lint exists
419 if let Some(tool_name) = tool_name {
420 match self.by_name.get(&complete_name) {
421 None => match self.lint_groups.get(&*complete_name) {
422 // If the lint isn't registered, there are two possibilities:
423 None => {
424 // 1. The tool is currently running, so this lint really doesn't exist.
425 // FIXME: should this handle tools that never register a lint, like rustfmt?
426 debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>());
427 let tool_prefix = format!("{}::", tool_name);
428 return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) {
429 self.no_lint_suggestion(&complete_name)
430 } else {
431 // 2. The tool isn't currently running, so no lints will be registered.
432 // To avoid giving a false positive, ignore all unknown lints.
433 CheckLintNameResult::Tool(Err((None, String::new())))
434 };
435 }
436 Some(LintGroup { lint_ids, .. }) => {
437 return CheckLintNameResult::Tool(Ok(&lint_ids));
438 }
439 },
440 Some(Id(id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))),
441 // If the lint was registered as removed or renamed by the lint tool, we don't need
442 // to treat tool_lints and rustc lints different and can use the code below.
443 _ => {}
444 }
445 }
446 match self.by_name.get(&complete_name) {
447 Some(Renamed(new_name, _)) => CheckLintNameResult::Warning(
448 format!("lint `{}` has been renamed to `{}`", complete_name, new_name),
449 Some(new_name.to_owned()),
450 ),
451 Some(Removed(reason)) => CheckLintNameResult::Warning(
452 format!("lint `{}` has been removed: {}", complete_name, reason),
453 None,
454 ),
455 None => match self.lint_groups.get(&*complete_name) {
456 // If neither the lint, nor the lint group exists check if there is a `clippy::`
457 // variant of this lint
458 None => self.check_tool_name_for_backwards_compat(&complete_name, "clippy"),
459 Some(LintGroup { lint_ids, depr, .. }) => {
460 // Check if the lint group name is deprecated
461 if let Some(LintAlias { name, silent }) = depr {
462 let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap();
463 return if *silent {
464 CheckLintNameResult::Ok(&lint_ids)
465 } else {
466 CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string())))
467 };
468 }
469 CheckLintNameResult::Ok(&lint_ids)
470 }
471 },
472 Some(Id(id)) => CheckLintNameResult::Ok(slice::from_ref(id)),
473 Some(&Ignored) => CheckLintNameResult::Ok(&[]),
474 }
475 }
476
no_lint_suggestion(&self, lint_name: &str) -> CheckLintNameResult<'_>477 fn no_lint_suggestion(&self, lint_name: &str) -> CheckLintNameResult<'_> {
478 let name_lower = lint_name.to_lowercase();
479
480 if lint_name.chars().any(char::is_uppercase) && self.find_lints(&name_lower).is_ok() {
481 // First check if the lint name is (partly) in upper case instead of lower case...
482 return CheckLintNameResult::NoLint(Some(Symbol::intern(&name_lower)));
483 }
484 // ...if not, search for lints with a similar name
485 // Note: find_best_match_for_name depends on the sort order of its input vector.
486 // To ensure deterministic output, sort elements of the lint_groups hash map.
487 // Also, never suggest deprecated lint groups.
488 let mut groups: Vec<_> = self
489 .lint_groups
490 .iter()
491 .filter_map(|(k, LintGroup { depr, .. })| depr.is_none().then_some(k))
492 .collect();
493 groups.sort();
494 let groups = groups.iter().map(|k| Symbol::intern(k));
495 let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower()));
496 let names: Vec<Symbol> = groups.chain(lints).collect();
497 let suggestion = find_best_match_for_name(&names, Symbol::intern(&name_lower), None);
498 CheckLintNameResult::NoLint(suggestion)
499 }
500
check_tool_name_for_backwards_compat( &self, lint_name: &str, tool_name: &str, ) -> CheckLintNameResult<'_>501 fn check_tool_name_for_backwards_compat(
502 &self,
503 lint_name: &str,
504 tool_name: &str,
505 ) -> CheckLintNameResult<'_> {
506 let complete_name = format!("{}::{}", tool_name, lint_name);
507 match self.by_name.get(&complete_name) {
508 None => match self.lint_groups.get(&*complete_name) {
509 // Now we are sure, that this lint exists nowhere
510 None => self.no_lint_suggestion(lint_name),
511 Some(LintGroup { lint_ids, depr, .. }) => {
512 // Reaching this would be weird, but let's cover this case anyway
513 if let Some(LintAlias { name, silent }) = depr {
514 let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap();
515 return if *silent {
516 CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name)))
517 } else {
518 CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string())))
519 };
520 }
521 CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name)))
522 }
523 },
524 Some(Id(id)) => {
525 CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name)))
526 }
527 Some(other) => {
528 debug!("got renamed lint {:?}", other);
529 CheckLintNameResult::NoLint(None)
530 }
531 }
532 }
533 }
534
535 /// Context for lint checking outside of type inference.
536 pub struct LateContext<'tcx> {
537 /// Type context we're checking in.
538 pub tcx: TyCtxt<'tcx>,
539
540 /// Current body, or `None` if outside a body.
541 pub enclosing_body: Option<hir::BodyId>,
542
543 /// Type-checking results for the current body. Access using the `typeck_results`
544 /// and `maybe_typeck_results` methods, which handle querying the typeck results on demand.
545 // FIXME(eddyb) move all the code accessing internal fields like this,
546 // to this module, to avoid exposing it to lint logic.
547 pub(super) cached_typeck_results: Cell<Option<&'tcx ty::TypeckResults<'tcx>>>,
548
549 /// Parameter environment for the item we are in.
550 pub param_env: ty::ParamEnv<'tcx>,
551
552 /// Items accessible from the crate being checked.
553 pub effective_visibilities: &'tcx EffectiveVisibilities,
554
555 /// The store of registered lints and the lint levels.
556 pub lint_store: &'tcx LintStore,
557
558 pub last_node_with_lint_attrs: hir::HirId,
559
560 /// Generic type parameters in scope for the item we are in.
561 pub generics: Option<&'tcx hir::Generics<'tcx>>,
562
563 /// We are only looking at one module
564 pub only_module: bool,
565 }
566
567 /// Context for lint checking of the AST, after expansion, before lowering to HIR.
568 pub struct EarlyContext<'a> {
569 pub builder: LintLevelsBuilder<'a, crate::levels::TopDown>,
570 pub buffered: LintBuffer,
571 }
572
573 pub trait LintPassObject: Sized {}
574
575 impl LintPassObject for EarlyLintPassObject {}
576
577 impl LintPassObject for LateLintPassObject<'_> {}
578
579 pub trait LintContext: Sized {
580 type PassObject: LintPassObject;
581
sess(&self) -> &Session582 fn sess(&self) -> &Session;
lints(&self) -> &LintStore583 fn lints(&self) -> &LintStore;
584
585 /// Emit a lint at the appropriate level, with an optional associated span and an existing diagnostic.
586 ///
587 /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
588 ///
589 /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
590 #[rustc_lint_diagnostics]
lookup_with_diagnostics( &self, lint: &'static Lint, span: Option<impl Into<MultiSpan>>, msg: impl Into<DiagnosticMessage>, decorate: impl for<'a, 'b> FnOnce( &'b mut DiagnosticBuilder<'a, ()>, ) -> &'b mut DiagnosticBuilder<'a, ()>, diagnostic: BuiltinLintDiagnostics, )591 fn lookup_with_diagnostics(
592 &self,
593 lint: &'static Lint,
594 span: Option<impl Into<MultiSpan>>,
595 msg: impl Into<DiagnosticMessage>,
596 decorate: impl for<'a, 'b> FnOnce(
597 &'b mut DiagnosticBuilder<'a, ()>,
598 ) -> &'b mut DiagnosticBuilder<'a, ()>,
599 diagnostic: BuiltinLintDiagnostics,
600 ) {
601 // We first generate a blank diagnostic.
602 self.lookup(lint, span, msg,|db| {
603 // Now, set up surrounding context.
604 let sess = self.sess();
605 match diagnostic {
606 BuiltinLintDiagnostics::UnicodeTextFlow(span, content) => {
607 let spans: Vec<_> = content
608 .char_indices()
609 .filter_map(|(i, c)| {
610 TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| {
611 let lo = span.lo() + BytePos(2 + i as u32);
612 (c, span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32)))
613 })
614 })
615 .collect();
616 let (an, s) = match spans.len() {
617 1 => ("an ", ""),
618 _ => ("", "s"),
619 };
620 db.span_label(span, format!(
621 "this comment contains {}invisible unicode text flow control codepoint{}",
622 an,
623 s,
624 ));
625 for (c, span) in &spans {
626 db.span_label(*span, format!("{:?}", c));
627 }
628 db.note(
629 "these kind of unicode codepoints change the way text flows on \
630 applications that support them, but can cause confusion because they \
631 change the order of characters on the screen",
632 );
633 if !spans.is_empty() {
634 db.multipart_suggestion_with_style(
635 "if their presence wasn't intentional, you can remove them",
636 spans.into_iter().map(|(_, span)| (span, "".to_string())).collect(),
637 Applicability::MachineApplicable,
638 SuggestionStyle::HideCodeAlways,
639 );
640 }
641 },
642 BuiltinLintDiagnostics::Normal => (),
643 BuiltinLintDiagnostics::AbsPathWithModule(span) => {
644 let (sugg, app) = match sess.source_map().span_to_snippet(span) {
645 Ok(ref s) => {
646 // FIXME(Manishearth) ideally the emitting code
647 // can tell us whether or not this is global
648 let opt_colon =
649 if s.trim_start().starts_with("::") { "" } else { "::" };
650
651 (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable)
652 }
653 Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
654 };
655 db.span_suggestion(span, "use `crate`", sugg, app);
656 }
657 BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => {
658 db.span_label(
659 span,
660 "names from parent modules are not accessible without an explicit import",
661 );
662 }
663 BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(
664 span_def,
665 ) => {
666 db.span_note(span_def, "the macro is defined here");
667 }
668 BuiltinLintDiagnostics::ElidedLifetimesInPaths(
669 n,
670 path_span,
671 incl_angl_brckt,
672 insertion_span,
673 ) => {
674 add_elided_lifetime_in_path_suggestion(
675 sess.source_map(),
676 db,
677 n,
678 path_span,
679 incl_angl_brckt,
680 insertion_span,
681 );
682 }
683 BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
684 db.span_suggestion(span, note, sugg, Applicability::MaybeIncorrect);
685 }
686 BuiltinLintDiagnostics::UnusedImports(message, replaces, in_test_module) => {
687 if !replaces.is_empty() {
688 db.tool_only_multipart_suggestion(
689 message,
690 replaces,
691 Applicability::MachineApplicable,
692 );
693 }
694
695 if let Some(span) = in_test_module {
696 db.span_help(
697 self.sess().source_map().guess_head_span(span),
698 "consider adding a `#[cfg(test)]` to the containing module",
699 );
700 }
701 }
702 BuiltinLintDiagnostics::RedundantImport(spans, ident) => {
703 for (span, is_imported) in spans {
704 let introduced = if is_imported { "imported" } else { "defined" };
705 db.span_label(
706 span,
707 format!("the item `{}` is already {} here", ident, introduced),
708 );
709 }
710 }
711 BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => {
712 stability::deprecation_suggestion(db, "macro", suggestion, span)
713 }
714 BuiltinLintDiagnostics::UnusedDocComment(span) => {
715 db.span_label(span, "rustdoc does not generate documentation for macro invocations");
716 db.help("to document an item produced by a macro, \
717 the macro must produce the documentation as part of its expansion");
718 }
719 BuiltinLintDiagnostics::PatternsInFnsWithoutBody(span, ident) => {
720 db.span_suggestion(span, "remove `mut` from the parameter", ident, Applicability::MachineApplicable);
721 }
722 BuiltinLintDiagnostics::MissingAbi(span, default_abi) => {
723 db.span_label(span, "ABI should be specified here");
724 db.help(format!("the default ABI is {}", default_abi.name()));
725 }
726 BuiltinLintDiagnostics::LegacyDeriveHelpers(span) => {
727 db.span_label(span, "the attribute is introduced here");
728 }
729 BuiltinLintDiagnostics::ProcMacroBackCompat(note) => {
730 db.note(note);
731 }
732 BuiltinLintDiagnostics::OrPatternsBackCompat(span,suggestion) => {
733 db.span_suggestion(span, "use pat_param to preserve semantics", suggestion, Applicability::MachineApplicable);
734 }
735 BuiltinLintDiagnostics::ReservedPrefix(span) => {
736 db.span_label(span, "unknown prefix");
737 db.span_suggestion_verbose(
738 span.shrink_to_hi(),
739 "insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
740 " ",
741 Applicability::MachineApplicable,
742 );
743 }
744 BuiltinLintDiagnostics::UnusedBuiltinAttribute {
745 attr_name,
746 macro_name,
747 invoc_span
748 } => {
749 db.span_note(
750 invoc_span,
751 format!("the built-in attribute `{attr_name}` will be ignored, since it's applied to the macro invocation `{macro_name}`")
752 );
753 }
754 BuiltinLintDiagnostics::TrailingMacro(is_trailing, name) => {
755 if is_trailing {
756 db.note("macro invocations at the end of a block are treated as expressions");
757 db.note(format!("to ignore the value produced by the macro, add a semicolon after the invocation of `{name}`"));
758 }
759 }
760 BuiltinLintDiagnostics::BreakWithLabelAndLoop(span) => {
761 db.multipart_suggestion(
762 "wrap this expression in parentheses",
763 vec![(span.shrink_to_lo(), "(".to_string()),
764 (span.shrink_to_hi(), ")".to_string())],
765 Applicability::MachineApplicable
766 );
767 }
768 BuiltinLintDiagnostics::NamedAsmLabel(help) => {
769 db.help(help);
770 db.note("see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information");
771 },
772 BuiltinLintDiagnostics::UnexpectedCfgName((name, name_span), value) => {
773 let possibilities: Vec<Symbol> = sess.parse_sess.check_config.expecteds.keys().map(|s| *s).collect();
774
775 // Suggest the most probable if we found one
776 if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) {
777 if let Some(ExpectedValues::Some(best_match_values)) =
778 sess.parse_sess.check_config.expecteds.get(&best_match) {
779 let mut possibilities = best_match_values.iter()
780 .flatten()
781 .map(Symbol::as_str)
782 .collect::<Vec<_>>();
783 possibilities.sort();
784
785 if let Some((value, value_span)) = value {
786 if best_match_values.contains(&Some(value)) {
787 db.span_suggestion(name_span, "there is a config with a similar name and value", best_match, Applicability::MaybeIncorrect);
788 } else if best_match_values.contains(&None) {
789 db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and no value", best_match, Applicability::MaybeIncorrect);
790 } else if let Some(first_value) = possibilities.first() {
791 db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and different values", format!("{best_match} = \"{first_value}\""), Applicability::MaybeIncorrect);
792 } else {
793 db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and different values", best_match, Applicability::MaybeIncorrect);
794 };
795 } else {
796 db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect);
797 }
798
799 if !possibilities.is_empty() {
800 let possibilities = possibilities.join("`, `");
801 db.help(format!("expected values for `{best_match}` are: `{possibilities}`"));
802 }
803 } else {
804 db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect);
805 }
806 }
807 },
808 BuiltinLintDiagnostics::UnexpectedCfgValue((name, name_span), value) => {
809 let Some(ExpectedValues::Some(values)) = &sess.parse_sess.check_config.expecteds.get(&name) else {
810 bug!("it shouldn't be possible to have a diagnostic on a value whose name is not in values");
811 };
812 let mut have_none_possibility = false;
813 let possibilities: Vec<Symbol> = values.iter()
814 .inspect(|a| have_none_possibility |= a.is_none())
815 .copied()
816 .flatten()
817 .collect();
818
819 // Show the full list if all possible values for a given name, but don't do it
820 // for names as the possibilities could be very long
821 if !possibilities.is_empty() {
822 {
823 let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::<Vec<_>>();
824 possibilities.sort();
825
826 let possibilities = possibilities.join("`, `");
827 let none = if have_none_possibility { "(none), " } else { "" };
828
829 db.note(format!("expected values for `{name}` are: {none}`{possibilities}`"));
830 }
831
832 if let Some((value, value_span)) = value {
833 // Suggest the most probable if we found one
834 if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) {
835 db.span_suggestion(value_span, "there is a expected value with a similar name", format!("\"{best_match}\""), Applicability::MaybeIncorrect);
836
837 }
838 } else if let &[first_possibility] = &possibilities[..] {
839 db.span_suggestion(name_span.shrink_to_hi(), "specify a config value", format!(" = \"{first_possibility}\""), Applicability::MaybeIncorrect);
840 }
841 } else if have_none_possibility {
842 db.note(format!("no expected value for `{name}`"));
843 if let Some((_value, value_span)) = value {
844 db.span_suggestion(name_span.shrink_to_hi().to(value_span), "remove the value", "", Applicability::MaybeIncorrect);
845 }
846 }
847 },
848 BuiltinLintDiagnostics::DeprecatedWhereclauseLocation(new_span, suggestion) => {
849 db.multipart_suggestion(
850 "move it to the end of the type declaration",
851 vec![(db.span.primary_span().unwrap(), "".to_string()), (new_span, suggestion)],
852 Applicability::MachineApplicable,
853 );
854 db.note(
855 "see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information",
856 );
857 },
858 BuiltinLintDiagnostics::SingleUseLifetime {
859 param_span,
860 use_span: Some((use_span, elide)),
861 deletion_span,
862 } => {
863 debug!(?param_span, ?use_span, ?deletion_span);
864 db.span_label(param_span, "this lifetime...");
865 db.span_label(use_span, "...is used only here");
866 if let Some(deletion_span) = deletion_span {
867 let msg = "elide the single-use lifetime";
868 let (use_span, replace_lt) = if elide {
869 let use_span = sess.source_map().span_extend_while(
870 use_span,
871 char::is_whitespace,
872 ).unwrap_or(use_span);
873 (use_span, String::new())
874 } else {
875 (use_span, "'_".to_owned())
876 };
877 debug!(?deletion_span, ?use_span);
878
879 // issue 107998 for the case such as a wrong function pointer type
880 // `deletion_span` is empty and there is no need to report lifetime uses here
881 let suggestions = if deletion_span.is_empty() {
882 vec![(use_span, replace_lt)]
883 } else {
884 vec![(deletion_span, String::new()), (use_span, replace_lt)]
885 };
886 db.multipart_suggestion(
887 msg,
888 suggestions,
889 Applicability::MachineApplicable,
890 );
891 }
892 },
893 BuiltinLintDiagnostics::SingleUseLifetime {
894 param_span: _,
895 use_span: None,
896 deletion_span,
897 } => {
898 debug!(?deletion_span);
899 if let Some(deletion_span) = deletion_span {
900 db.span_suggestion(
901 deletion_span,
902 "elide the unused lifetime",
903 "",
904 Applicability::MachineApplicable,
905 );
906 }
907 },
908 BuiltinLintDiagnostics::NamedArgumentUsedPositionally{ position_sp_to_replace, position_sp_for_msg, named_arg_sp, named_arg_name, is_formatting_arg} => {
909 db.span_label(named_arg_sp, "this named argument is referred to by position in formatting string");
910 if let Some(positional_arg_for_msg) = position_sp_for_msg {
911 let msg = format!("this formatting argument uses named argument `{}` by position", named_arg_name);
912 db.span_label(positional_arg_for_msg, msg);
913 }
914
915 if let Some(positional_arg_to_replace) = position_sp_to_replace {
916 let name = if is_formatting_arg { named_arg_name + "$" } else { named_arg_name };
917 let span_to_replace = if let Ok(positional_arg_content) =
918 self.sess().source_map().span_to_snippet(positional_arg_to_replace) && positional_arg_content.starts_with(':') {
919 positional_arg_to_replace.shrink_to_lo()
920 } else {
921 positional_arg_to_replace
922 };
923 db.span_suggestion_verbose(
924 span_to_replace,
925 "use the named argument by name to avoid ambiguity",
926 name,
927 Applicability::MaybeIncorrect,
928 );
929 }
930 }
931 BuiltinLintDiagnostics::ByteSliceInPackedStructWithDerive => {
932 db.help("consider implementing the trait by hand, or remove the `packed` attribute");
933 }
934 BuiltinLintDiagnostics::UnusedExternCrate { removal_span }=> {
935 db.span_suggestion(
936 removal_span,
937 "remove it",
938 "",
939 Applicability::MachineApplicable,
940 );
941 }
942 BuiltinLintDiagnostics::ExternCrateNotIdiomatic { vis_span, ident_span }=> {
943 let suggestion_span = vis_span.between(ident_span);
944 db.span_suggestion_verbose(
945 suggestion_span,
946 "convert it to a `use`",
947 if vis_span.is_empty() { "use " } else { " use " },
948 Applicability::MachineApplicable,
949 );
950 }
951 BuiltinLintDiagnostics::AmbiguousGlobReexports { name, namespace, first_reexport_span, duplicate_reexport_span } => {
952 db.span_label(first_reexport_span, format!("the name `{}` in the {} namespace is first re-exported here", name, namespace));
953 db.span_label(duplicate_reexport_span, format!("but the name `{}` in the {} namespace is also re-exported here", name, namespace));
954 }
955 BuiltinLintDiagnostics::HiddenGlobReexports { name, namespace, glob_reexport_span, private_item_span } => {
956 db.span_note(glob_reexport_span, format!("the name `{}` in the {} namespace is supposed to be publicly re-exported here", name, namespace));
957 db.span_note(private_item_span, "but the private item here shadows it".to_owned());
958 }
959 }
960 // Rewrap `db`, and pass control to the user.
961 decorate(db)
962 });
963 }
964
965 // FIXME: These methods should not take an Into<MultiSpan> -- instead, callers should need to
966 // set the span in their `decorate` function (preferably using set_span).
967 /// Emit a lint at the appropriate level, with an optional associated span.
968 ///
969 /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
970 ///
971 /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
972 #[rustc_lint_diagnostics]
lookup<S: Into<MultiSpan>>( &self, lint: &'static Lint, span: Option<S>, msg: impl Into<DiagnosticMessage>, decorate: impl for<'a, 'b> FnOnce( &'b mut DiagnosticBuilder<'a, ()>, ) -> &'b mut DiagnosticBuilder<'a, ()>, )973 fn lookup<S: Into<MultiSpan>>(
974 &self,
975 lint: &'static Lint,
976 span: Option<S>,
977 msg: impl Into<DiagnosticMessage>,
978 decorate: impl for<'a, 'b> FnOnce(
979 &'b mut DiagnosticBuilder<'a, ()>,
980 ) -> &'b mut DiagnosticBuilder<'a, ()>,
981 );
982
983 /// Emit a lint at `span` from a lint struct (some type that implements `DecorateLint`,
984 /// typically generated by `#[derive(LintDiagnostic)]`).
emit_spanned_lint<S: Into<MultiSpan>>( &self, lint: &'static Lint, span: S, decorator: impl for<'a> DecorateLint<'a, ()>, )985 fn emit_spanned_lint<S: Into<MultiSpan>>(
986 &self,
987 lint: &'static Lint,
988 span: S,
989 decorator: impl for<'a> DecorateLint<'a, ()>,
990 ) {
991 self.lookup(lint, Some(span), decorator.msg(), |diag| decorator.decorate_lint(diag));
992 }
993
994 /// Emit a lint at the appropriate level, with an associated span.
995 ///
996 /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
997 ///
998 /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
999 #[rustc_lint_diagnostics]
struct_span_lint<S: Into<MultiSpan>>( &self, lint: &'static Lint, span: S, msg: impl Into<DiagnosticMessage>, decorate: impl for<'a, 'b> FnOnce( &'b mut DiagnosticBuilder<'a, ()>, ) -> &'b mut DiagnosticBuilder<'a, ()>, )1000 fn struct_span_lint<S: Into<MultiSpan>>(
1001 &self,
1002 lint: &'static Lint,
1003 span: S,
1004 msg: impl Into<DiagnosticMessage>,
1005 decorate: impl for<'a, 'b> FnOnce(
1006 &'b mut DiagnosticBuilder<'a, ()>,
1007 ) -> &'b mut DiagnosticBuilder<'a, ()>,
1008 ) {
1009 self.lookup(lint, Some(span), msg, decorate);
1010 }
1011
1012 /// Emit a lint from a lint struct (some type that implements `DecorateLint`, typically
1013 /// generated by `#[derive(LintDiagnostic)]`).
emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> DecorateLint<'a, ()>)1014 fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> DecorateLint<'a, ()>) {
1015 self.lookup(lint, None as Option<Span>, decorator.msg(), |diag| {
1016 decorator.decorate_lint(diag)
1017 });
1018 }
1019
1020 /// Emit a lint at the appropriate level, with no associated span.
1021 ///
1022 /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
1023 ///
1024 /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
1025 #[rustc_lint_diagnostics]
lint( &self, lint: &'static Lint, msg: impl Into<DiagnosticMessage>, decorate: impl for<'a, 'b> FnOnce( &'b mut DiagnosticBuilder<'a, ()>, ) -> &'b mut DiagnosticBuilder<'a, ()>, )1026 fn lint(
1027 &self,
1028 lint: &'static Lint,
1029 msg: impl Into<DiagnosticMessage>,
1030 decorate: impl for<'a, 'b> FnOnce(
1031 &'b mut DiagnosticBuilder<'a, ()>,
1032 ) -> &'b mut DiagnosticBuilder<'a, ()>,
1033 ) {
1034 self.lookup(lint, None as Option<Span>, msg, decorate);
1035 }
1036
1037 /// This returns the lint level for the given lint at the current location.
get_lint_level(&self, lint: &'static Lint) -> Level1038 fn get_lint_level(&self, lint: &'static Lint) -> Level;
1039
1040 /// This function can be used to manually fulfill an expectation. This can
1041 /// be used for lints which contain several spans, and should be suppressed,
1042 /// if either location was marked with an expectation.
1043 ///
1044 /// Note that this function should only be called for [`LintExpectationId`]s
1045 /// retrieved from the current lint pass. Buffered or manually created ids can
1046 /// cause ICEs.
1047 #[rustc_lint_diagnostics]
fulfill_expectation(&self, expectation: LintExpectationId)1048 fn fulfill_expectation(&self, expectation: LintExpectationId) {
1049 // We need to make sure that submitted expectation ids are correctly fulfilled suppressed
1050 // and stored between compilation sessions. To not manually do these steps, we simply create
1051 // a dummy diagnostic and emit is as usual, which will be suppressed and stored like a normal
1052 // expected lint diagnostic.
1053 self.sess()
1054 .struct_expect(
1055 "this is a dummy diagnostic, to submit and store an expectation",
1056 expectation,
1057 )
1058 .emit();
1059 }
1060 }
1061
1062 impl<'a> EarlyContext<'a> {
new( sess: &'a Session, warn_about_weird_lints: bool, lint_store: &'a LintStore, registered_tools: &'a RegisteredTools, buffered: LintBuffer, ) -> EarlyContext<'a>1063 pub(crate) fn new(
1064 sess: &'a Session,
1065 warn_about_weird_lints: bool,
1066 lint_store: &'a LintStore,
1067 registered_tools: &'a RegisteredTools,
1068 buffered: LintBuffer,
1069 ) -> EarlyContext<'a> {
1070 EarlyContext {
1071 builder: LintLevelsBuilder::new(
1072 sess,
1073 warn_about_weird_lints,
1074 lint_store,
1075 registered_tools,
1076 ),
1077 buffered,
1078 }
1079 }
1080 }
1081
1082 impl<'tcx> LintContext for LateContext<'tcx> {
1083 type PassObject = LateLintPassObject<'tcx>;
1084
1085 /// Gets the overall compiler `Session` object.
sess(&self) -> &Session1086 fn sess(&self) -> &Session {
1087 &self.tcx.sess
1088 }
1089
lints(&self) -> &LintStore1090 fn lints(&self) -> &LintStore {
1091 &*self.lint_store
1092 }
1093
1094 #[rustc_lint_diagnostics]
lookup<S: Into<MultiSpan>>( &self, lint: &'static Lint, span: Option<S>, msg: impl Into<DiagnosticMessage>, decorate: impl for<'a, 'b> FnOnce( &'b mut DiagnosticBuilder<'a, ()>, ) -> &'b mut DiagnosticBuilder<'a, ()>, )1095 fn lookup<S: Into<MultiSpan>>(
1096 &self,
1097 lint: &'static Lint,
1098 span: Option<S>,
1099 msg: impl Into<DiagnosticMessage>,
1100 decorate: impl for<'a, 'b> FnOnce(
1101 &'b mut DiagnosticBuilder<'a, ()>,
1102 ) -> &'b mut DiagnosticBuilder<'a, ()>,
1103 ) {
1104 let hir_id = self.last_node_with_lint_attrs;
1105
1106 match span {
1107 Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, msg, decorate),
1108 None => self.tcx.struct_lint_node(lint, hir_id, msg, decorate),
1109 }
1110 }
1111
get_lint_level(&self, lint: &'static Lint) -> Level1112 fn get_lint_level(&self, lint: &'static Lint) -> Level {
1113 self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs).0
1114 }
1115 }
1116
1117 impl LintContext for EarlyContext<'_> {
1118 type PassObject = EarlyLintPassObject;
1119
1120 /// Gets the overall compiler `Session` object.
sess(&self) -> &Session1121 fn sess(&self) -> &Session {
1122 &self.builder.sess()
1123 }
1124
lints(&self) -> &LintStore1125 fn lints(&self) -> &LintStore {
1126 self.builder.lint_store()
1127 }
1128
1129 #[rustc_lint_diagnostics]
lookup<S: Into<MultiSpan>>( &self, lint: &'static Lint, span: Option<S>, msg: impl Into<DiagnosticMessage>, decorate: impl for<'a, 'b> FnOnce( &'b mut DiagnosticBuilder<'a, ()>, ) -> &'b mut DiagnosticBuilder<'a, ()>, )1130 fn lookup<S: Into<MultiSpan>>(
1131 &self,
1132 lint: &'static Lint,
1133 span: Option<S>,
1134 msg: impl Into<DiagnosticMessage>,
1135 decorate: impl for<'a, 'b> FnOnce(
1136 &'b mut DiagnosticBuilder<'a, ()>,
1137 ) -> &'b mut DiagnosticBuilder<'a, ()>,
1138 ) {
1139 self.builder.struct_lint(lint, span.map(|s| s.into()), msg, decorate)
1140 }
1141
get_lint_level(&self, lint: &'static Lint) -> Level1142 fn get_lint_level(&self, lint: &'static Lint) -> Level {
1143 self.builder.lint_level(lint).0
1144 }
1145 }
1146
1147 impl<'tcx> LateContext<'tcx> {
1148 /// Gets the type-checking results for the current body,
1149 /// or `None` if outside a body.
maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>>1150 pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> {
1151 self.cached_typeck_results.get().or_else(|| {
1152 self.enclosing_body.map(|body| {
1153 let typeck_results = self.tcx.typeck_body(body);
1154 self.cached_typeck_results.set(Some(typeck_results));
1155 typeck_results
1156 })
1157 })
1158 }
1159
1160 /// Gets the type-checking results for the current body.
1161 /// As this will ICE if called outside bodies, only call when working with
1162 /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
1163 #[track_caller]
typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx>1164 pub fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> {
1165 self.maybe_typeck_results().expect("`LateContext::typeck_results` called outside of body")
1166 }
1167
1168 /// Returns the final resolution of a `QPath`, or `Res::Err` if unavailable.
1169 /// Unlike `.typeck_results().qpath_res(qpath, id)`, this can be used even outside
1170 /// bodies (e.g. for paths in `hir::Ty`), without any risk of ICE-ing.
qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res1171 pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
1172 match *qpath {
1173 hir::QPath::Resolved(_, ref path) => path.res,
1174 hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self
1175 .maybe_typeck_results()
1176 .filter(|typeck_results| typeck_results.hir_owner == id.owner)
1177 .or_else(|| {
1178 self.tcx
1179 .has_typeck_results(id.owner.to_def_id())
1180 .then(|| self.tcx.typeck(id.owner.def_id))
1181 })
1182 .and_then(|typeck_results| typeck_results.type_dependent_def(id))
1183 .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)),
1184 }
1185 }
1186
1187 /// Check if a `DefId`'s path matches the given absolute type path usage.
1188 ///
1189 /// Anonymous scopes such as `extern` imports are matched with `kw::Empty`;
1190 /// inherent `impl` blocks are matched with the name of the type.
1191 ///
1192 /// Instead of using this method, it is often preferable to instead use
1193 /// `rustc_diagnostic_item` or a `lang_item`. This is less prone to errors
1194 /// as paths get invalidated if the target definition moves.
1195 ///
1196 /// # Examples
1197 ///
1198 /// ```rust,ignore (no context or def id available)
1199 /// if cx.match_def_path(def_id, &[sym::core, sym::option, sym::Option]) {
1200 /// // The given `def_id` is that of an `Option` type
1201 /// }
1202 /// ```
1203 ///
1204 /// Used by clippy, but should be replaced by diagnostic items eventually.
match_def_path(&self, def_id: DefId, path: &[Symbol]) -> bool1205 pub fn match_def_path(&self, def_id: DefId, path: &[Symbol]) -> bool {
1206 let names = self.get_def_path(def_id);
1207
1208 names.len() == path.len() && iter::zip(names, path).all(|(a, &b)| a == b)
1209 }
1210
1211 /// Gets the absolute path of `def_id` as a vector of `Symbol`.
1212 ///
1213 /// # Examples
1214 ///
1215 /// ```rust,ignore (no context or def id available)
1216 /// let def_path = cx.get_def_path(def_id);
1217 /// if let &[sym::core, sym::option, sym::Option] = &def_path[..] {
1218 /// // The given `def_id` is that of an `Option` type
1219 /// }
1220 /// ```
get_def_path(&self, def_id: DefId) -> Vec<Symbol>1221 pub fn get_def_path(&self, def_id: DefId) -> Vec<Symbol> {
1222 pub struct AbsolutePathPrinter<'tcx> {
1223 pub tcx: TyCtxt<'tcx>,
1224 }
1225
1226 impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
1227 type Error = !;
1228
1229 type Path = Vec<Symbol>;
1230 type Region = ();
1231 type Type = ();
1232 type DynExistential = ();
1233 type Const = ();
1234
1235 fn tcx(&self) -> TyCtxt<'tcx> {
1236 self.tcx
1237 }
1238
1239 fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> {
1240 Ok(())
1241 }
1242
1243 fn print_type(self, _ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
1244 Ok(())
1245 }
1246
1247 fn print_dyn_existential(
1248 self,
1249 _predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
1250 ) -> Result<Self::DynExistential, Self::Error> {
1251 Ok(())
1252 }
1253
1254 fn print_const(self, _ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
1255 Ok(())
1256 }
1257
1258 fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
1259 Ok(vec![self.tcx.crate_name(cnum)])
1260 }
1261
1262 fn path_qualified(
1263 self,
1264 self_ty: Ty<'tcx>,
1265 trait_ref: Option<ty::TraitRef<'tcx>>,
1266 ) -> Result<Self::Path, Self::Error> {
1267 if trait_ref.is_none() {
1268 if let ty::Adt(def, substs) = self_ty.kind() {
1269 return self.print_def_path(def.did(), substs);
1270 }
1271 }
1272
1273 // This shouldn't ever be needed, but just in case:
1274 with_no_trimmed_paths!({
1275 Ok(vec![match trait_ref {
1276 Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)),
1277 None => Symbol::intern(&format!("<{}>", self_ty)),
1278 }])
1279 })
1280 }
1281
1282 fn path_append_impl(
1283 self,
1284 print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
1285 _disambiguated_data: &DisambiguatedDefPathData,
1286 self_ty: Ty<'tcx>,
1287 trait_ref: Option<ty::TraitRef<'tcx>>,
1288 ) -> Result<Self::Path, Self::Error> {
1289 let mut path = print_prefix(self)?;
1290
1291 // This shouldn't ever be needed, but just in case:
1292 path.push(match trait_ref {
1293 Some(trait_ref) => {
1294 with_no_trimmed_paths!(Symbol::intern(&format!(
1295 "<impl {} for {}>",
1296 trait_ref.print_only_trait_path(),
1297 self_ty
1298 )))
1299 }
1300 None => {
1301 with_no_trimmed_paths!(Symbol::intern(&format!("<impl {}>", self_ty)))
1302 }
1303 });
1304
1305 Ok(path)
1306 }
1307
1308 fn path_append(
1309 self,
1310 print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
1311 disambiguated_data: &DisambiguatedDefPathData,
1312 ) -> Result<Self::Path, Self::Error> {
1313 let mut path = print_prefix(self)?;
1314
1315 // Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs.
1316 if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data {
1317 return Ok(path);
1318 }
1319
1320 path.push(Symbol::intern(&disambiguated_data.data.to_string()));
1321 Ok(path)
1322 }
1323
1324 fn path_generic_args(
1325 self,
1326 print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
1327 _args: &[GenericArg<'tcx>],
1328 ) -> Result<Self::Path, Self::Error> {
1329 print_prefix(self)
1330 }
1331 }
1332
1333 AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]).unwrap()
1334 }
1335
1336 /// Returns the associated type `name` for `self_ty` as an implementation of `trait_id`.
1337 /// Do not invoke without first verifying that the type implements the trait.
get_associated_type( &self, self_ty: Ty<'tcx>, trait_id: DefId, name: &str, ) -> Option<Ty<'tcx>>1338 pub fn get_associated_type(
1339 &self,
1340 self_ty: Ty<'tcx>,
1341 trait_id: DefId,
1342 name: &str,
1343 ) -> Option<Ty<'tcx>> {
1344 let tcx = self.tcx;
1345 tcx.associated_items(trait_id)
1346 .find_by_name_and_kind(tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id)
1347 .and_then(|assoc| {
1348 let proj = Ty::new_projection(tcx, assoc.def_id, [self_ty]);
1349 tcx.try_normalize_erasing_regions(self.param_env, proj).ok()
1350 })
1351 }
1352 }
1353
1354 impl<'tcx> abi::HasDataLayout for LateContext<'tcx> {
1355 #[inline]
data_layout(&self) -> &abi::TargetDataLayout1356 fn data_layout(&self) -> &abi::TargetDataLayout {
1357 &self.tcx.data_layout
1358 }
1359 }
1360
1361 impl<'tcx> ty::layout::HasTyCtxt<'tcx> for LateContext<'tcx> {
1362 #[inline]
tcx(&self) -> TyCtxt<'tcx>1363 fn tcx(&self) -> TyCtxt<'tcx> {
1364 self.tcx
1365 }
1366 }
1367
1368 impl<'tcx> ty::layout::HasParamEnv<'tcx> for LateContext<'tcx> {
1369 #[inline]
param_env(&self) -> ty::ParamEnv<'tcx>1370 fn param_env(&self) -> ty::ParamEnv<'tcx> {
1371 self.param_env
1372 }
1373 }
1374
1375 impl<'tcx> LayoutOfHelpers<'tcx> for LateContext<'tcx> {
1376 type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
1377
1378 #[inline]
handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx>1379 fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx> {
1380 err
1381 }
1382 }
1383
parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str)1384 pub fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
1385 match lint_name.split_once("::") {
1386 Some((tool_name, lint_name)) => {
1387 let tool_name = Symbol::intern(tool_name);
1388
1389 (Some(tool_name), lint_name)
1390 }
1391 None => (None, lint_name),
1392 }
1393 }
1394