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, Diagnostic, MultiSpan, msg};
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, diag_lint_level,
14 lint_level, 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(name, _) => {
584 OverruledAttributeSub::CommandLineSource { id: name }
585 }
586 };
587 if !fcw_warning {
588 self.sess.dcx().emit_err(OverruledAttribute {
589 span: src.span(),
590 overruled: src.span(),
591 lint_level: level.as_str(),
592 lint_source: src.name(),
593 sub,
594 });
595 } else {
596 self.emit_span_lint(
597 FORBIDDEN_LINT_GROUPS,
598 src.span().into(),
599 OverruledAttributeLint {
600 overruled: src.span(),
601 lint_level: level.as_str(),
602 lint_source: src.name(),
603 sub,
604 },
605 );
606 }
607
608 if !fcw_warning {
612 return;
613 }
614 }
615
616 if let Level::Expect = level
620 && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS)
621 {
622 return;
623 }
624
625 match (old_level, level) {
626 (Level::ForceWarn, Level::Expect) => {
628 self.insert(id, LevelAndSource { level: Level::ForceWarn, lint_id, src: old_src })
629 }
630 (Level::ForceWarn, _) => self.insert(
632 id,
633 LevelAndSource { level: Level::ForceWarn, lint_id: None, src: old_src },
634 ),
635 _ => self.insert(id, LevelAndSource { level, lint_id, src }),
637 };
638 }
639
640 fn add(
641 &mut self,
642 attrs: &[impl AttributeExt],
643 is_crate_node: bool,
644 source_hir_id: Option<HirId>,
645 ) {
646 let sess = self.sess;
647 for (attr_index, attr) in attrs.iter().enumerate() {
648 if attr.is_automatically_derived_attr() {
649 self.insert(
650 LintId::of(SINGLE_USE_LIFETIMES),
651 LevelAndSource {
652 level: Level::Allow,
653 lint_id: None,
654 src: LintLevelSource::Default,
655 },
656 );
657 continue;
658 }
659
660 if attr.is_doc_hidden() {
662 self.insert(
663 LintId::of(MISSING_DOCS),
664 LevelAndSource {
665 level: Level::Allow,
666 lint_id: None,
667 src: LintLevelSource::Default,
668 },
669 );
670 continue;
671 }
672
673 let (level, lint_id) = match Level::from_attr(attr) {
674 None => continue,
675 Some((Level::Expect, Some(unstable_id))) if let Some(hir_id) = source_hir_id => {
678 let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = unstable_id
679 else {
680 ::rustc_middle::util::bug::bug_fmt(format_args!("stable id Level::from_attr"))bug!("stable id Level::from_attr")
681 };
682
683 let stable_id = LintExpectationId::Stable {
684 hir_id,
685 attr_index: attr_index.try_into().unwrap(),
686 lint_index: None,
687 };
688
689 (Level::Expect, Some(stable_id))
690 }
691 Some((lvl, id)) => (lvl, id),
692 };
693
694 let Some(mut metas) = attr.meta_item_list() else { continue };
695
696 let Some(tail_li) = metas.last() else {
698 continue;
700 };
701
702 let mut reason = None;
705 if let Some(item) = tail_li.meta_item() {
706 match item.kind {
707 ast::MetaItemKind::Word => {} ast::MetaItemKind::NameValue(ref name_value) => {
709 if item.path == sym::reason {
710 if let ast::LitKind::Str(rationale, _) = name_value.kind {
711 reason = Some(rationale);
712 } else {
713 sess.dcx().emit_err(MalformedAttribute {
714 span: name_value.span,
715 sub: MalformedAttributeSub::ReasonMustBeStringLiteral(
716 name_value.span,
717 ),
718 });
719 }
720 metas.pop().unwrap();
722 } else {
723 sess.dcx().emit_err(MalformedAttribute {
724 span: item.span,
725 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
726 });
727 }
728 }
729 ast::MetaItemKind::List(_) => {
730 sess.dcx().emit_err(MalformedAttribute {
731 span: item.span,
732 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
733 });
734 }
735 }
736 }
737
738 for (lint_index, li) in metas.iter_mut().enumerate() {
739 let mut lint_id = lint_id;
740 if let Some(id) = &mut lint_id {
741 id.set_lint_index(Some(lint_index as u16));
742 }
743
744 let sp = li.span();
745 let meta_item = match li {
746 ast::MetaItemInner::MetaItem(meta_item) if meta_item.is_word() => meta_item,
747 _ => {
748 let sub = if let Some(item) = li.meta_item()
749 && let ast::MetaItemKind::NameValue(_) = item.kind
750 && item.path == sym::reason
751 {
752 MalformedAttributeSub::ReasonMustComeLast(sp)
753 } else {
754 MalformedAttributeSub::BadAttributeArgument(sp)
755 };
756
757 sess.dcx().emit_err(MalformedAttribute { span: sp, sub });
758 continue;
759 }
760 };
761 let tool_ident = if meta_item.path.segments.len() > 1 {
762 Some(meta_item.path.segments.remove(0).ident)
763 } else {
764 None
765 };
766 let tool_name = tool_ident.map(|ident| ident.name);
767 let name = pprust::path_to_string(&meta_item.path);
768 let lint_result =
769 self.store.check_lint_name(&name, tool_name, self.registered_tools);
770
771 let (ids, name) = match lint_result {
772 CheckLintNameResult::Ok(ids) => {
773 let name =
774 meta_item.path.segments.last().expect("empty lint name").ident.name;
775 (ids, name)
776 }
777
778 CheckLintNameResult::Tool(ids, new_lint_name) => {
779 let name = match new_lint_name {
780 None => {
781 let complete_name =
782 &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}",
tool_ident.unwrap().name, name))
})format!("{}::{}", tool_ident.unwrap().name, name);
783 Symbol::intern(complete_name)
784 }
785 Some(new_lint_name) => {
786 self.emit_span_lint(
787 builtin::RENAMED_AND_REMOVED_LINTS,
788 sp.into(),
789 DeprecatedLintName {
790 name,
791 suggestion: sp,
792 replace: &new_lint_name,
793 },
794 );
795 Symbol::intern(&new_lint_name)
796 }
797 };
798 (ids, name)
799 }
800
801 CheckLintNameResult::MissingTool => {
802 continue;
807 }
808
809 CheckLintNameResult::NoTool => {
810 sess.dcx().emit_err(UnknownToolInScopedLint {
811 span: tool_ident.map(|ident| ident.span),
812 tool_name: tool_name.unwrap(),
813 lint_name: pprust::path_to_string(&meta_item.path),
814 is_nightly_build: sess.is_nightly_build(),
815 });
816 continue;
817 }
818
819 CheckLintNameResult::Renamed(ref replace) => {
820 if self.lint_added_lints {
821 let suggestion =
822 RenamedLintSuggestion::WithSpan { suggestion: sp, replace };
823 let name =
824 tool_ident.map(|tool| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}", tool, name))
})format!("{tool}::{name}")).unwrap_or(name);
825 self.emit_span_lint(
826 RENAMED_AND_REMOVED_LINTS,
827 sp.into(),
828 RenamedLint { name: name.as_str(), replace, suggestion },
829 );
830 }
831
832 let CheckLintNameResult::Ok(ids) =
838 self.store.check_lint_name(replace, None, self.registered_tools)
839 else {
840 {
::core::panicking::panic_fmt(format_args!("renamed lint does not exist: {0}",
replace));
};panic!("renamed lint does not exist: {replace}");
841 };
842
843 (ids, Symbol::intern(&replace))
844 }
845
846 CheckLintNameResult::Removed(ref reason) => {
847 if self.lint_added_lints {
848 let name =
849 tool_ident.map(|tool| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}", tool, name))
})format!("{tool}::{name}")).unwrap_or(name);
850 self.emit_span_lint(
851 RENAMED_AND_REMOVED_LINTS,
852 sp.into(),
853 RemovedLint { name: name.as_str(), reason },
854 );
855 }
856 continue;
857 }
858
859 CheckLintNameResult::NoLint(suggestion) => {
860 if self.lint_added_lints {
861 let name =
862 tool_ident.map(|tool| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}", tool, name))
})format!("{tool}::{name}")).unwrap_or(name);
863 let suggestion = suggestion.map(|(replace, from_rustc)| {
864 UnknownLintSuggestion::WithSpan {
865 suggestion: sp,
866 replace,
867 from_rustc,
868 }
869 });
870 self.emit_span_lint(
871 UNKNOWN_LINTS,
872 sp.into(),
873 UnknownLint { name, suggestion },
874 );
875 }
876 continue;
877 }
878 };
879
880 let src = LintLevelSource::Node { name, span: sp, reason };
881 for &id in ids {
882 if self.check_gated_lint(id, sp, false) {
883 self.insert_spec(id, LevelAndSource { level, lint_id, src });
884 }
885 }
886
887 if let (Level::Expect, Some(expect_id)) = (level, lint_id) {
893 let is_unfulfilled_lint_expectations = match ids {
897 [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS),
898 _ => false,
899 };
900 self.provider.push_expectation(
901 expect_id,
902 LintExpectation::new(
903 reason,
904 sp,
905 is_unfulfilled_lint_expectations,
906 tool_name,
907 ),
908 );
909 }
910 }
911 }
912
913 if self.lint_added_lints && !is_crate_node {
914 for (id, &LevelAndSource { level, ref src, .. }) in self.current_specs().iter() {
915 if !id.lint.crate_level_only {
916 continue;
917 }
918
919 let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } = *src
920 else {
921 continue;
922 };
923
924 self.emit_span_lint(
925 UNUSED_ATTRIBUTES,
926 lint_attr_span.into(),
927 IgnoredUnlessCrateSpecified { level: level.as_str(), name: lint_attr_name },
928 );
929 break;
931 }
932 }
933 }
934
935 #[track_caller]
939 fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool {
940 let feature = if let Some(feature) = lint_id.lint.feature_gate
941 && !self.features.enabled(feature)
942 && !span.allows_unstable(feature)
943 {
944 feature
946 } else {
947 return true;
949 };
950
951 if self.lint_added_lints {
952 let lint = builtin::UNKNOWN_LINTS;
953 let level = self.lint_level(builtin::UNKNOWN_LINTS);
954 lint_level(self.sess, lint, level, Some(span.into()), |lint| {
955 lint.primary_message(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("unknown lint: `{$name}`"))msg!("unknown lint: `{$name}`"));
956 lint.arg("name", lint_id.lint.name_lower());
957 lint.note(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("the `{$name}` lint is unstable"))msg!("the `{$name}` lint is unstable"));
958 rustc_session::parse::add_feature_diagnostics_for_issue(
959 lint,
960 &self.sess,
961 feature,
962 GateIssue::Language,
963 lint_from_cli,
964 None,
965 );
966 });
967 }
968
969 false
970 }
971
972 pub fn lint_level(&self, lint: &'static Lint) -> LevelAndSource {
974 self.provider.get_lint_level(lint, self.sess)
975 }
976
977 #[track_caller]
980 pub(crate) fn opt_span_lint(
981 &self,
982 lint: &'static Lint,
983 span: Option<MultiSpan>,
984 decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
985 ) {
986 let level = self.lint_level(lint);
987 lint_level(self.sess, lint, level, span, decorate)
988 }
989
990 #[track_caller]
993 pub(crate) fn opt_span_diag_lint(
994 &self,
995 lint: &'static Lint,
996 span: Option<MultiSpan>,
997 decorator: impl for<'a> Diagnostic<'a, ()>,
998 ) {
999 let level = self.lint_level(lint);
1000 diag_lint_level(self.sess, lint, level, span, decorator)
1001 }
1002
1003 #[track_caller]
1004 pub fn emit_span_lint(
1005 &self,
1006 lint: &'static Lint,
1007 span: MultiSpan,
1008 decorator: impl for<'a> Diagnostic<'a, ()>,
1009 ) {
1010 let level = self.lint_level(lint);
1011 diag_lint_level(self.sess, lint, level, Some(span), decorator);
1012 }
1013
1014 #[track_caller]
1015 pub fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> Diagnostic<'a, ()>) {
1016 let level = self.lint_level(lint);
1017 diag_lint_level(self.sess, lint, level, None, decorator);
1018 }
1019}
1020
1021pub(crate) fn provide(providers: &mut Providers) {
1022 *providers = Providers { shallow_lint_levels_on, lints_that_dont_need_to_run, ..*providers };
1023}
1024
1025pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
1026 match lint_name.split_once("::") {
1027 Some((tool_name, lint_name)) => {
1028 let tool_name = Symbol::intern(tool_name);
1029
1030 (Some(tool_name), lint_name)
1031 }
1032 None => (None, lint_name),
1033 }
1034}