1use std::fmt::Debug;
2
3use rustc_ast as ast;
4use rustc_ast::attr::AttributeExt;
5use rustc_ast_pretty::pprust;
6use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
7use rustc_data_structures::unord::UnordSet;
8use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, MultiSpan, msg};
9use rustc_feature::{Features, GateIssue};
10use rustc_hir as hir;
11use rustc_hir::HirId;
12use rustc_hir::intravisit::{self, Visitor};
13use rustc_index::IndexVec;
14use rustc_middle::hir::nested_filter;
15use rustc_middle::lint::{
16 LevelSpec, LintExpectation, LintLevelSource, ShallowLintLevelMap, StableLevelSpec,
17 UnstableLevelSpec, emit_lint_base, reveal_actual_level_spec,
18};
19use rustc_middle::query::Providers;
20use rustc_middle::ty::{RegisteredTools, TyCtxt};
21use rustc_session::Session;
22use rustc_session::lint::builtin::{
23 self, FORBIDDEN_LINT_GROUPS, RENAMED_AND_REMOVED_LINTS, SINGLE_USE_LIFETIMES,
24 UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES,
25};
26use rustc_session::lint::{
27 Level, Lint, LintExpectationId, LintId, StableLintExpectationId, UnstableLintExpectationId,
28};
29use rustc_span::{AttrId, DUMMY_SP, Span, Symbol, sym};
30use tracing::{debug, instrument};
31
32use crate::builtin::MISSING_DOCS;
33use crate::context::{CheckLintNameResult, LintStore};
34use crate::errors::{
35 CheckNameUnknownTool, MalformedAttribute, MalformedAttributeSub, OverruledAttribute,
36 OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup,
37};
38use crate::late::unerased_lint_store;
39use crate::lints::{
40 DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified,
41 OverruledAttributeLint, RemovedLint, RemovedLintFromCommandLine, RenamedLint,
42 RenamedLintFromCommandLine, RenamedLintSuggestion, UnknownLint, UnknownLintFromCommandLine,
43 UnknownLintSuggestion,
44};
45
46#[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)]
50struct LintLevelSets {
51 list: IndexVec<LintStackIndex, LintSet>,
53}
54
55impl ::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! {
56 struct LintStackIndex {
57 const COMMAND_LINE = 0;
58 }
59}
60
61#[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)]
70struct LintSet {
71 specs: FxIndexMap<LintId, UnstableLevelSpec>,
72 parent: LintStackIndex,
73}
74
75impl LintLevelSets {
76 fn new() -> Self {
77 LintLevelSets { list: IndexVec::new() }
78 }
79
80 fn get_lint_level_spec(
81 &self,
82 lint: &'static Lint,
83 idx: LintStackIndex,
84 aux: Option<&FxIndexMap<LintId, UnstableLevelSpec>>,
85 sess: &Session,
86 ) -> UnstableLevelSpec {
87 reveal_actual_level_spec(sess, LintId::of(lint), |id| {
88 self.raw_lint_level_spec(id, idx, aux)
89 })
90 }
91
92 fn raw_lint_level_spec(
93 &self,
94 id: LintId,
95 mut idx: LintStackIndex,
96 aux: Option<&FxIndexMap<LintId, UnstableLevelSpec>>,
97 ) -> Option<UnstableLevelSpec> {
98 if let Some(specs) = aux
99 && let Some(level_spec) = specs.get(&id)
100 {
101 return Some(*level_spec);
102 }
103
104 loop {
105 let LintSet { ref specs, parent } = self.list[idx];
106 if let Some(level_spec) = specs.get(&id) {
107 return Some(*level_spec);
108 }
109 if idx == COMMAND_LINE {
110 return None;
111 }
112 idx = parent;
113 }
114 }
115}
116
117fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet<LintId> {
118 let store = unerased_lint_store(&tcx.sess);
119 let root_map = tcx.shallow_lint_levels_on(hir::CRATE_OWNER_ID);
120
121 let mut dont_need_to_run: FxHashSet<LintId> = store
122 .get_lints()
123 .into_iter()
124 .filter(|lint| {
125 let has_future_breakage =
127 lint.future_incompatible.is_some_and(|fut| fut.report_in_deps);
128 !has_future_breakage && !lint.eval_always
129 })
130 .filter(|lint| {
131 let level_spec =
132 root_map.lint_level_spec_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID);
133 level_spec.is_allow()
135 || (#[allow(non_exhaustive_omitted_patterns)] match level_spec.src {
LintLevelSource::Default => true,
_ => false,
}matches!(level_spec.src, LintLevelSource::Default)
136 && lint.default_level(tcx.sess.edition()) == Level::Allow)
137 })
138 .map(|lint| LintId::of(*lint))
139 .collect();
140
141 for owner in tcx.hir_crate_items(()).owners() {
142 let map = tcx.shallow_lint_levels_on(owner);
143
144 for (_, specs) in map.specs.iter() {
146 for (lint, level_spec) in specs.iter() {
147 if !level_spec.is_allow() {
148 dont_need_to_run.remove(lint);
149 }
150 }
151 }
152 }
153
154 dont_need_to_run.into()
155}
156
157x;#[instrument(level = "trace", skip(tcx), ret)]
158fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap {
159 let store = unerased_lint_store(tcx.sess);
160 let attrs = tcx.hir_attr_map(owner);
161
162 let mut levels = LintLevelsBuilder {
163 sess: tcx.sess,
164 features: tcx.features(),
165 provider: LintLevelQueryMap {
166 tcx,
167 cur: owner.into(),
168 specs: ShallowLintLevelMap::default(),
169 empty: FxIndexMap::default(),
170 attrs,
171 },
172 lint_added_lints: false,
173 store,
174 registered_tools: tcx.registered_tools(()),
175 };
176
177 if owner == hir::CRATE_OWNER_ID {
178 levels.add_command_line();
179 }
180
181 match attrs.map.range(..) {
182 [] => {}
184 &[(local_id, _)] => levels.add_id(HirId { owner, local_id }),
186 _ => match tcx.hir_owner_node(owner) {
190 hir::OwnerNode::Item(item) => levels.visit_item(item),
191 hir::OwnerNode::ForeignItem(item) => levels.visit_foreign_item(item),
192 hir::OwnerNode::TraitItem(item) => levels.visit_trait_item(item),
193 hir::OwnerNode::ImplItem(item) => levels.visit_impl_item(item),
194 hir::OwnerNode::Crate(mod_) => {
195 levels.add_id(hir::CRATE_HIR_ID);
196 levels.visit_mod(mod_, mod_.spans.inner_span, hir::CRATE_HIR_ID)
197 }
198 hir::OwnerNode::Synthetic => unreachable!(),
199 },
200 }
201
202 let specs = levels.provider.specs;
203
204 #[cfg(debug_assertions)]
205 for (_, v) in specs.specs.iter() {
206 debug_assert!(!v.is_empty());
207 }
208
209 specs
210}
211
212pub struct TopDown {
213 sets: LintLevelSets,
214 cur: LintStackIndex,
215}
216
217pub trait LintLevelsProvider {
218 type LintExpectationId: Copy + Debug + Into<LintExpectationId>;
219
220 fn current_specs(&self) -> &FxIndexMap<LintId, LevelSpec<Self::LintExpectationId>>;
221
222 fn insert(&mut self, id: LintId, level_spec: LevelSpec<Self::LintExpectationId>);
223
224 fn get_lint_level_spec(
225 &self,
226 lint: &'static Lint,
227 sess: &Session,
228 ) -> LevelSpec<Self::LintExpectationId>;
229
230 fn push_expectation(&mut self, id: Self::LintExpectationId, expectation: LintExpectation);
231
232 fn mk_lint_expectation_id(
233 &self,
234 attr_id: AttrId,
235 attr_index: usize,
236 lint_index: u16,
237 ) -> Self::LintExpectationId;
238}
239
240impl LintLevelsProvider for TopDown {
241 type LintExpectationId = UnstableLintExpectationId;
242
243 fn current_specs(&self) -> &FxIndexMap<LintId, UnstableLevelSpec> {
244 &self.sets.list[self.cur].specs
245 }
246
247 fn insert(&mut self, id: LintId, level_spec: UnstableLevelSpec) {
248 self.sets.list[self.cur].specs.insert(id, level_spec);
249 }
250
251 fn get_lint_level_spec(&self, lint: &'static Lint, sess: &Session) -> UnstableLevelSpec {
252 self.sets.get_lint_level_spec(lint, self.cur, Some(self.current_specs()), sess)
253 }
254
255 fn push_expectation(&mut self, _: Self::LintExpectationId, _: LintExpectation) {}
256
257 fn mk_lint_expectation_id(
258 &self,
259 attr_id: AttrId,
260 _attr_index: usize,
261 lint_index: u16,
262 ) -> Self::LintExpectationId {
263 UnstableLintExpectationId { attr_id, lint_index }
264 }
265}
266
267struct LintLevelQueryMap<'tcx> {
268 tcx: TyCtxt<'tcx>,
269 cur: HirId,
270 specs: ShallowLintLevelMap,
271 empty: FxIndexMap<LintId, StableLevelSpec>,
273 attrs: &'tcx hir::AttributeMap<'tcx>,
274}
275
276impl LintLevelsProvider for LintLevelQueryMap<'_> {
277 type LintExpectationId = StableLintExpectationId;
278
279 fn current_specs(&self) -> &FxIndexMap<LintId, StableLevelSpec> {
280 self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
281 }
282
283 fn insert(&mut self, id: LintId, level_spec: StableLevelSpec) {
284 self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, level_spec);
285 }
286
287 fn get_lint_level_spec(&self, lint: &'static Lint, _: &Session) -> StableLevelSpec {
288 self.specs.lint_level_spec_at_node(self.tcx, LintId::of(lint), self.cur)
289 }
290
291 fn push_expectation(&mut self, id: Self::LintExpectationId, expectation: LintExpectation) {
292 self.specs.expectations.push((id, expectation))
293 }
294
295 fn mk_lint_expectation_id(
296 &self,
297 _attr_id: AttrId,
298 attr_index: usize,
299 lint_index: u16,
300 ) -> Self::LintExpectationId {
301 let attr_index = attr_index.try_into().unwrap();
302 StableLintExpectationId { hir_id: self.cur, attr_index, lint_index }
303 }
304}
305
306impl<'tcx> LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
307 fn add_id(&mut self, hir_id: HirId) {
308 self.provider.cur = hir_id;
309 self.add(self.provider.attrs.get(hir_id.local_id), hir_id == hir::CRATE_HIR_ID);
310 }
311}
312
313impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
314 type NestedFilter = nested_filter::OnlyBodies;
315
316 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
317 self.provider.tcx
318 }
319
320 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
321 self.add_id(param.hir_id);
322 intravisit::walk_param(self, param);
323 }
324
325 fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
326 self.add_id(it.hir_id());
327 intravisit::walk_item(self, it);
328 }
329
330 fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
331 self.add_id(it.hir_id());
332 intravisit::walk_foreign_item(self, it);
333 }
334
335 fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
336 self.add_id(s.hir_id);
337 intravisit::walk_stmt(self, s);
338 }
339
340 fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
341 self.add_id(e.hir_id);
342 intravisit::walk_expr(self, e);
343 }
344
345 fn visit_pat_field(&mut self, f: &'tcx hir::PatField<'tcx>) -> Self::Result {
346 self.add_id(f.hir_id);
347 intravisit::walk_pat_field(self, f);
348 }
349
350 fn visit_expr_field(&mut self, f: &'tcx hir::ExprField<'tcx>) {
351 self.add_id(f.hir_id);
352 intravisit::walk_expr_field(self, f);
353 }
354
355 fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
356 self.add_id(s.hir_id);
357 intravisit::walk_field_def(self, s);
358 }
359
360 fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
361 self.add_id(v.hir_id);
362 intravisit::walk_variant(self, v);
363 }
364
365 fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) {
366 self.add_id(l.hir_id);
367 intravisit::walk_local(self, l);
368 }
369
370 fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
371 self.add_id(a.hir_id);
372 intravisit::walk_arm(self, a);
373 }
374
375 fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
376 self.add_id(trait_item.hir_id());
377 intravisit::walk_trait_item(self, trait_item);
378 }
379
380 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
381 self.add_id(impl_item.hir_id());
382 intravisit::walk_impl_item(self, impl_item);
383 }
384}
385
386pub struct LintLevelsBuilder<'s, P> {
387 sess: &'s Session,
388 features: &'s Features,
389 provider: P,
390 lint_added_lints: bool,
391 store: &'s LintStore,
392 registered_tools: &'s RegisteredTools,
393}
394
395pub(crate) struct BuilderPush {
396 prev: LintStackIndex,
397}
398
399impl<'s> LintLevelsBuilder<'s, TopDown> {
400 pub(crate) fn new(
401 sess: &'s Session,
402 features: &'s Features,
403 lint_added_lints: bool,
404 store: &'s LintStore,
405 registered_tools: &'s RegisteredTools,
406 ) -> Self {
407 let mut builder = LintLevelsBuilder {
408 sess,
409 features,
410 provider: TopDown { sets: LintLevelSets::new(), cur: COMMAND_LINE },
411 lint_added_lints,
412 store,
413 registered_tools,
414 };
415 builder.process_command_line();
416 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);
417 builder
418 }
419
420 pub fn crate_root(
421 sess: &'s Session,
422 features: &'s Features,
423 lint_added_lints: bool,
424 store: &'s LintStore,
425 registered_tools: &'s RegisteredTools,
426 crate_attrs: &[ast::Attribute],
427 ) -> Self {
428 let mut builder = Self::new(sess, features, lint_added_lints, store, registered_tools);
429 builder.add(crate_attrs, true);
430 builder
431 }
432
433 fn process_command_line(&mut self) {
434 self.provider.cur = self
435 .provider
436 .sets
437 .list
438 .push(LintSet { specs: FxIndexMap::default(), parent: COMMAND_LINE });
439 self.add_command_line();
440 }
441
442 pub(crate) fn push(&mut self, attrs: &[ast::Attribute], is_crate_node: bool) -> BuilderPush {
457 let prev = self.provider.cur;
458 self.provider.cur =
459 self.provider.sets.list.push(LintSet { specs: FxIndexMap::default(), parent: prev });
460
461 self.add(attrs, is_crate_node);
462
463 if self.provider.current_specs().is_empty() {
464 self.provider.sets.list.pop();
465 self.provider.cur = prev;
466 }
467
468 BuilderPush { prev }
469 }
470
471 pub(crate) fn pop(&mut self, push: BuilderPush) {
473 self.provider.cur = push.prev;
474 std::mem::forget(push);
475 }
476}
477
478#[cfg(debug_assertions)]
479impl Drop for BuilderPush {
480 fn drop(&mut self) {
481 {
::core::panicking::panic_fmt(format_args!("Found a `push` without a `pop`."));
};panic!("Found a `push` without a `pop`.");
482 }
483}
484
485impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P>
486where
487 LevelSpec<P::LintExpectationId>: Into<LevelSpec>,
488{
489 pub(crate) fn sess(&self) -> &Session {
490 self.sess
491 }
492
493 pub(crate) fn features(&self) -> &Features {
494 self.features
495 }
496
497 fn add_command_line(&mut self) {
498 for &(ref lint_name, level) in &self.sess.opts.lint_opts {
499 let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
501 if lint_name_only == crate::WARNINGS.name_lower() && #[allow(non_exhaustive_omitted_patterns)] match level {
Level::ForceWarn => true,
_ => false,
}matches!(level, Level::ForceWarn) {
502 self.sess
503 .dcx()
504 .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
505 }
506 match self.store.check_lint_name(lint_name_only, tool_name, self.registered_tools) {
507 CheckLintNameResult::Renamed(ref replace) => {
508 let name = lint_name.as_str();
509 let suggestion = RenamedLintSuggestion::WithoutSpan { replace };
510 let requested_level = RequestedLevel { level, lint_name };
511 let lint =
512 RenamedLintFromCommandLine { name, replace, suggestion, requested_level };
513 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
514 }
515 CheckLintNameResult::Removed(ref reason) => {
516 let name = lint_name.as_str();
517 let requested_level = RequestedLevel { level, lint_name };
518 let lint = RemovedLintFromCommandLine { name, reason, requested_level };
519 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
520 }
521 CheckLintNameResult::NoLint(suggestion) => {
522 let name = lint_name.clone();
523 let suggestion = suggestion.map(|(replace, from_rustc)| {
524 UnknownLintSuggestion::WithoutSpan { replace, from_rustc }
525 });
526 let requested_level = RequestedLevel { level, lint_name };
527 let lint = UnknownLintFromCommandLine { name, suggestion, requested_level };
528 self.emit_lint(UNKNOWN_LINTS, lint);
529 }
530 CheckLintNameResult::Tool(_, Some(ref replace)) => {
531 let name = lint_name.clone();
532 let requested_level = RequestedLevel { level, lint_name };
533 let lint = DeprecatedLintNameFromCommandLine { name, replace, requested_level };
534 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
535 }
536 CheckLintNameResult::NoTool => {
537 self.sess.dcx().emit_err(CheckNameUnknownTool {
538 tool_name: tool_name.unwrap(),
539 sub: RequestedLevel { level, lint_name },
540 });
541 }
542 _ => {}
543 };
544
545 let lint_flag_val = Symbol::intern(lint_name);
546
547 let Some(ids) = self.store.find_lints(lint_name) else {
548 continue;
550 };
551 for &id in ids {
552 if let Some(level_spec) = self.provider.current_specs().get(&id)
554 && #[allow(non_exhaustive_omitted_patterns)] match level_spec.level() {
Level::ForceWarn | Level::Forbid => true,
_ => false,
}matches!(level_spec.level(), Level::ForceWarn | Level::Forbid)
555 {
556 continue;
557 }
558
559 if self.check_gated_lint(id, DUMMY_SP, true) {
560 let src = LintLevelSource::CommandLine(lint_flag_val, level);
561 self.provider.insert(id, LevelSpec::new(level, None, src));
562 }
563 }
564 }
565 }
566
567 fn insert_spec(&mut self, id: LintId, level_spec: LevelSpec<P::LintExpectationId>) {
571 let level = level_spec.level();
572 let lint_id = level_spec.lint_id();
573 let src = level_spec.src;
574
575 let old_level_spec = self.provider.get_lint_level_spec(id.lint, self.sess);
576 let old_level = old_level_spec.level();
577 let old_src = old_level_spec.src;
578
579 if self.lint_added_lints && level == Level::Deny && old_level == Level::Forbid {
586 return;
588 } else if self.lint_added_lints && level != Level::Forbid && old_level == Level::Forbid {
589 let fcw_warning = match old_src {
596 LintLevelSource::Default => false,
597 LintLevelSource::Node { name, .. } => self.store.is_lint_group(name),
598 LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol),
599 };
600 {
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:600",
"rustc_lint::levels", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/levels.rs"),
::tracing_core::__macro_support::Option::Some(600u32),
::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.provider.current_specs(), old_src,
id.lint.name_lower()) as &dyn Value))])
});
} else { ; }
};debug!(
601 "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}",
602 fcw_warning,
603 self.provider.current_specs(),
604 old_src,
605 id.lint.name_lower(),
606 );
607 let sub = match old_src {
608 LintLevelSource::Default => {
609 OverruledAttributeSub::DefaultSource { id: id.to_string() }
610 }
611 LintLevelSource::Node { span, reason, .. } => {
612 OverruledAttributeSub::NodeSource { span, reason }
613 }
614 LintLevelSource::CommandLine(name, _) => {
615 OverruledAttributeSub::CommandLineSource { id: name }
616 }
617 };
618 if !fcw_warning {
619 self.sess.dcx().emit_err(OverruledAttribute {
620 span: src.span(),
621 overruled: src.span(),
622 lint_level: level.as_str(),
623 lint_source: src.name(),
624 sub,
625 });
626 } else {
627 self.emit_span_lint(
628 FORBIDDEN_LINT_GROUPS,
629 src.span().into(),
630 OverruledAttributeLint {
631 overruled: src.span(),
632 lint_level: level.as_str(),
633 lint_source: src.name(),
634 sub,
635 },
636 );
637 }
638
639 if !fcw_warning {
643 return;
644 }
645 }
646
647 if let Level::Expect = level
651 && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS)
652 {
653 return;
654 }
655
656 match (old_level, level) {
657 (Level::ForceWarn, Level::Expect) => {
659 self.provider.insert(id, LevelSpec::new(Level::ForceWarn, lint_id, old_src))
660 }
661 (Level::ForceWarn, _) => {
663 self.provider.insert(id, LevelSpec::new(Level::ForceWarn, None, old_src))
664 }
665 _ => self.provider.insert(id, LevelSpec::new(level, lint_id, src)),
667 };
668 }
669
670 fn add(&mut self, attrs: &[impl AttributeExt], is_crate_node: bool) {
671 let sess = self.sess;
672 for (attr_index, attr) in attrs.iter().enumerate() {
673 if attr.is_automatically_derived_attr() {
674 self.provider.insert(
675 LintId::of(SINGLE_USE_LIFETIMES),
676 LevelSpec::new(Level::Allow, None, LintLevelSource::Default),
677 );
678 continue;
679 }
680
681 if attr.is_doc_hidden() {
683 self.provider.insert(
684 LintId::of(MISSING_DOCS),
685 LevelSpec::new(Level::Allow, None, LintLevelSource::Default),
686 );
687 continue;
688 }
689
690 let level = match Level::from_opt_symbol(attr.name()) {
691 None => continue,
692 Some(level) => level,
693 };
694
695 let Some(mut metas) = attr.meta_item_list() else { continue };
696
697 let Some(tail_li) = metas.last() else {
699 continue;
701 };
702
703 let mut reason = None;
706 if let Some(item) = tail_li.meta_item() {
707 match item.kind {
708 ast::MetaItemKind::Word => {} ast::MetaItemKind::NameValue(ref name_value) => {
710 if item.path == sym::reason {
711 if let ast::LitKind::Str(rationale, _) = name_value.kind {
712 reason = Some(rationale);
713 } else {
714 sess.dcx().emit_err(MalformedAttribute {
715 span: name_value.span,
716 sub: MalformedAttributeSub::ReasonMustBeStringLiteral(
717 name_value.span,
718 ),
719 });
720 }
721 metas.pop().unwrap();
723 } else {
724 sess.dcx().emit_err(MalformedAttribute {
725 span: item.span,
726 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
727 });
728 }
729 }
730 ast::MetaItemKind::List(_) => {
731 sess.dcx().emit_err(MalformedAttribute {
732 span: item.span,
733 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
734 });
735 }
736 }
737 }
738
739 for (lint_index, li) in metas.iter_mut().enumerate() {
740 let lint_id = (level == Level::Expect).then(|| {
743 self.provider.mk_lint_expectation_id(attr.id(), attr_index, lint_index as u16)
744 });
745
746 let sp = li.span();
747 let meta_item = match li {
748 ast::MetaItemInner::MetaItem(meta_item) if meta_item.is_word() => meta_item,
749 _ => {
750 let sub = if let Some(item) = li.meta_item()
751 && let ast::MetaItemKind::NameValue(_) = item.kind
752 && item.path == sym::reason
753 {
754 MalformedAttributeSub::ReasonMustComeLast(sp)
755 } else {
756 MalformedAttributeSub::BadAttributeArgument(sp)
757 };
758
759 sess.dcx().emit_err(MalformedAttribute { span: sp, sub });
760 continue;
761 }
762 };
763 let tool_ident = if meta_item.path.segments.len() > 1 {
764 Some(meta_item.path.segments.remove(0).ident)
765 } else {
766 None
767 };
768 let tool_name = tool_ident.map(|ident| ident.name);
769 let name = pprust::path_to_string(&meta_item.path);
770 let lint_result =
771 self.store.check_lint_name(&name, tool_name, self.registered_tools);
772
773 let (ids, name) = match lint_result {
774 CheckLintNameResult::Ok(ids) => {
775 let name =
776 meta_item.path.segments.last().expect("empty lint name").ident.name;
777 (ids, name)
778 }
779
780 CheckLintNameResult::Tool(ids, new_lint_name) => {
781 let name = match new_lint_name {
782 None => {
783 let complete_name =
784 &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}",
tool_ident.unwrap().name, name))
})format!("{}::{}", tool_ident.unwrap().name, name);
785 Symbol::intern(complete_name)
786 }
787 Some(new_lint_name) => {
788 self.emit_span_lint(
789 builtin::RENAMED_AND_REMOVED_LINTS,
790 sp.into(),
791 DeprecatedLintName {
792 name,
793 suggestion: sp,
794 replace: &new_lint_name,
795 },
796 );
797 Symbol::intern(&new_lint_name)
798 }
799 };
800 (ids, name)
801 }
802
803 CheckLintNameResult::MissingTool => {
804 continue;
809 }
810
811 CheckLintNameResult::NoTool => {
812 sess.dcx().emit_err(UnknownToolInScopedLint {
813 span: tool_ident.map(|ident| ident.span),
814 tool_name: tool_name.unwrap(),
815 lint_name: pprust::path_to_string(&meta_item.path),
816 is_nightly_build: sess.is_nightly_build(),
817 });
818 continue;
819 }
820
821 CheckLintNameResult::Renamed(ref replace) => {
822 if self.lint_added_lints {
823 let suggestion =
824 RenamedLintSuggestion::WithSpan { suggestion: sp, replace };
825 let name =
826 tool_ident.map(|tool| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}", tool, name))
})format!("{tool}::{name}")).unwrap_or(name);
827 self.emit_span_lint(
828 RENAMED_AND_REMOVED_LINTS,
829 sp.into(),
830 RenamedLint { name: name.as_str(), replace, suggestion },
831 );
832 }
833
834 let CheckLintNameResult::Ok(ids) =
840 self.store.check_lint_name(replace, None, self.registered_tools)
841 else {
842 {
::core::panicking::panic_fmt(format_args!("renamed lint does not exist: {0}",
replace));
};panic!("renamed lint does not exist: {replace}");
843 };
844
845 (ids, Symbol::intern(&replace))
846 }
847
848 CheckLintNameResult::Removed(ref reason) => {
849 if self.lint_added_lints {
850 let name =
851 tool_ident.map(|tool| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}", tool, name))
})format!("{tool}::{name}")).unwrap_or(name);
852 self.emit_span_lint(
853 RENAMED_AND_REMOVED_LINTS,
854 sp.into(),
855 RemovedLint { name: name.as_str(), reason },
856 );
857 }
858 continue;
859 }
860
861 CheckLintNameResult::NoLint(suggestion) => {
862 if self.lint_added_lints {
863 let name =
864 tool_ident.map(|tool| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}", tool, name))
})format!("{tool}::{name}")).unwrap_or(name);
865 let suggestion = suggestion.map(|(replace, from_rustc)| {
866 UnknownLintSuggestion::WithSpan {
867 suggestion: sp,
868 replace,
869 from_rustc,
870 }
871 });
872 self.emit_span_lint(
873 UNKNOWN_LINTS,
874 sp.into(),
875 UnknownLint { name, suggestion },
876 );
877 }
878 continue;
879 }
880 };
881
882 let src = LintLevelSource::Node { name, span: sp, reason };
883 for &id in ids {
884 if self.check_gated_lint(id, sp, false) {
885 self.insert_spec(id, LevelSpec::new(level, lint_id, src));
886 }
887 }
888
889 if let (Level::Expect, Some(expect_id)) = (level, lint_id) {
895 let is_unfulfilled_lint_expectations = match ids {
899 [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS),
900 _ => false,
901 };
902 self.provider.push_expectation(
903 expect_id,
904 LintExpectation::new(
905 reason,
906 sp,
907 is_unfulfilled_lint_expectations,
908 tool_name,
909 ),
910 );
911 }
912 }
913 }
914
915 if self.lint_added_lints && !is_crate_node {
916 for (id, level_spec) in self.provider.current_specs().iter() {
917 if !id.lint.crate_level_only {
918 continue;
919 }
920
921 let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } =
922 level_spec.src
923 else {
924 continue;
925 };
926
927 self.emit_span_lint(
928 UNUSED_ATTRIBUTES,
929 lint_attr_span.into(),
930 IgnoredUnlessCrateSpecified {
931 level: level_spec.level().as_str(),
932 name: lint_attr_name,
933 },
934 );
935 break;
937 }
938 }
939 }
940
941 #[track_caller]
945 fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool {
946 let feature = if let Some(feature) = lint_id.lint.feature_gate
947 && !self.features.enabled(feature)
948 && !span.allows_unstable(feature)
949 {
950 feature
952 } else {
953 return true;
955 };
956
957 struct UnknownLint<'a> {
958 sess: &'a Session,
959 lint_id: LintId,
960 feature: Symbol,
961 lint_from_cli: bool,
962 }
963
964 impl<'a, 'b> Diagnostic<'a, ()> for UnknownLint<'b> {
965 fn into_diag(
966 self,
967 dcx: DiagCtxtHandle<'a>,
968 level: rustc_errors::Level,
969 ) -> Diag<'a, ()> {
970 let Self { sess, lint_id, feature, lint_from_cli } = self;
971 let mut lint = Diag::new(dcx, level, rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("unknown lint: `{$name}`"))msg!("unknown lint: `{$name}`"))
972 .with_arg("name", lint_id.lint.name_lower())
973 .with_note(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("the `{$name}` lint is unstable"))msg!("the `{$name}` lint is unstable"));
974 rustc_session::errors::add_feature_diagnostics_for_issue(
975 &mut lint,
976 sess,
977 feature,
978 GateIssue::Language,
979 lint_from_cli,
980 None,
981 );
982 lint
983 }
984 }
985
986 if self.lint_added_lints {
987 let lint = builtin::UNKNOWN_LINTS;
988 let level_spec = self.lint_level_spec(builtin::UNKNOWN_LINTS);
989 emit_lint_base(
990 self.sess,
991 lint,
992 level_spec,
993 Some(span.into()),
994 UnknownLint { sess: &self.sess, lint_id, feature, lint_from_cli },
995 );
996 }
997
998 false
999 }
1000
1001 pub fn lint_level_spec(&self, lint: &'static Lint) -> LevelSpec<P::LintExpectationId> {
1003 self.provider.get_lint_level_spec(lint, self.sess)
1004 }
1005
1006 #[track_caller]
1009 pub(crate) fn opt_span_lint(
1010 &self,
1011 lint: &'static Lint,
1012 span: Option<MultiSpan>,
1013 decorator: impl for<'a> Diagnostic<'a, ()>,
1014 ) {
1015 let level_spec = self.lint_level_spec(lint);
1016 emit_lint_base(self.sess, lint, level_spec, span, decorator)
1017 }
1018
1019 #[track_caller]
1020 pub fn emit_span_lint(
1021 &self,
1022 lint: &'static Lint,
1023 span: MultiSpan,
1024 decorator: impl for<'a> Diagnostic<'a, ()>,
1025 ) {
1026 let level_spec = self.lint_level_spec(lint);
1027 emit_lint_base(self.sess, lint, level_spec, Some(span), decorator);
1028 }
1029
1030 #[track_caller]
1031 pub fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> Diagnostic<'a, ()>) {
1032 let level_spec = self.lint_level_spec(lint);
1033 emit_lint_base(self.sess, lint, level_spec, None, decorator);
1034 }
1035}
1036
1037pub(crate) fn provide(providers: &mut Providers) {
1038 *providers = Providers { shallow_lint_levels_on, lints_that_dont_need_to_run, ..*providers };
1039}
1040
1041pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
1042 match lint_name.split_once("::") {
1043 Some((tool_name, lint_name)) => {
1044 let tool_name = Symbol::intern(tool_name);
1045
1046 (Some(tool_name), lint_name)
1047 }
1048 None => (None, lint_name),
1049 }
1050}