1use rustc_ast::attr::AttributeExt;
2use rustc_ast_pretty::pprust;
3use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
4use rustc_data_structures::unord::UnordSet;
5use rustc_errors::{Diag, LintDiagnostic, MultiSpan, inline_fluent};
6use rustc_feature::{Features, GateIssue};
7use rustc_hir::HirId;
8use rustc_hir::intravisit::{self, Visitor};
9use rustc_index::IndexVec;
10use rustc_middle::bug;
11use rustc_middle::hir::nested_filter;
12use rustc_middle::lint::{
13 LevelAndSource, LintExpectation, LintLevelSource, ShallowLintLevelMap, lint_level,
14 reveal_actual_level,
15};
16use rustc_middle::query::Providers;
17use rustc_middle::ty::{RegisteredTools, TyCtxt};
18use rustc_session::Session;
19use rustc_session::lint::builtin::{
20 self, FORBIDDEN_LINT_GROUPS, RENAMED_AND_REMOVED_LINTS, SINGLE_USE_LIFETIMES,
21 UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES,
22};
23use rustc_session::lint::{Level, Lint, LintExpectationId, LintId};
24use rustc_span::{DUMMY_SP, Span, Symbol, sym};
25use tracing::{debug, instrument};
26use {rustc_ast as ast, rustc_hir as hir};
27
28use crate::builtin::MISSING_DOCS;
29use crate::context::{CheckLintNameResult, LintStore};
30use crate::errors::{
31 CheckNameUnknownTool, MalformedAttribute, MalformedAttributeSub, OverruledAttribute,
32 OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup,
33};
34use crate::late::unerased_lint_store;
35use crate::lints::{
36 DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified,
37 OverruledAttributeLint, RemovedLint, RemovedLintFromCommandLine, RenamedLint,
38 RenamedLintFromCommandLine, RenamedLintSuggestion, UnknownLint, UnknownLintFromCommandLine,
39 UnknownLintSuggestion,
40};
41
42#[derive(#[automatically_derived]
impl ::core::fmt::Debug for LintLevelSets {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field1_finish(f, "LintLevelSets",
"list", &&self.list)
}
}Debug)]
46struct LintLevelSets {
47 list: IndexVec<LintStackIndex, LintSet>,
49}
50
51impl ::std::fmt::Debug for LintStackIndex {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
fmt.write_fmt(format_args!("{0}", self.as_u32()))
}
}rustc_index::newtype_index! {
52 struct LintStackIndex {
53 const COMMAND_LINE = 0;
54 }
55}
56
57#[derive(#[automatically_derived]
impl ::core::fmt::Debug for LintSet {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "LintSet",
"specs", &self.specs, "parent", &&self.parent)
}
}Debug)]
66struct LintSet {
67 specs: FxIndexMap<LintId, LevelAndSource>,
70 parent: LintStackIndex,
71}
72
73impl LintLevelSets {
74 fn new() -> Self {
75 LintLevelSets { list: IndexVec::new() }
76 }
77
78 fn get_lint_level(
79 &self,
80 lint: &'static Lint,
81 idx: LintStackIndex,
82 aux: Option<&FxIndexMap<LintId, LevelAndSource>>,
83 sess: &Session,
84 ) -> LevelAndSource {
85 let lint = LintId::of(lint);
86 let (level, mut src) = self.raw_lint_id_level(lint, idx, aux);
87 let (level, lint_id) = reveal_actual_level(level, &mut src, sess, lint, |id| {
88 self.raw_lint_id_level(id, idx, aux)
89 });
90 LevelAndSource { level, lint_id, src }
91 }
92
93 fn raw_lint_id_level(
94 &self,
95 id: LintId,
96 mut idx: LintStackIndex,
97 aux: Option<&FxIndexMap<LintId, LevelAndSource>>,
98 ) -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource) {
99 if let Some(specs) = aux
100 && let Some(&LevelAndSource { level, lint_id, src }) = specs.get(&id)
101 {
102 return (Some((level, lint_id)), src);
103 }
104
105 loop {
106 let LintSet { ref specs, parent } = self.list[idx];
107 if let Some(&LevelAndSource { level, lint_id, src }) = specs.get(&id) {
108 return (Some((level, lint_id)), src);
109 }
110 if idx == COMMAND_LINE {
111 return (None, LintLevelSource::Default);
112 }
113 idx = parent;
114 }
115 }
116}
117
118fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet<LintId> {
119 let store = unerased_lint_store(&tcx.sess);
120 let root_map = tcx.shallow_lint_levels_on(hir::CRATE_OWNER_ID);
121
122 let mut dont_need_to_run: FxHashSet<LintId> = store
123 .get_lints()
124 .into_iter()
125 .filter(|lint| {
126 let has_future_breakage =
128 lint.future_incompatible.is_some_and(|fut| fut.report_in_deps);
129 !has_future_breakage && !lint.eval_always
130 })
131 .filter(|lint| {
132 let lint_level =
133 root_map.lint_level_id_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID);
134 #[allow(non_exhaustive_omitted_patterns)] match lint_level.level {
Level::Allow => true,
_ => false,
}matches!(lint_level.level, Level::Allow)
136 || (#[allow(non_exhaustive_omitted_patterns)] match lint_level.src {
LintLevelSource::Default => true,
_ => false,
}matches!(lint_level.src, LintLevelSource::Default)
137 && lint.default_level(tcx.sess.edition()) == Level::Allow)
138 })
139 .map(|lint| LintId::of(*lint))
140 .collect();
141
142 for owner in tcx.hir_crate_items(()).owners() {
143 let map = tcx.shallow_lint_levels_on(owner);
144
145 for (_, specs) in map.specs.iter() {
147 for (lint, level_and_source) in specs.iter() {
148 if !#[allow(non_exhaustive_omitted_patterns)] match level_and_source.level {
Level::Allow => true,
_ => false,
}matches!(level_and_source.level, Level::Allow) {
149 dont_need_to_run.remove(lint);
150 }
151 }
152 }
153 }
154
155 dont_need_to_run.into()
156}
157
158x;#[instrument(level = "trace", skip(tcx), ret)]
159fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap {
160 let store = unerased_lint_store(tcx.sess);
161 let attrs = tcx.hir_attr_map(owner);
162
163 let mut levels = LintLevelsBuilder {
164 sess: tcx.sess,
165 features: tcx.features(),
166 provider: LintLevelQueryMap {
167 tcx,
168 cur: owner.into(),
169 specs: ShallowLintLevelMap::default(),
170 empty: FxIndexMap::default(),
171 attrs,
172 },
173 lint_added_lints: false,
174 store,
175 registered_tools: tcx.registered_tools(()),
176 };
177
178 if owner == hir::CRATE_OWNER_ID {
179 levels.add_command_line();
180 }
181
182 match attrs.map.range(..) {
183 [] => {}
185 &[(local_id, _)] => levels.add_id(HirId { owner, local_id }),
187 _ => match tcx.hir_owner_node(owner) {
191 hir::OwnerNode::Item(item) => levels.visit_item(item),
192 hir::OwnerNode::ForeignItem(item) => levels.visit_foreign_item(item),
193 hir::OwnerNode::TraitItem(item) => levels.visit_trait_item(item),
194 hir::OwnerNode::ImplItem(item) => levels.visit_impl_item(item),
195 hir::OwnerNode::Crate(mod_) => {
196 levels.add_id(hir::CRATE_HIR_ID);
197 levels.visit_mod(mod_, mod_.spans.inner_span, hir::CRATE_HIR_ID)
198 }
199 hir::OwnerNode::Synthetic => unreachable!(),
200 },
201 }
202
203 let specs = levels.provider.specs;
204
205 #[cfg(debug_assertions)]
206 for (_, v) in specs.specs.iter() {
207 debug_assert!(!v.is_empty());
208 }
209
210 specs
211}
212
213pub struct TopDown {
214 sets: LintLevelSets,
215 cur: LintStackIndex,
216}
217
218pub trait LintLevelsProvider {
219 fn current_specs(&self) -> &FxIndexMap<LintId, LevelAndSource>;
220 fn insert(&mut self, id: LintId, lvl: LevelAndSource);
221 fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource;
222 fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation);
223}
224
225impl LintLevelsProvider for TopDown {
226 fn current_specs(&self) -> &FxIndexMap<LintId, LevelAndSource> {
227 &self.sets.list[self.cur].specs
228 }
229
230 fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
231 self.sets.list[self.cur].specs.insert(id, lvl);
232 }
233
234 fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource {
235 self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), sess)
236 }
237
238 fn push_expectation(&mut self, _: LintExpectationId, _: LintExpectation) {}
239}
240
241struct LintLevelQueryMap<'tcx> {
242 tcx: TyCtxt<'tcx>,
243 cur: HirId,
244 specs: ShallowLintLevelMap,
245 empty: FxIndexMap<LintId, LevelAndSource>,
247 attrs: &'tcx hir::AttributeMap<'tcx>,
248}
249
250impl LintLevelsProvider for LintLevelQueryMap<'_> {
251 fn current_specs(&self) -> &FxIndexMap<LintId, LevelAndSource> {
252 self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
253 }
254 fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
255 self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, lvl);
256 }
257 fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
258 self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
259 }
260 fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation) {
261 self.specs.expectations.push((id, expectation))
262 }
263}
264
265impl<'tcx> LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
266 fn add_id(&mut self, hir_id: HirId) {
267 self.provider.cur = hir_id;
268 self.add(
269 self.provider.attrs.get(hir_id.local_id),
270 hir_id == hir::CRATE_HIR_ID,
271 Some(hir_id),
272 );
273 }
274}
275
276impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
277 type NestedFilter = nested_filter::OnlyBodies;
278
279 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
280 self.provider.tcx
281 }
282
283 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
284 self.add_id(param.hir_id);
285 intravisit::walk_param(self, param);
286 }
287
288 fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
289 self.add_id(it.hir_id());
290 intravisit::walk_item(self, it);
291 }
292
293 fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
294 self.add_id(it.hir_id());
295 intravisit::walk_foreign_item(self, it);
296 }
297
298 fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
299 self.add_id(s.hir_id);
300 intravisit::walk_stmt(self, s);
301 }
302
303 fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
304 self.add_id(e.hir_id);
305 intravisit::walk_expr(self, e);
306 }
307
308 fn visit_pat_field(&mut self, f: &'tcx hir::PatField<'tcx>) -> Self::Result {
309 self.add_id(f.hir_id);
310 intravisit::walk_pat_field(self, f);
311 }
312
313 fn visit_expr_field(&mut self, f: &'tcx hir::ExprField<'tcx>) {
314 self.add_id(f.hir_id);
315 intravisit::walk_expr_field(self, f);
316 }
317
318 fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
319 self.add_id(s.hir_id);
320 intravisit::walk_field_def(self, s);
321 }
322
323 fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
324 self.add_id(v.hir_id);
325 intravisit::walk_variant(self, v);
326 }
327
328 fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) {
329 self.add_id(l.hir_id);
330 intravisit::walk_local(self, l);
331 }
332
333 fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
334 self.add_id(a.hir_id);
335 intravisit::walk_arm(self, a);
336 }
337
338 fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
339 self.add_id(trait_item.hir_id());
340 intravisit::walk_trait_item(self, trait_item);
341 }
342
343 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
344 self.add_id(impl_item.hir_id());
345 intravisit::walk_impl_item(self, impl_item);
346 }
347}
348
349pub struct LintLevelsBuilder<'s, P> {
350 sess: &'s Session,
351 features: &'s Features,
352 provider: P,
353 lint_added_lints: bool,
354 store: &'s LintStore,
355 registered_tools: &'s RegisteredTools,
356}
357
358pub(crate) struct BuilderPush {
359 prev: LintStackIndex,
360}
361
362impl<'s> LintLevelsBuilder<'s, TopDown> {
363 pub(crate) fn new(
364 sess: &'s Session,
365 features: &'s Features,
366 lint_added_lints: bool,
367 store: &'s LintStore,
368 registered_tools: &'s RegisteredTools,
369 ) -> Self {
370 let mut builder = LintLevelsBuilder {
371 sess,
372 features,
373 provider: TopDown { sets: LintLevelSets::new(), cur: COMMAND_LINE },
374 lint_added_lints,
375 store,
376 registered_tools,
377 };
378 builder.process_command_line();
379 match (&builder.provider.sets.list.len(), &1) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(builder.provider.sets.list.len(), 1);
380 builder
381 }
382
383 pub fn crate_root(
384 sess: &'s Session,
385 features: &'s Features,
386 lint_added_lints: bool,
387 store: &'s LintStore,
388 registered_tools: &'s RegisteredTools,
389 crate_attrs: &[ast::Attribute],
390 ) -> Self {
391 let mut builder = Self::new(sess, features, lint_added_lints, store, registered_tools);
392 builder.add(crate_attrs, true, None);
393 builder
394 }
395
396 fn process_command_line(&mut self) {
397 self.provider.cur = self
398 .provider
399 .sets
400 .list
401 .push(LintSet { specs: FxIndexMap::default(), parent: COMMAND_LINE });
402 self.add_command_line();
403 }
404
405 pub(crate) fn push(
420 &mut self,
421 attrs: &[ast::Attribute],
422 is_crate_node: bool,
423 source_hir_id: Option<HirId>,
424 ) -> BuilderPush {
425 let prev = self.provider.cur;
426 self.provider.cur =
427 self.provider.sets.list.push(LintSet { specs: FxIndexMap::default(), parent: prev });
428
429 self.add(attrs, is_crate_node, source_hir_id);
430
431 if self.provider.current_specs().is_empty() {
432 self.provider.sets.list.pop();
433 self.provider.cur = prev;
434 }
435
436 BuilderPush { prev }
437 }
438
439 pub(crate) fn pop(&mut self, push: BuilderPush) {
441 self.provider.cur = push.prev;
442 std::mem::forget(push);
443 }
444}
445
446#[cfg(debug_assertions)]
447impl Drop for BuilderPush {
448 fn drop(&mut self) {
449 {
::core::panicking::panic_fmt(format_args!("Found a `push` without a `pop`."));
};panic!("Found a `push` without a `pop`.");
450 }
451}
452
453impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
454 pub(crate) fn sess(&self) -> &Session {
455 self.sess
456 }
457
458 pub(crate) fn features(&self) -> &Features {
459 self.features
460 }
461
462 fn current_specs(&self) -> &FxIndexMap<LintId, LevelAndSource> {
463 self.provider.current_specs()
464 }
465
466 fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
467 self.provider.insert(id, lvl)
468 }
469
470 fn add_command_line(&mut self) {
471 for &(ref lint_name, level) in &self.sess.opts.lint_opts {
472 let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
474 if lint_name_only == crate::WARNINGS.name_lower() && #[allow(non_exhaustive_omitted_patterns)] match level {
Level::ForceWarn => true,
_ => false,
}matches!(level, Level::ForceWarn) {
475 self.sess
476 .dcx()
477 .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
478 }
479 match self.store.check_lint_name(lint_name_only, tool_name, self.registered_tools) {
480 CheckLintNameResult::Renamed(ref replace) => {
481 let name = lint_name.as_str();
482 let suggestion = RenamedLintSuggestion::WithoutSpan { replace };
483 let requested_level = RequestedLevel { level, lint_name };
484 let lint =
485 RenamedLintFromCommandLine { name, replace, suggestion, requested_level };
486 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
487 }
488 CheckLintNameResult::Removed(ref reason) => {
489 let name = lint_name.as_str();
490 let requested_level = RequestedLevel { level, lint_name };
491 let lint = RemovedLintFromCommandLine { name, reason, requested_level };
492 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
493 }
494 CheckLintNameResult::NoLint(suggestion) => {
495 let name = lint_name.clone();
496 let suggestion = suggestion.map(|(replace, from_rustc)| {
497 UnknownLintSuggestion::WithoutSpan { replace, from_rustc }
498 });
499 let requested_level = RequestedLevel { level, lint_name };
500 let lint = UnknownLintFromCommandLine { name, suggestion, requested_level };
501 self.emit_lint(UNKNOWN_LINTS, lint);
502 }
503 CheckLintNameResult::Tool(_, Some(ref replace)) => {
504 let name = lint_name.clone();
505 let requested_level = RequestedLevel { level, lint_name };
506 let lint = DeprecatedLintNameFromCommandLine { name, replace, requested_level };
507 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
508 }
509 CheckLintNameResult::NoTool => {
510 self.sess.dcx().emit_err(CheckNameUnknownTool {
511 tool_name: tool_name.unwrap(),
512 sub: RequestedLevel { level, lint_name },
513 });
514 }
515 _ => {}
516 };
517
518 let lint_flag_val = Symbol::intern(lint_name);
519
520 let Some(ids) = self.store.find_lints(lint_name) else {
521 continue;
523 };
524 for &id in ids {
525 if let Some(LevelAndSource { level: Level::ForceWarn | Level::Forbid, .. }) =
527 self.current_specs().get(&id)
528 {
529 continue;
530 }
531
532 if self.check_gated_lint(id, DUMMY_SP, true) {
533 let src = LintLevelSource::CommandLine(lint_flag_val, level);
534 self.insert(id, LevelAndSource { level, lint_id: None, src });
535 }
536 }
537 }
538 }
539
540 fn insert_spec(&mut self, id: LintId, LevelAndSource { level, lint_id, src }: LevelAndSource) {
544 let LevelAndSource { level: old_level, src: old_src, .. } =
545 self.provider.get_lint_level(id.lint, self.sess);
546
547 if self.lint_added_lints && level == Level::Deny && old_level == Level::Forbid {
554 return;
556 } else if self.lint_added_lints && level != Level::Forbid && old_level == Level::Forbid {
557 let id_name = id.lint.name_lower();
564 let fcw_warning = match old_src {
565 LintLevelSource::Default => false,
566 LintLevelSource::Node { name, .. } => self.store.is_lint_group(name),
567 LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol),
568 };
569 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/levels.rs:569",
"rustc_lint::levels", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/levels.rs"),
::tracing_core::__macro_support::Option::Some(569u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::levels"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("fcw_warning={0:?}, specs.get(&id) = {1:?}, old_src={2:?}, id_name={3:?}",
fcw_warning, self.current_specs(), old_src, id_name) as
&dyn Value))])
});
} else { ; }
};debug!(
570 "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}",
571 fcw_warning,
572 self.current_specs(),
573 old_src,
574 id_name
575 );
576 let sub = match old_src {
577 LintLevelSource::Default => {
578 OverruledAttributeSub::DefaultSource { id: id.to_string() }
579 }
580 LintLevelSource::Node { span, reason, .. } => {
581 OverruledAttributeSub::NodeSource { span, reason }
582 }
583 LintLevelSource::CommandLine(_, _) => OverruledAttributeSub::CommandLineSource,
584 };
585 if !fcw_warning {
586 self.sess.dcx().emit_err(OverruledAttribute {
587 span: src.span(),
588 overruled: src.span(),
589 lint_level: level.as_str(),
590 lint_source: src.name(),
591 sub,
592 });
593 } else {
594 self.emit_span_lint(
595 FORBIDDEN_LINT_GROUPS,
596 src.span().into(),
597 OverruledAttributeLint {
598 overruled: src.span(),
599 lint_level: level.as_str(),
600 lint_source: src.name(),
601 sub,
602 },
603 );
604 }
605
606 if !fcw_warning {
610 return;
611 }
612 }
613
614 if let Level::Expect = level
618 && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS)
619 {
620 return;
621 }
622
623 match (old_level, level) {
624 (Level::ForceWarn, Level::Expect) => {
626 self.insert(id, LevelAndSource { level: Level::ForceWarn, lint_id, src: old_src })
627 }
628 (Level::ForceWarn, _) => self.insert(
630 id,
631 LevelAndSource { level: Level::ForceWarn, lint_id: None, src: old_src },
632 ),
633 _ => self.insert(id, LevelAndSource { level, lint_id, src }),
635 };
636 }
637
638 fn add(
639 &mut self,
640 attrs: &[impl AttributeExt],
641 is_crate_node: bool,
642 source_hir_id: Option<HirId>,
643 ) {
644 let sess = self.sess;
645 for (attr_index, attr) in attrs.iter().enumerate() {
646 if attr.is_automatically_derived_attr() {
647 self.insert(
648 LintId::of(SINGLE_USE_LIFETIMES),
649 LevelAndSource {
650 level: Level::Allow,
651 lint_id: None,
652 src: LintLevelSource::Default,
653 },
654 );
655 continue;
656 }
657
658 if attr.is_doc_hidden() {
660 self.insert(
661 LintId::of(MISSING_DOCS),
662 LevelAndSource {
663 level: Level::Allow,
664 lint_id: None,
665 src: LintLevelSource::Default,
666 },
667 );
668 continue;
669 }
670
671 let (level, lint_id) = match Level::from_attr(attr) {
672 None => continue,
673 Some((Level::Expect, Some(unstable_id))) if let Some(hir_id) = source_hir_id => {
676 let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = unstable_id
677 else {
678 ::rustc_middle::util::bug::bug_fmt(format_args!("stable id Level::from_attr"))bug!("stable id Level::from_attr")
679 };
680
681 let stable_id = LintExpectationId::Stable {
682 hir_id,
683 attr_index: attr_index.try_into().unwrap(),
684 lint_index: None,
685 };
686
687 (Level::Expect, Some(stable_id))
688 }
689 Some((lvl, id)) => (lvl, id),
690 };
691
692 let Some(mut metas) = attr.meta_item_list() else { continue };
693
694 let Some(tail_li) = metas.last() else {
696 continue;
698 };
699
700 let mut reason = None;
703 if let Some(item) = tail_li.meta_item() {
704 match item.kind {
705 ast::MetaItemKind::Word => {} ast::MetaItemKind::NameValue(ref name_value) => {
707 if item.path == sym::reason {
708 if let ast::LitKind::Str(rationale, _) = name_value.kind {
709 reason = Some(rationale);
710 } else {
711 sess.dcx().emit_err(MalformedAttribute {
712 span: name_value.span,
713 sub: MalformedAttributeSub::ReasonMustBeStringLiteral(
714 name_value.span,
715 ),
716 });
717 }
718 metas.pop().unwrap();
720 } else {
721 sess.dcx().emit_err(MalformedAttribute {
722 span: item.span,
723 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
724 });
725 }
726 }
727 ast::MetaItemKind::List(_) => {
728 sess.dcx().emit_err(MalformedAttribute {
729 span: item.span,
730 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
731 });
732 }
733 }
734 }
735
736 for (lint_index, li) in metas.iter_mut().enumerate() {
737 let mut lint_id = lint_id;
738 if let Some(id) = &mut lint_id {
739 id.set_lint_index(Some(lint_index as u16));
740 }
741
742 let sp = li.span();
743 let meta_item = match li {
744 ast::MetaItemInner::MetaItem(meta_item) if meta_item.is_word() => meta_item,
745 _ => {
746 let sub = if let Some(item) = li.meta_item()
747 && let ast::MetaItemKind::NameValue(_) = item.kind
748 && item.path == sym::reason
749 {
750 MalformedAttributeSub::ReasonMustComeLast(sp)
751 } else {
752 MalformedAttributeSub::BadAttributeArgument(sp)
753 };
754
755 sess.dcx().emit_err(MalformedAttribute { span: sp, sub });
756 continue;
757 }
758 };
759 let tool_ident = if meta_item.path.segments.len() > 1 {
760 Some(meta_item.path.segments.remove(0).ident)
761 } else {
762 None
763 };
764 let tool_name = tool_ident.map(|ident| ident.name);
765 let name = pprust::path_to_string(&meta_item.path);
766 let lint_result =
767 self.store.check_lint_name(&name, tool_name, self.registered_tools);
768
769 let (ids, name) = match lint_result {
770 CheckLintNameResult::Ok(ids) => {
771 let name =
772 meta_item.path.segments.last().expect("empty lint name").ident.name;
773 (ids, name)
774 }
775
776 CheckLintNameResult::Tool(ids, new_lint_name) => {
777 let name = match new_lint_name {
778 None => {
779 let complete_name =
780 &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}",
tool_ident.unwrap().name, name))
})format!("{}::{}", tool_ident.unwrap().name, name);
781 Symbol::intern(complete_name)
782 }
783 Some(new_lint_name) => {
784 self.emit_span_lint(
785 builtin::RENAMED_AND_REMOVED_LINTS,
786 sp.into(),
787 DeprecatedLintName {
788 name,
789 suggestion: sp,
790 replace: &new_lint_name,
791 },
792 );
793 Symbol::intern(&new_lint_name)
794 }
795 };
796 (ids, name)
797 }
798
799 CheckLintNameResult::MissingTool => {
800 continue;
805 }
806
807 CheckLintNameResult::NoTool => {
808 sess.dcx().emit_err(UnknownToolInScopedLint {
809 span: tool_ident.map(|ident| ident.span),
810 tool_name: tool_name.unwrap(),
811 lint_name: pprust::path_to_string(&meta_item.path),
812 is_nightly_build: sess.is_nightly_build(),
813 });
814 continue;
815 }
816
817 CheckLintNameResult::Renamed(ref replace) => {
818 if self.lint_added_lints {
819 let suggestion =
820 RenamedLintSuggestion::WithSpan { suggestion: sp, replace };
821 let name =
822 tool_ident.map(|tool| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}", tool, name))
})format!("{tool}::{name}")).unwrap_or(name);
823 let lint = RenamedLint { name: name.as_str(), replace, suggestion };
824 self.emit_span_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
825 }
826
827 let CheckLintNameResult::Ok(ids) =
833 self.store.check_lint_name(replace, None, self.registered_tools)
834 else {
835 {
::core::panicking::panic_fmt(format_args!("renamed lint does not exist: {0}",
replace));
};panic!("renamed lint does not exist: {replace}");
836 };
837
838 (ids, Symbol::intern(&replace))
839 }
840
841 CheckLintNameResult::Removed(ref reason) => {
842 if self.lint_added_lints {
843 let name =
844 tool_ident.map(|tool| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}", tool, name))
})format!("{tool}::{name}")).unwrap_or(name);
845 let lint = RemovedLint { name: name.as_str(), reason };
846 self.emit_span_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
847 }
848 continue;
849 }
850
851 CheckLintNameResult::NoLint(suggestion) => {
852 if self.lint_added_lints {
853 let name =
854 tool_ident.map(|tool| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}", tool, name))
})format!("{tool}::{name}")).unwrap_or(name);
855 let suggestion = suggestion.map(|(replace, from_rustc)| {
856 UnknownLintSuggestion::WithSpan {
857 suggestion: sp,
858 replace,
859 from_rustc,
860 }
861 });
862 let lint = UnknownLint { name, suggestion };
863 self.emit_span_lint(UNKNOWN_LINTS, sp.into(), lint);
864 }
865 continue;
866 }
867 };
868
869 let src = LintLevelSource::Node { name, span: sp, reason };
870 for &id in ids {
871 if self.check_gated_lint(id, sp, false) {
872 self.insert_spec(id, LevelAndSource { level, lint_id, src });
873 }
874 }
875
876 if let (Level::Expect, Some(expect_id)) = (level, lint_id) {
882 let is_unfulfilled_lint_expectations = match ids {
886 [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS),
887 _ => false,
888 };
889 self.provider.push_expectation(
890 expect_id,
891 LintExpectation::new(
892 reason,
893 sp,
894 is_unfulfilled_lint_expectations,
895 tool_name,
896 ),
897 );
898 }
899 }
900 }
901
902 if self.lint_added_lints && !is_crate_node {
903 for (id, &LevelAndSource { level, ref src, .. }) in self.current_specs().iter() {
904 if !id.lint.crate_level_only {
905 continue;
906 }
907
908 let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } = *src
909 else {
910 continue;
911 };
912
913 self.emit_span_lint(
914 UNUSED_ATTRIBUTES,
915 lint_attr_span.into(),
916 IgnoredUnlessCrateSpecified { level: level.as_str(), name: lint_attr_name },
917 );
918 break;
920 }
921 }
922 }
923
924 #[track_caller]
928 fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool {
929 let feature = if let Some(feature) = lint_id.lint.feature_gate
930 && !self.features.enabled(feature)
931 && !span.allows_unstable(feature)
932 {
933 feature
935 } else {
936 return true;
938 };
939
940 if self.lint_added_lints {
941 let lint = builtin::UNKNOWN_LINTS;
942 let level = self.lint_level(builtin::UNKNOWN_LINTS);
943 lint_level(self.sess, lint, level, Some(span.into()), |lint| {
944 lint.primary_message(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("unknown lint: `{$name}`"))inline_fluent!("unknown lint: `{$name}`"));
945 lint.arg("name", lint_id.lint.name_lower());
946 lint.note(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("the `{$name}` lint is unstable"))inline_fluent!("the `{$name}` lint is unstable"));
947 rustc_session::parse::add_feature_diagnostics_for_issue(
948 lint,
949 &self.sess,
950 feature,
951 GateIssue::Language,
952 lint_from_cli,
953 None,
954 );
955 });
956 }
957
958 false
959 }
960
961 pub fn lint_level(&self, lint: &'static Lint) -> LevelAndSource {
963 self.provider.get_lint_level(lint, self.sess)
964 }
965
966 #[track_caller]
971 pub(crate) fn opt_span_lint(
972 &self,
973 lint: &'static Lint,
974 span: Option<MultiSpan>,
975 decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
976 ) {
977 let level = self.lint_level(lint);
978 lint_level(self.sess, lint, level, span, decorate)
979 }
980
981 #[track_caller]
982 pub fn emit_span_lint(
983 &self,
984 lint: &'static Lint,
985 span: MultiSpan,
986 decorate: impl for<'a> LintDiagnostic<'a, ()>,
987 ) {
988 let level = self.lint_level(lint);
989 lint_level(self.sess, lint, level, Some(span), |lint| {
990 decorate.decorate_lint(lint);
991 });
992 }
993
994 #[track_caller]
995 pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> LintDiagnostic<'a, ()>) {
996 let level = self.lint_level(lint);
997 lint_level(self.sess, lint, level, None, |lint| {
998 decorate.decorate_lint(lint);
999 });
1000 }
1001}
1002
1003pub(crate) fn provide(providers: &mut Providers) {
1004 *providers = Providers { shallow_lint_levels_on, lints_that_dont_need_to_run, ..*providers };
1005}
1006
1007pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
1008 match lint_name.split_once("::") {
1009 Some((tool_name, lint_name)) => {
1010 let tool_name = Symbol::intern(tool_name);
1011
1012 (Some(tool_name), lint_name)
1013 }
1014 None => (None, lint_name),
1015 }
1016}