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::diagnostics::{
35 CheckNameUnknownTool, DeprecatedLintName, DeprecatedLintNameFromCommandLine,
36 IgnoredUnlessCrateSpecified, MalformedAttribute, MalformedAttributeSub, OverruledAttribute,
37 OverruledAttributeLint, OverruledAttributeSub, RemovedLint, RemovedLintFromCommandLine,
38 RenamedLint, RenamedLintFromCommandLine, RenamedLintSuggestion, RequestedLevel, UnknownLint,
39 UnknownLintFromCommandLine, UnknownLintSuggestion, UnknownToolInScopedLint, UnsupportedGroup,
40};
41use crate::late::unerased_lint_store;
42
43#[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)]
47struct LintLevelSets {
48 list: IndexVec<LintStackIndex, LintSet>,
50}
51
52impl ::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! {
53 struct LintStackIndex {
54 const COMMAND_LINE = 0;
55 }
56}
57
58#[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)]
67struct LintSet {
68 specs: FxIndexMap<LintId, UnstableLevelSpec>,
69 parent: LintStackIndex,
70}
71
72impl LintLevelSets {
73 fn new() -> Self {
74 LintLevelSets { list: IndexVec::new() }
75 }
76
77 fn get_lint_level_spec(
78 &self,
79 lint: &'static Lint,
80 idx: LintStackIndex,
81 aux: Option<&FxIndexMap<LintId, UnstableLevelSpec>>,
82 sess: &Session,
83 ) -> UnstableLevelSpec {
84 reveal_actual_level_spec(sess, LintId::of(lint), |id| {
85 self.raw_lint_level_spec(id, idx, aux)
86 })
87 }
88
89 fn raw_lint_level_spec(
90 &self,
91 id: LintId,
92 mut idx: LintStackIndex,
93 aux: Option<&FxIndexMap<LintId, UnstableLevelSpec>>,
94 ) -> Option<UnstableLevelSpec> {
95 if let Some(specs) = aux
96 && let Some(level_spec) = specs.get(&id)
97 {
98 return Some(*level_spec);
99 }
100
101 loop {
102 let LintSet { ref specs, parent } = self.list[idx];
103 if let Some(level_spec) = specs.get(&id) {
104 return Some(*level_spec);
105 }
106 if idx == COMMAND_LINE {
107 return None;
108 }
109 idx = parent;
110 }
111 }
112}
113
114fn skippable_lints(tcx: TyCtxt<'_>, (): ()) -> UnordSet<LintId> {
115 let store = unerased_lint_store(&tcx.sess);
116 let root_map = tcx.shallow_lint_levels_on(hir::CRATE_OWNER_ID);
117
118 let mut skippable: FxHashSet<LintId> = store
119 .get_lints()
120 .into_iter()
121 .filter(|lint| {
122 let has_future_breakage =
124 lint.future_incompatible.is_some_and(|fut| fut.report_in_deps);
125 !has_future_breakage && !lint.eval_always
126 })
127 .filter(|lint| {
128 let level_spec =
129 root_map.lint_level_spec_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID);
130 level_spec.is_allow()
132 || (#[allow(non_exhaustive_omitted_patterns)] match level_spec.src {
LintLevelSource::Default => true,
_ => false,
}matches!(level_spec.src, LintLevelSource::Default)
133 && lint.default_level(tcx.sess.edition()) == Level::Allow)
134 })
135 .map(|lint| LintId::of(*lint))
136 .collect();
137
138 for owner in tcx.hir_crate_items(()).owners() {
139 let map = tcx.shallow_lint_levels_on(owner);
140
141 for (_, specs) in map.specs.iter() {
143 for (lint, level_spec) in specs.iter() {
144 if !level_spec.is_allow() {
145 skippable.remove(lint);
146 }
147 }
148 }
149 }
150
151 skippable.into()
152}
153
154x;#[instrument(level = "trace", skip(tcx), ret)]
155fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap {
156 let store = unerased_lint_store(tcx.sess);
157 let attrs = tcx.hir_attr_map(owner);
158
159 let mut levels = LintLevelsBuilder {
160 sess: tcx.sess,
161 features: tcx.features(),
162 provider: LintLevelQueryMap {
163 tcx,
164 cur: owner.into(),
165 specs: ShallowLintLevelMap::default(),
166 empty: FxIndexMap::default(),
167 attrs,
168 },
169 lint_added_lints: false,
170 store,
171 registered_lint_tools: tcx.registered_lint_tools(()),
172 };
173
174 if owner == hir::CRATE_OWNER_ID {
175 levels.add_command_line();
176 }
177
178 match attrs.map.range(..) {
179 [] => {}
181 &[(local_id, _)] => levels.add_id(HirId { owner, local_id }),
183 _ => match tcx.hir_owner_node(owner) {
187 hir::OwnerNode::Item(item) => levels.visit_item(item),
188 hir::OwnerNode::ForeignItem(item) => levels.visit_foreign_item(item),
189 hir::OwnerNode::TraitItem(item) => levels.visit_trait_item(item),
190 hir::OwnerNode::ImplItem(item) => levels.visit_impl_item(item),
191 hir::OwnerNode::Crate(mod_) => {
192 levels.add_id(hir::CRATE_HIR_ID);
193 levels.visit_mod(mod_, mod_.spans.inner_span, hir::CRATE_HIR_ID)
194 }
195 hir::OwnerNode::Synthetic => unreachable!(),
196 },
197 }
198
199 let specs = levels.provider.specs;
200
201 #[cfg(debug_assertions)]
202 for (_, v) in specs.specs.iter() {
203 debug_assert!(!v.is_empty());
204 }
205
206 specs
207}
208
209pub struct TopDown {
210 sets: LintLevelSets,
211 cur: LintStackIndex,
212}
213
214pub trait LintLevelsProvider {
215 type LintExpectationId: Copy + Debug + Into<LintExpectationId>;
216
217 fn current_specs(&self) -> &FxIndexMap<LintId, LevelSpec<Self::LintExpectationId>>;
218
219 fn insert(&mut self, id: LintId, level_spec: LevelSpec<Self::LintExpectationId>);
220
221 fn get_lint_level_spec(
222 &self,
223 lint: &'static Lint,
224 sess: &Session,
225 ) -> LevelSpec<Self::LintExpectationId>;
226
227 fn push_expectation(&mut self, id: Self::LintExpectationId, expectation: LintExpectation);
228
229 fn mk_lint_expectation_id(
230 &self,
231 attr_id: AttrId,
232 attr_index: usize,
233 lint_index: u16,
234 ) -> Self::LintExpectationId;
235}
236
237impl LintLevelsProvider for TopDown {
238 type LintExpectationId = UnstableLintExpectationId;
239
240 fn current_specs(&self) -> &FxIndexMap<LintId, UnstableLevelSpec> {
241 &self.sets.list[self.cur].specs
242 }
243
244 fn insert(&mut self, id: LintId, level_spec: UnstableLevelSpec) {
245 self.sets.list[self.cur].specs.insert(id, level_spec);
246 }
247
248 fn get_lint_level_spec(&self, lint: &'static Lint, sess: &Session) -> UnstableLevelSpec {
249 self.sets.get_lint_level_spec(lint, self.cur, Some(self.current_specs()), sess)
250 }
251
252 fn push_expectation(&mut self, _: Self::LintExpectationId, _: LintExpectation) {}
253
254 fn mk_lint_expectation_id(
255 &self,
256 attr_id: AttrId,
257 _attr_index: usize,
258 lint_index: u16,
259 ) -> Self::LintExpectationId {
260 UnstableLintExpectationId { attr_id, lint_index }
261 }
262}
263
264struct LintLevelQueryMap<'tcx> {
265 tcx: TyCtxt<'tcx>,
266 cur: HirId,
267 specs: ShallowLintLevelMap,
268 empty: FxIndexMap<LintId, StableLevelSpec>,
270 attrs: &'tcx hir::AttributeMap<'tcx>,
271}
272
273impl LintLevelsProvider for LintLevelQueryMap<'_> {
274 type LintExpectationId = StableLintExpectationId;
275
276 fn current_specs(&self) -> &FxIndexMap<LintId, StableLevelSpec> {
277 self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
278 }
279
280 fn insert(&mut self, id: LintId, level_spec: StableLevelSpec) {
281 self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, level_spec);
282 }
283
284 fn get_lint_level_spec(&self, lint: &'static Lint, _: &Session) -> StableLevelSpec {
285 self.specs.lint_level_spec_at_node(self.tcx, LintId::of(lint), self.cur)
286 }
287
288 fn push_expectation(&mut self, id: Self::LintExpectationId, expectation: LintExpectation) {
289 self.specs.expectations.push((id, expectation))
290 }
291
292 fn mk_lint_expectation_id(
293 &self,
294 _attr_id: AttrId,
295 attr_index: usize,
296 lint_index: u16,
297 ) -> Self::LintExpectationId {
298 let attr_index = attr_index.try_into().unwrap();
299 StableLintExpectationId { hir_id: self.cur, attr_index, lint_index }
300 }
301}
302
303impl<'tcx> LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
304 fn add_id(&mut self, hir_id: HirId) {
305 self.provider.cur = hir_id;
306 self.add(self.provider.attrs.get(hir_id.local_id), hir_id == hir::CRATE_HIR_ID);
307 }
308}
309
310impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
311 type NestedFilter = nested_filter::OnlyBodies;
312
313 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
314 self.provider.tcx
315 }
316
317 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
318 self.add_id(param.hir_id);
319 intravisit::walk_param(self, param);
320 }
321
322 fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
323 self.add_id(it.hir_id());
324 intravisit::walk_item(self, it);
325 }
326
327 fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
328 self.add_id(it.hir_id());
329 intravisit::walk_foreign_item(self, it);
330 }
331
332 fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
333 self.add_id(s.hir_id);
334 intravisit::walk_stmt(self, s);
335 }
336
337 fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
338 self.add_id(e.hir_id);
339 intravisit::walk_expr(self, e);
340 }
341
342 fn visit_pat_field(&mut self, f: &'tcx hir::PatField<'tcx>) -> Self::Result {
343 self.add_id(f.hir_id);
344 intravisit::walk_pat_field(self, f);
345 }
346
347 fn visit_expr_field(&mut self, f: &'tcx hir::ExprField<'tcx>) {
348 self.add_id(f.hir_id);
349 intravisit::walk_expr_field(self, f);
350 }
351
352 fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
353 self.add_id(s.hir_id);
354 intravisit::walk_field_def(self, s);
355 }
356
357 fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
358 self.add_id(v.hir_id);
359 intravisit::walk_variant(self, v);
360 }
361
362 fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) {
363 self.add_id(l.hir_id);
364 intravisit::walk_local(self, l);
365 }
366
367 fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
368 self.add_id(a.hir_id);
369 intravisit::walk_arm(self, a);
370 }
371
372 fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
373 self.add_id(trait_item.hir_id());
374 intravisit::walk_trait_item(self, trait_item);
375 }
376
377 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
378 self.add_id(impl_item.hir_id());
379 intravisit::walk_impl_item(self, impl_item);
380 }
381}
382
383pub struct LintLevelsBuilder<'s, P> {
384 sess: &'s Session,
385 features: &'s Features,
386 provider: P,
387 lint_added_lints: bool,
388 store: &'s LintStore,
389 registered_lint_tools: &'s RegisteredTools,
390}
391
392pub(crate) struct BuilderPush {
393 prev: LintStackIndex,
394}
395
396impl<'s> LintLevelsBuilder<'s, TopDown> {
397 pub(crate) fn new(
398 sess: &'s Session,
399 features: &'s Features,
400 lint_added_lints: bool,
401 store: &'s LintStore,
402 registered_lint_tools: &'s RegisteredTools,
403 ) -> Self {
404 let mut builder = LintLevelsBuilder {
405 sess,
406 features,
407 provider: TopDown { sets: LintLevelSets::new(), cur: COMMAND_LINE },
408 lint_added_lints,
409 store,
410 registered_lint_tools,
411 };
412 builder.process_command_line();
413 {
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);
414 builder
415 }
416
417 pub fn crate_root(
418 sess: &'s Session,
419 features: &'s Features,
420 lint_added_lints: bool,
421 store: &'s LintStore,
422 registered_lint_tools: &'s RegisteredTools,
423 crate_attrs: &[ast::Attribute],
424 ) -> Self {
425 let mut builder = Self::new(sess, features, lint_added_lints, store, registered_lint_tools);
426 builder.add(crate_attrs, true);
427 builder
428 }
429
430 fn process_command_line(&mut self) {
431 self.provider.cur = self
432 .provider
433 .sets
434 .list
435 .push(LintSet { specs: FxIndexMap::default(), parent: COMMAND_LINE });
436 self.add_command_line();
437 }
438
439 pub(crate) fn push(&mut self, attrs: &[ast::Attribute], is_crate_node: bool) -> BuilderPush {
454 let prev = self.provider.cur;
455 self.provider.cur =
456 self.provider.sets.list.push(LintSet { specs: FxIndexMap::default(), parent: prev });
457
458 self.add(attrs, is_crate_node);
459
460 if self.provider.current_specs().is_empty() {
461 self.provider.sets.list.pop();
462 self.provider.cur = prev;
463 }
464
465 BuilderPush { prev }
466 }
467
468 pub(crate) fn pop(&mut self, push: BuilderPush) {
470 self.provider.cur = push.prev;
471 std::mem::forget(push);
472 }
473}
474
475#[cfg(debug_assertions)]
476impl Drop for BuilderPush {
477 fn drop(&mut self) {
478 {
::core::panicking::panic_fmt(format_args!("Found a `push` without a `pop`."));
};panic!("Found a `push` without a `pop`.");
479 }
480}
481
482impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P>
483where
484 LevelSpec<P::LintExpectationId>: Into<LevelSpec>,
485{
486 pub(crate) fn sess(&self) -> &Session {
487 self.sess
488 }
489
490 pub(crate) fn features(&self) -> &Features {
491 self.features
492 }
493
494 fn add_command_line(&mut self) {
495 for &(ref lint_name, level) in &self.sess.opts.lint_opts {
496 let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
498 if lint_name_only == crate::WARNINGS.name_lower() && #[allow(non_exhaustive_omitted_patterns)] match level {
Level::ForceWarn => true,
_ => false,
}matches!(level, Level::ForceWarn) {
499 self.sess
500 .dcx()
501 .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
502 }
503 match self.store.check_lint_name(lint_name_only, tool_name, self.registered_lint_tools)
504 {
505 CheckLintNameResult::Renamed(ref replace) => {
506 let name = lint_name.as_str();
507 let suggestion = RenamedLintSuggestion::WithoutSpan { replace };
508 let requested_level = RequestedLevel { level, lint_name };
509 let lint =
510 RenamedLintFromCommandLine { name, replace, suggestion, requested_level };
511 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
512 }
513 CheckLintNameResult::Removed(ref reason) => {
514 let name = lint_name.as_str();
515 let requested_level = RequestedLevel { level, lint_name };
516 let lint = RemovedLintFromCommandLine { name, reason, requested_level };
517 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
518 }
519 CheckLintNameResult::NoLint(suggestion) => {
520 let name = lint_name.clone();
521 let suggestion = suggestion.map(|(replace, from_rustc)| {
522 UnknownLintSuggestion::WithoutSpan { replace, from_rustc }
523 });
524 let requested_level = RequestedLevel { level, lint_name };
525 let lint = UnknownLintFromCommandLine { name, suggestion, requested_level };
526 self.emit_lint(UNKNOWN_LINTS, lint);
527 }
528 CheckLintNameResult::Tool(_, Some(ref replace)) => {
529 let name = lint_name.clone();
530 let requested_level = RequestedLevel { level, lint_name };
531 let lint = DeprecatedLintNameFromCommandLine { name, replace, requested_level };
532 self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
533 }
534 CheckLintNameResult::NoTool => {
535 self.sess.dcx().emit_err(CheckNameUnknownTool {
536 tool_name: tool_name.unwrap(),
537 sub: RequestedLevel { level, lint_name },
538 });
539 }
540 _ => {}
541 };
542
543 let lint_flag_val = Symbol::intern(lint_name);
544
545 let Some(ids) = self.store.find_lints(lint_name) else {
546 continue;
548 };
549 for &id in ids {
550 if let Some(level_spec) = self.provider.current_specs().get(&id)
552 && #[allow(non_exhaustive_omitted_patterns)] match level_spec.level() {
Level::ForceWarn | Level::Forbid => true,
_ => false,
}matches!(level_spec.level(), Level::ForceWarn | Level::Forbid)
553 {
554 continue;
555 }
556
557 if self.check_gated_lint(id, DUMMY_SP, true) {
558 let src = LintLevelSource::CommandLine(lint_flag_val, level);
559 self.provider.insert(id, LevelSpec::new(level, None, src));
560 }
561 }
562 }
563 }
564
565 fn insert_spec(&mut self, id: LintId, level_spec: LevelSpec<P::LintExpectationId>) {
569 let level = level_spec.level();
570 let lint_id = level_spec.lint_id();
571 let src = level_spec.src;
572
573 let old_level_spec = self.provider.get_lint_level_spec(id.lint, self.sess);
574 let old_level = old_level_spec.level();
575 let old_src = old_level_spec.src;
576
577 if self.lint_added_lints && level == Level::Deny && old_level == Level::Forbid {
584 return;
586 } else if self.lint_added_lints && level != Level::Forbid && old_level == Level::Forbid {
587 let fcw_warning = match old_src {
594 LintLevelSource::Default => false,
595 LintLevelSource::Node { name, .. } => self.store.is_lint_group(name),
596 LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol),
597 };
598 {
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:598",
"rustc_lint::levels", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/levels.rs"),
::tracing_core::__macro_support::Option::Some(598u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::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 ::tracing::field::Value))])
});
} else { ; }
};debug!(
599 "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}",
600 fcw_warning,
601 self.provider.current_specs(),
602 old_src,
603 id.lint.name_lower(),
604 );
605 let sub = match old_src {
606 LintLevelSource::Default => {
607 OverruledAttributeSub::DefaultSource { id: id.to_string() }
608 }
609 LintLevelSource::Node { span, reason, .. } => {
610 OverruledAttributeSub::NodeSource { span, reason }
611 }
612 LintLevelSource::CommandLine(name, _) => {
613 OverruledAttributeSub::CommandLineSource { id: name }
614 }
615 };
616 if !fcw_warning {
617 self.sess.dcx().emit_err(OverruledAttribute {
618 span: src.span(),
619 overruled: src.span(),
620 lint_level: level.as_str(),
621 lint_source: src.name(),
622 sub,
623 });
624 } else {
625 self.emit_span_lint(
626 FORBIDDEN_LINT_GROUPS,
627 src.span().into(),
628 OverruledAttributeLint {
629 overruled: src.span(),
630 lint_level: level.as_str(),
631 lint_source: src.name(),
632 sub,
633 },
634 );
635 }
636
637 if !fcw_warning {
641 return;
642 }
643 }
644
645 if let Level::Expect = level
649 && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS)
650 {
651 return;
652 }
653
654 match (old_level, level) {
655 (Level::ForceWarn, Level::Expect) => {
657 self.provider.insert(id, LevelSpec::new(Level::ForceWarn, lint_id, old_src))
658 }
659 (Level::ForceWarn, _) => {
661 self.provider.insert(id, LevelSpec::new(Level::ForceWarn, None, old_src))
662 }
663 _ => self.provider.insert(id, LevelSpec::new(level, lint_id, src)),
665 };
666 }
667
668 fn add(&mut self, attrs: &[impl AttributeExt], is_crate_node: bool) {
669 let sess = self.sess;
670 for (attr_index, attr) in attrs.iter().enumerate() {
671 if attr.is_automatically_derived_attr() {
672 self.provider.insert(
673 LintId::of(SINGLE_USE_LIFETIMES),
674 LevelSpec::new(Level::Allow, None, LintLevelSource::Default),
675 );
676 continue;
677 }
678
679 if attr.is_doc_hidden() {
681 self.provider.insert(
682 LintId::of(MISSING_DOCS),
683 LevelSpec::new(Level::Allow, None, LintLevelSource::Default),
684 );
685 continue;
686 }
687
688 let level = match Level::from_opt_symbol(attr.name()) {
689 None => continue,
690 Some(level) => level,
691 };
692
693 let Some(mut metas) = attr.meta_item_list() else { continue };
694
695 let Some(tail_li) = metas.last() else {
697 continue;
699 };
700
701 let mut reason = None;
704 if let Some(item) = tail_li.meta_item() {
705 match item.kind {
706 ast::MetaItemKind::Word => {} ast::MetaItemKind::NameValue(ref name_value) => {
708 if item.path == sym::reason {
709 if let ast::LitKind::Str(rationale, _) = name_value.kind {
710 reason = Some(rationale);
711 } else {
712 sess.dcx().emit_err(MalformedAttribute {
713 span: name_value.span,
714 sub: MalformedAttributeSub::ReasonMustBeStringLiteral(
715 name_value.span,
716 ),
717 });
718 }
719 metas.pop().unwrap();
721 } else {
722 sess.dcx().emit_err(MalformedAttribute {
723 span: item.span,
724 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
725 });
726 }
727 }
728 ast::MetaItemKind::List(_) => {
729 sess.dcx().emit_err(MalformedAttribute {
730 span: item.span,
731 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
732 });
733 }
734 }
735 }
736
737 for (lint_index, li) in metas.iter_mut().enumerate() {
738 let lint_id = (level == Level::Expect).then(|| {
741 self.provider.mk_lint_expectation_id(attr.id(), attr_index, 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_lint_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_lint_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, LevelSpec::new(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, level_spec) in self.provider.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, .. } =
920 level_spec.src
921 else {
922 continue;
923 };
924
925 self.emit_span_lint(
926 UNUSED_ATTRIBUTES,
927 lint_attr_span.into(),
928 IgnoredUnlessCrateSpecified {
929 level: level_spec.level().as_str(),
930 name: lint_attr_name,
931 },
932 );
933 break;
935 }
936 }
937 }
938
939 #[track_caller]
943 fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool {
944 let feature = if let Some(feature) = lint_id.lint.feature_gate
945 && !self.features.enabled(feature)
946 && !span.allows_unstable(feature)
947 {
948 feature
950 } else {
951 return true;
953 };
954
955 struct UnknownLint<'a> {
956 sess: &'a Session,
957 lint_id: LintId,
958 feature: Symbol,
959 lint_from_cli: bool,
960 }
961
962 impl<'a, 'b> Diagnostic<'a, ()> for UnknownLint<'b> {
963 fn into_diag(
964 self,
965 dcx: DiagCtxtHandle<'a>,
966 level: rustc_errors::Level,
967 ) -> Diag<'a, ()> {
968 let Self { sess, lint_id, feature, lint_from_cli } = self;
969 let mut lint = Diag::new(dcx, level, rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("unknown lint: `{$name}`"))msg!("unknown lint: `{$name}`"))
970 .with_arg("name", lint_id.lint.name_lower())
971 .with_note(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("the `{$name}` lint is unstable"))msg!("the `{$name}` lint is unstable"));
972 rustc_session::diagnostics::add_feature_diagnostics_for_issue(
973 &mut lint,
974 sess,
975 feature,
976 GateIssue::Language,
977 lint_from_cli,
978 None,
979 );
980 lint
981 }
982 }
983
984 if self.lint_added_lints {
985 let lint = builtin::UNKNOWN_LINTS;
986 let level_spec = self.lint_level_spec(builtin::UNKNOWN_LINTS);
987 emit_lint_base(
988 self.sess,
989 lint,
990 level_spec,
991 Some(span.into()),
992 UnknownLint { sess: &self.sess, lint_id, feature, lint_from_cli },
993 );
994 }
995
996 false
997 }
998
999 pub fn lint_level_spec(&self, lint: &'static Lint) -> LevelSpec<P::LintExpectationId> {
1001 self.provider.get_lint_level_spec(lint, self.sess)
1002 }
1003
1004 #[track_caller]
1007 pub(crate) fn opt_span_lint(
1008 &self,
1009 lint: &'static Lint,
1010 span: Option<MultiSpan>,
1011 decorator: impl for<'a> Diagnostic<'a, ()>,
1012 ) {
1013 let level_spec = self.lint_level_spec(lint);
1014 emit_lint_base(self.sess, lint, level_spec, span, decorator)
1015 }
1016
1017 #[track_caller]
1018 pub fn emit_span_lint(
1019 &self,
1020 lint: &'static Lint,
1021 span: MultiSpan,
1022 decorator: impl for<'a> Diagnostic<'a, ()>,
1023 ) {
1024 let level_spec = self.lint_level_spec(lint);
1025 emit_lint_base(self.sess, lint, level_spec, Some(span), decorator);
1026 }
1027
1028 #[track_caller]
1029 pub fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> Diagnostic<'a, ()>) {
1030 let level_spec = self.lint_level_spec(lint);
1031 emit_lint_base(self.sess, lint, level_spec, None, decorator);
1032 }
1033}
1034
1035pub(crate) fn provide(providers: &mut Providers) {
1036 *providers = Providers { shallow_lint_levels_on, skippable_lints, ..*providers };
1037}
1038
1039pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
1040 match lint_name.split_once("::") {
1041 Some((tool_name, lint_name)) => {
1042 let tool_name = Symbol::intern(tool_name);
1043
1044 (Some(tool_name), lint_name)
1045 }
1046 None => (None, lint_name),
1047 }
1048}