1 use crate::{
2 context::{CheckLintNameResult, LintStore},
3 fluent_generated as fluent,
4 late::unerased_lint_store,
5 lints::{
6 DeprecatedLintName, IgnoredUnlessCrateSpecified, OverruledAttributeLint,
7 RenamedOrRemovedLint, RenamedOrRemovedLintSuggestion, UnknownLint, UnknownLintSuggestion,
8 },
9 };
10 use rustc_ast as ast;
11 use rustc_ast_pretty::pprust;
12 use rustc_data_structures::fx::FxHashMap;
13 use rustc_errors::{DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
14 use rustc_hir as hir;
15 use rustc_hir::intravisit::{self, Visitor};
16 use rustc_hir::HirId;
17 use rustc_index::IndexVec;
18 use rustc_middle::hir::nested_filter;
19 use rustc_middle::lint::{
20 reveal_actual_level, struct_lint_level, LevelAndSource, LintExpectation, LintLevelSource,
21 ShallowLintLevelMap,
22 };
23 use rustc_middle::query::Providers;
24 use rustc_middle::ty::{RegisteredTools, TyCtxt};
25 use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES};
26 use rustc_session::lint::{
27 builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS},
28 Level, Lint, LintExpectationId, LintId,
29 };
30 use rustc_session::parse::{add_feature_diagnostics, feature_err};
31 use rustc_session::Session;
32 use rustc_span::symbol::{sym, Symbol};
33 use rustc_span::{Span, DUMMY_SP};
34
35 use crate::errors::{
36 MalformedAttribute, MalformedAttributeSub, OverruledAttribute, OverruledAttributeSub,
37 UnknownToolInScopedLint,
38 };
39
40 /// Collection of lint levels for the whole crate.
41 /// This is used by AST-based lints, which do not
42 /// wait until we have built HIR to be emitted.
43 #[derive(Debug)]
44 struct LintLevelSets {
45 /// Linked list of specifications.
46 list: IndexVec<LintStackIndex, LintSet>,
47 }
48
49 rustc_index::newtype_index! {
50 #[custom_encodable] // we don't need encoding
51 struct LintStackIndex {
52 const COMMAND_LINE = 0;
53 }
54 }
55
56 /// Specifications found at this position in the stack. This map only represents the lints
57 /// found for one set of attributes (like `shallow_lint_levels_on` does).
58 ///
59 /// We store the level specifications as a linked list.
60 /// Each `LintSet` represents a set of attributes on the same AST node.
61 /// The `parent` forms a linked list that matches the AST tree.
62 /// This way, walking the linked list is equivalent to walking the AST bottom-up
63 /// to find the specifications for a given lint.
64 #[derive(Debug)]
65 struct LintSet {
66 // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
67 // flag.
68 specs: FxHashMap<LintId, LevelAndSource>,
69 parent: LintStackIndex,
70 }
71
72 impl LintLevelSets {
new() -> Self73 fn new() -> Self {
74 LintLevelSets { list: IndexVec::new() }
75 }
76
get_lint_level( &self, lint: &'static Lint, idx: LintStackIndex, aux: Option<&FxHashMap<LintId, LevelAndSource>>, sess: &Session, ) -> LevelAndSource77 fn get_lint_level(
78 &self,
79 lint: &'static Lint,
80 idx: LintStackIndex,
81 aux: Option<&FxHashMap<LintId, LevelAndSource>>,
82 sess: &Session,
83 ) -> LevelAndSource {
84 let lint = LintId::of(lint);
85 let (level, mut src) = self.raw_lint_id_level(lint, idx, aux);
86 let level = reveal_actual_level(level, &mut src, sess, lint, |id| {
87 self.raw_lint_id_level(id, idx, aux)
88 });
89 (level, src)
90 }
91
raw_lint_id_level( &self, id: LintId, mut idx: LintStackIndex, aux: Option<&FxHashMap<LintId, LevelAndSource>>, ) -> (Option<Level>, LintLevelSource)92 fn raw_lint_id_level(
93 &self,
94 id: LintId,
95 mut idx: LintStackIndex,
96 aux: Option<&FxHashMap<LintId, LevelAndSource>>,
97 ) -> (Option<Level>, LintLevelSource) {
98 if let Some(specs) = aux {
99 if let Some(&(level, src)) = specs.get(&id) {
100 return (Some(level), src);
101 }
102 }
103 loop {
104 let LintSet { ref specs, parent } = self.list[idx];
105 if let Some(&(level, src)) = specs.get(&id) {
106 return (Some(level), src);
107 }
108 if idx == COMMAND_LINE {
109 return (None, LintLevelSource::Default);
110 }
111 idx = parent;
112 }
113 }
114 }
115
lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExpectation)>116 fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExpectation)> {
117 let store = unerased_lint_store(tcx);
118
119 let mut builder = LintLevelsBuilder {
120 sess: tcx.sess,
121 provider: QueryMapExpectationsWrapper {
122 tcx,
123 cur: hir::CRATE_HIR_ID,
124 specs: ShallowLintLevelMap::default(),
125 expectations: Vec::new(),
126 unstable_to_stable_ids: FxHashMap::default(),
127 empty: FxHashMap::default(),
128 },
129 warn_about_weird_lints: false,
130 store,
131 registered_tools: &tcx.registered_tools(()),
132 };
133
134 builder.add_command_line();
135 builder.add_id(hir::CRATE_HIR_ID);
136 tcx.hir().walk_toplevel_module(&mut builder);
137
138 tcx.sess.diagnostic().update_unstable_expectation_id(&builder.provider.unstable_to_stable_ids);
139
140 builder.provider.expectations
141 }
142
143 #[instrument(level = "trace", skip(tcx), ret)]
shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap144 fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap {
145 let store = unerased_lint_store(tcx);
146 let attrs = tcx.hir_attrs(owner);
147
148 let mut levels = LintLevelsBuilder {
149 sess: tcx.sess,
150 provider: LintLevelQueryMap {
151 tcx,
152 cur: owner.into(),
153 specs: ShallowLintLevelMap::default(),
154 empty: FxHashMap::default(),
155 attrs,
156 },
157 warn_about_weird_lints: false,
158 store,
159 registered_tools: &tcx.registered_tools(()),
160 };
161
162 if owner == hir::CRATE_OWNER_ID {
163 levels.add_command_line();
164 }
165
166 match attrs.map.range(..) {
167 // There is only something to do if there are attributes at all.
168 [] => {}
169 // Most of the time, there is only one attribute. Avoid fetching HIR in that case.
170 [(local_id, _)] => levels.add_id(HirId { owner, local_id: *local_id }),
171 // Otherwise, we need to visit the attributes in source code order, so we fetch HIR and do
172 // a standard visit.
173 // FIXME(#102522) Just iterate on attrs once that iteration order matches HIR's.
174 _ => match tcx.hir().owner(owner) {
175 hir::OwnerNode::Item(item) => levels.visit_item(item),
176 hir::OwnerNode::ForeignItem(item) => levels.visit_foreign_item(item),
177 hir::OwnerNode::TraitItem(item) => levels.visit_trait_item(item),
178 hir::OwnerNode::ImplItem(item) => levels.visit_impl_item(item),
179 hir::OwnerNode::Crate(mod_) => {
180 levels.add_id(hir::CRATE_HIR_ID);
181 levels.visit_mod(mod_, mod_.spans.inner_span, hir::CRATE_HIR_ID)
182 }
183 },
184 }
185
186 let specs = levels.provider.specs;
187
188 #[cfg(debug_assertions)]
189 for (_, v) in specs.specs.iter() {
190 debug_assert!(!v.is_empty());
191 }
192
193 specs
194 }
195
196 pub struct TopDown {
197 sets: LintLevelSets,
198 cur: LintStackIndex,
199 }
200
201 pub trait LintLevelsProvider {
current_specs(&self) -> &FxHashMap<LintId, LevelAndSource>202 fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource>;
insert(&mut self, id: LintId, lvl: LevelAndSource)203 fn insert(&mut self, id: LintId, lvl: LevelAndSource);
get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource204 fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource;
push_expectation(&mut self, _id: LintExpectationId, _expectation: LintExpectation)205 fn push_expectation(&mut self, _id: LintExpectationId, _expectation: LintExpectation) {}
206 }
207
208 impl LintLevelsProvider for TopDown {
current_specs(&self) -> &FxHashMap<LintId, LevelAndSource>209 fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
210 &self.sets.list[self.cur].specs
211 }
212
insert(&mut self, id: LintId, lvl: LevelAndSource)213 fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
214 self.sets.list[self.cur].specs.insert(id, lvl);
215 }
216
get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource217 fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource {
218 self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), sess)
219 }
220 }
221
222 struct LintLevelQueryMap<'tcx> {
223 tcx: TyCtxt<'tcx>,
224 cur: HirId,
225 specs: ShallowLintLevelMap,
226 /// Empty hash map to simplify code.
227 empty: FxHashMap<LintId, LevelAndSource>,
228 attrs: &'tcx hir::AttributeMap<'tcx>,
229 }
230
231 impl LintLevelsProvider for LintLevelQueryMap<'_> {
current_specs(&self) -> &FxHashMap<LintId, LevelAndSource>232 fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
233 self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
234 }
insert(&mut self, id: LintId, lvl: LevelAndSource)235 fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
236 self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, lvl);
237 }
get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource238 fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
239 self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
240 }
241 }
242
243 struct QueryMapExpectationsWrapper<'tcx> {
244 tcx: TyCtxt<'tcx>,
245 /// HirId of the currently investigated element.
246 cur: HirId,
247 /// Level map for `cur`.
248 specs: ShallowLintLevelMap,
249 expectations: Vec<(LintExpectationId, LintExpectation)>,
250 unstable_to_stable_ids: FxHashMap<LintExpectationId, LintExpectationId>,
251 /// Empty hash map to simplify code.
252 empty: FxHashMap<LintId, LevelAndSource>,
253 }
254
255 impl LintLevelsProvider for QueryMapExpectationsWrapper<'_> {
current_specs(&self) -> &FxHashMap<LintId, LevelAndSource>256 fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
257 self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
258 }
insert(&mut self, id: LintId, lvl: LevelAndSource)259 fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
260 self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, lvl);
261 }
get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource262 fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
263 // We cannot use `tcx.lint_level_at_node` because we want to know in which order the
264 // attributes have been inserted, in particular whether an `expect` follows a `forbid`.
265 self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
266 }
push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation)267 fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation) {
268 let LintExpectationId::Stable { attr_id: Some(attr_id), hir_id, attr_index, .. } = id else { bug!("unstable expectation id should already be mapped") };
269 let key = LintExpectationId::Unstable { attr_id, lint_index: None };
270
271 self.unstable_to_stable_ids.entry(key).or_insert(LintExpectationId::Stable {
272 hir_id,
273 attr_index,
274 lint_index: None,
275 attr_id: None,
276 });
277
278 self.expectations.push((id.normalize(), expectation));
279 }
280 }
281
282 impl<'tcx> LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
add_id(&mut self, hir_id: HirId)283 fn add_id(&mut self, hir_id: HirId) {
284 self.provider.cur = hir_id;
285 self.add(
286 self.provider.attrs.get(hir_id.local_id),
287 hir_id == hir::CRATE_HIR_ID,
288 Some(hir_id),
289 );
290 }
291 }
292
293 impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
294 type NestedFilter = nested_filter::OnlyBodies;
295
nested_visit_map(&mut self) -> Self::Map296 fn nested_visit_map(&mut self) -> Self::Map {
297 self.provider.tcx.hir()
298 }
299
visit_param(&mut self, param: &'tcx hir::Param<'tcx>)300 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
301 self.add_id(param.hir_id);
302 intravisit::walk_param(self, param);
303 }
304
visit_item(&mut self, it: &'tcx hir::Item<'tcx>)305 fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
306 self.add_id(it.hir_id());
307 intravisit::walk_item(self, it);
308 }
309
visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>)310 fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
311 self.add_id(it.hir_id());
312 intravisit::walk_foreign_item(self, it);
313 }
314
visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>)315 fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) {
316 // We will call `add_id` when we walk
317 // the `StmtKind`. The outer statement itself doesn't
318 // define the lint levels.
319 intravisit::walk_stmt(self, e);
320 }
321
visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>)322 fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
323 self.add_id(e.hir_id);
324 intravisit::walk_expr(self, e);
325 }
326
visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>)327 fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
328 self.add_id(s.hir_id);
329 intravisit::walk_field_def(self, s);
330 }
331
visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>)332 fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
333 self.add_id(v.hir_id);
334 intravisit::walk_variant(self, v);
335 }
336
visit_local(&mut self, l: &'tcx hir::Local<'tcx>)337 fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
338 self.add_id(l.hir_id);
339 intravisit::walk_local(self, l);
340 }
341
visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>)342 fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
343 self.add_id(a.hir_id);
344 intravisit::walk_arm(self, a);
345 }
346
visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>)347 fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
348 self.add_id(trait_item.hir_id());
349 intravisit::walk_trait_item(self, trait_item);
350 }
351
visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>)352 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
353 self.add_id(impl_item.hir_id());
354 intravisit::walk_impl_item(self, impl_item);
355 }
356 }
357
358 impl<'tcx> LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'tcx>> {
add_id(&mut self, hir_id: HirId)359 fn add_id(&mut self, hir_id: HirId) {
360 // Change both the `HirId` and the associated specs.
361 self.provider.cur = hir_id;
362 self.provider.specs.specs.clear();
363 self.add(self.provider.tcx.hir().attrs(hir_id), hir_id == hir::CRATE_HIR_ID, Some(hir_id));
364 }
365 }
366
367 impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'tcx>> {
368 type NestedFilter = nested_filter::All;
369
nested_visit_map(&mut self) -> Self::Map370 fn nested_visit_map(&mut self) -> Self::Map {
371 self.provider.tcx.hir()
372 }
373
visit_param(&mut self, param: &'tcx hir::Param<'tcx>)374 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
375 self.add_id(param.hir_id);
376 intravisit::walk_param(self, param);
377 }
378
visit_item(&mut self, it: &'tcx hir::Item<'tcx>)379 fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
380 self.add_id(it.hir_id());
381 intravisit::walk_item(self, it);
382 }
383
visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>)384 fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
385 self.add_id(it.hir_id());
386 intravisit::walk_foreign_item(self, it);
387 }
388
visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>)389 fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) {
390 // We will call `add_id` when we walk
391 // the `StmtKind`. The outer statement itself doesn't
392 // define the lint levels.
393 intravisit::walk_stmt(self, e);
394 }
395
visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>)396 fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
397 self.add_id(e.hir_id);
398 intravisit::walk_expr(self, e);
399 }
400
visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>)401 fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
402 self.add_id(s.hir_id);
403 intravisit::walk_field_def(self, s);
404 }
405
visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>)406 fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
407 self.add_id(v.hir_id);
408 intravisit::walk_variant(self, v);
409 }
410
visit_local(&mut self, l: &'tcx hir::Local<'tcx>)411 fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
412 self.add_id(l.hir_id);
413 intravisit::walk_local(self, l);
414 }
415
visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>)416 fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
417 self.add_id(a.hir_id);
418 intravisit::walk_arm(self, a);
419 }
420
visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>)421 fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
422 self.add_id(trait_item.hir_id());
423 intravisit::walk_trait_item(self, trait_item);
424 }
425
visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>)426 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
427 self.add_id(impl_item.hir_id());
428 intravisit::walk_impl_item(self, impl_item);
429 }
430 }
431
432 pub struct LintLevelsBuilder<'s, P> {
433 sess: &'s Session,
434 provider: P,
435 warn_about_weird_lints: bool,
436 store: &'s LintStore,
437 registered_tools: &'s RegisteredTools,
438 }
439
440 pub(crate) struct BuilderPush {
441 prev: LintStackIndex,
442 }
443
444 impl<'s> LintLevelsBuilder<'s, TopDown> {
new( sess: &'s Session, warn_about_weird_lints: bool, store: &'s LintStore, registered_tools: &'s RegisteredTools, ) -> Self445 pub(crate) fn new(
446 sess: &'s Session,
447 warn_about_weird_lints: bool,
448 store: &'s LintStore,
449 registered_tools: &'s RegisteredTools,
450 ) -> Self {
451 let mut builder = LintLevelsBuilder {
452 sess,
453 provider: TopDown { sets: LintLevelSets::new(), cur: COMMAND_LINE },
454 warn_about_weird_lints,
455 store,
456 registered_tools,
457 };
458 builder.process_command_line();
459 assert_eq!(builder.provider.sets.list.len(), 1);
460 builder
461 }
462
process_command_line(&mut self)463 fn process_command_line(&mut self) {
464 self.provider.cur = self
465 .provider
466 .sets
467 .list
468 .push(LintSet { specs: FxHashMap::default(), parent: COMMAND_LINE });
469 self.add_command_line();
470 }
471
472 /// Pushes a list of AST lint attributes onto this context.
473 ///
474 /// This function will return a `BuilderPush` object which should be passed
475 /// to `pop` when this scope for the attributes provided is exited.
476 ///
477 /// This function will perform a number of tasks:
478 ///
479 /// * It'll validate all lint-related attributes in `attrs`
480 /// * It'll mark all lint-related attributes as used
481 /// * Lint levels will be updated based on the attributes provided
482 /// * Lint attributes are validated, e.g., a `#[forbid]` can't be switched to
483 /// `#[allow]`
484 ///
485 /// Don't forget to call `pop`!
push( &mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: Option<HirId>, ) -> BuilderPush486 pub(crate) fn push(
487 &mut self,
488 attrs: &[ast::Attribute],
489 is_crate_node: bool,
490 source_hir_id: Option<HirId>,
491 ) -> BuilderPush {
492 let prev = self.provider.cur;
493 self.provider.cur =
494 self.provider.sets.list.push(LintSet { specs: FxHashMap::default(), parent: prev });
495
496 self.add(attrs, is_crate_node, source_hir_id);
497
498 if self.provider.current_specs().is_empty() {
499 self.provider.sets.list.pop();
500 self.provider.cur = prev;
501 }
502
503 BuilderPush { prev }
504 }
505
506 /// Called after `push` when the scope of a set of attributes are exited.
pop(&mut self, push: BuilderPush)507 pub(crate) fn pop(&mut self, push: BuilderPush) {
508 self.provider.cur = push.prev;
509 std::mem::forget(push);
510 }
511 }
512
513 #[cfg(debug_assertions)]
514 impl Drop for BuilderPush {
drop(&mut self)515 fn drop(&mut self) {
516 panic!("Found a `push` without a `pop`.");
517 }
518 }
519
520 impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
sess(&self) -> &Session521 pub(crate) fn sess(&self) -> &Session {
522 self.sess
523 }
524
lint_store(&self) -> &LintStore525 pub(crate) fn lint_store(&self) -> &LintStore {
526 self.store
527 }
528
current_specs(&self) -> &FxHashMap<LintId, LevelAndSource>529 fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
530 self.provider.current_specs()
531 }
532
insert(&mut self, id: LintId, lvl: LevelAndSource)533 fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
534 self.provider.insert(id, lvl)
535 }
536
add_command_line(&mut self)537 fn add_command_line(&mut self) {
538 for &(ref lint_name, level) in &self.sess.opts.lint_opts {
539 self.store.check_lint_name_cmdline(self.sess, &lint_name, level, self.registered_tools);
540 let orig_level = level;
541 let lint_flag_val = Symbol::intern(lint_name);
542
543 let Ok(ids) = self.store.find_lints(&lint_name) else {
544 // errors handled in check_lint_name_cmdline above
545 continue
546 };
547 for id in ids {
548 // ForceWarn and Forbid cannot be overridden
549 if let Some((Level::ForceWarn(_) | Level::Forbid, _)) =
550 self.current_specs().get(&id)
551 {
552 continue;
553 }
554
555 if self.check_gated_lint(id, DUMMY_SP) {
556 let src = LintLevelSource::CommandLine(lint_flag_val, orig_level);
557 self.insert(id, (level, src));
558 }
559 }
560 }
561 }
562
563 /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful
564 /// (e.g. if a forbid was already inserted on the same scope), then emits a
565 /// diagnostic with no change to `specs`.
insert_spec(&mut self, id: LintId, (mut level, src): LevelAndSource)566 fn insert_spec(&mut self, id: LintId, (mut level, src): LevelAndSource) {
567 let (old_level, old_src) = self.provider.get_lint_level(id.lint, &self.sess);
568 if let Level::Expect(id) = &mut level && let LintExpectationId::Stable { .. } = id {
569 *id = id.normalize();
570 }
571 // Setting to a non-forbid level is an error if the lint previously had
572 // a forbid level. Note that this is not necessarily true even with a
573 // `#[forbid(..)]` attribute present, as that is overridden by `--cap-lints`.
574 //
575 // This means that this only errors if we're truly lowering the lint
576 // level from forbid.
577 if level != Level::Forbid {
578 if let Level::Forbid = old_level {
579 // Backwards compatibility check:
580 //
581 // We used to not consider `forbid(lint_group)`
582 // as preventing `allow(lint)` for some lint `lint` in
583 // `lint_group`. For now, issue a future-compatibility
584 // warning for this case.
585 let id_name = id.lint.name_lower();
586 let fcw_warning = match old_src {
587 LintLevelSource::Default => false,
588 LintLevelSource::Node { name, .. } => self.store.is_lint_group(name),
589 LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol),
590 };
591 debug!(
592 "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}",
593 fcw_warning,
594 self.current_specs(),
595 old_src,
596 id_name
597 );
598 let sub = match old_src {
599 LintLevelSource::Default => {
600 OverruledAttributeSub::DefaultSource { id: id.to_string() }
601 }
602 LintLevelSource::Node { span, reason, .. } => {
603 OverruledAttributeSub::NodeSource { span, reason }
604 }
605 LintLevelSource::CommandLine(_, _) => OverruledAttributeSub::CommandLineSource,
606 };
607 if !fcw_warning {
608 self.sess.emit_err(OverruledAttribute {
609 span: src.span(),
610 overruled: src.span(),
611 lint_level: level.as_str(),
612 lint_source: src.name(),
613 sub,
614 });
615 } else {
616 self.emit_spanned_lint(
617 FORBIDDEN_LINT_GROUPS,
618 src.span().into(),
619 OverruledAttributeLint {
620 overruled: src.span(),
621 lint_level: level.as_str(),
622 lint_source: src.name(),
623 sub,
624 },
625 );
626 }
627
628 // Retain the forbid lint level, unless we are
629 // issuing a FCW. In the FCW case, we want to
630 // respect the new setting.
631 if !fcw_warning {
632 return;
633 }
634 }
635 }
636
637 // The lint `unfulfilled_lint_expectations` can't be expected, as it would suppress itself.
638 // Handling expectations of this lint would add additional complexity with little to no
639 // benefit. The expect level for this lint will therefore be ignored.
640 if let Level::Expect(_) = level && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS) {
641 return;
642 }
643
644 match (old_level, level) {
645 // If the new level is an expectation store it in `ForceWarn`
646 (Level::ForceWarn(_), Level::Expect(expectation_id)) => {
647 self.insert(id, (Level::ForceWarn(Some(expectation_id)), old_src))
648 }
649 // Keep `ForceWarn` level but drop the expectation
650 (Level::ForceWarn(_), _) => self.insert(id, (Level::ForceWarn(None), old_src)),
651 // Set the lint level as normal
652 _ => self.insert(id, (level, src)),
653 };
654 }
655
add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: Option<HirId>)656 fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: Option<HirId>) {
657 let sess = self.sess;
658 for (attr_index, attr) in attrs.iter().enumerate() {
659 if attr.has_name(sym::automatically_derived) {
660 self.insert(
661 LintId::of(SINGLE_USE_LIFETIMES),
662 (Level::Allow, LintLevelSource::Default),
663 );
664 continue;
665 }
666
667 let level = match Level::from_attr(attr) {
668 None => continue,
669 // This is the only lint level with a `LintExpectationId` that can be created from an attribute
670 Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => {
671 let LintExpectationId::Unstable { attr_id, lint_index } = unstable_id
672 else { bug!("stable id Level::from_attr") };
673
674 let stable_id = LintExpectationId::Stable {
675 hir_id,
676 attr_index: attr_index.try_into().unwrap(),
677 lint_index,
678 // we pass the previous unstable attr_id such that we can trace the ast id when building a map
679 // to go from unstable to stable id.
680 attr_id: Some(attr_id),
681 };
682
683 Level::Expect(stable_id)
684 }
685 Some(lvl) => lvl,
686 };
687
688 let Some(mut metas) = attr.meta_item_list() else {
689 continue
690 };
691
692 if metas.is_empty() {
693 // This emits the unused_attributes lint for `#[level()]`
694 continue;
695 }
696
697 // Before processing the lint names, look for a reason (RFC 2383)
698 // at the end.
699 let mut reason = None;
700 let tail_li = &metas[metas.len() - 1];
701 if let Some(item) = tail_li.meta_item() {
702 match item.kind {
703 ast::MetaItemKind::Word => {} // actual lint names handled later
704 ast::MetaItemKind::NameValue(ref name_value) => {
705 if item.path == sym::reason {
706 if let ast::LitKind::Str(rationale, _) = name_value.kind {
707 if !self.sess.features_untracked().lint_reasons {
708 feature_err(
709 &self.sess.parse_sess,
710 sym::lint_reasons,
711 item.span,
712 "lint reasons are experimental",
713 )
714 .emit();
715 }
716 reason = Some(rationale);
717 } else {
718 sess.emit_err(MalformedAttribute {
719 span: name_value.span,
720 sub: MalformedAttributeSub::ReasonMustBeStringLiteral(
721 name_value.span,
722 ),
723 });
724 }
725 // found reason, reslice meta list to exclude it
726 metas.pop().unwrap();
727 } else {
728 sess.emit_err(MalformedAttribute {
729 span: item.span,
730 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
731 });
732 }
733 }
734 ast::MetaItemKind::List(_) => {
735 sess.emit_err(MalformedAttribute {
736 span: item.span,
737 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
738 });
739 }
740 }
741 }
742
743 for (lint_index, li) in metas.iter_mut().enumerate() {
744 let level = match level {
745 Level::Expect(mut id) => {
746 id.set_lint_index(Some(lint_index as u16));
747 Level::Expect(id)
748 }
749 level => level,
750 };
751
752 let sp = li.span();
753 let meta_item = match li {
754 ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item,
755 _ => {
756 if let Some(item) = li.meta_item() {
757 if let ast::MetaItemKind::NameValue(_) = item.kind {
758 if item.path == sym::reason {
759 sess.emit_err(MalformedAttribute {
760 span: sp,
761 sub: MalformedAttributeSub::ReasonMustComeLast(sp),
762 });
763 continue;
764 }
765 }
766 }
767 sess.emit_err(MalformedAttribute {
768 span: sp,
769 sub: MalformedAttributeSub::BadAttributeArgument(sp),
770 });
771 continue;
772 }
773 };
774 let tool_ident = if meta_item.path.segments.len() > 1 {
775 Some(meta_item.path.segments.remove(0).ident)
776 } else {
777 None
778 };
779 let tool_name = tool_ident.map(|ident| ident.name);
780 let name = pprust::path_to_string(&meta_item.path);
781 let lint_result =
782 self.store.check_lint_name(&name, tool_name, self.registered_tools);
783 match &lint_result {
784 CheckLintNameResult::Ok(ids) => {
785 // This checks for instances where the user writes `#[expect(unfulfilled_lint_expectations)]`
786 // in that case we want to avoid overriding the lint level but instead add an expectation that
787 // can't be fulfilled. The lint message will include an explanation, that the
788 // `unfulfilled_lint_expectations` lint can't be expected.
789 if let Level::Expect(expect_id) = level {
790 // The `unfulfilled_lint_expectations` lint is not part of any lint groups. Therefore. we
791 // only need to check the slice if it contains a single lint.
792 let is_unfulfilled_lint_expectations = match ids {
793 [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS),
794 _ => false,
795 };
796 self.provider.push_expectation(
797 expect_id,
798 LintExpectation::new(
799 reason,
800 sp,
801 is_unfulfilled_lint_expectations,
802 tool_name,
803 ),
804 );
805 }
806 let src = LintLevelSource::Node {
807 name: meta_item
808 .path
809 .segments
810 .last()
811 .expect("empty lint name")
812 .ident
813 .name,
814 span: sp,
815 reason,
816 };
817 for &id in *ids {
818 if self.check_gated_lint(id, attr.span) {
819 self.insert_spec(id, (level, src));
820 }
821 }
822 }
823
824 CheckLintNameResult::Tool(result) => {
825 match *result {
826 Ok(ids) => {
827 let complete_name =
828 &format!("{}::{}", tool_ident.unwrap().name, name);
829 let src = LintLevelSource::Node {
830 name: Symbol::intern(complete_name),
831 span: sp,
832 reason,
833 };
834 for &id in ids {
835 if self.check_gated_lint(id, attr.span) {
836 self.insert_spec(id, (level, src));
837 }
838 }
839 if let Level::Expect(expect_id) = level {
840 self.provider.push_expectation(
841 expect_id,
842 LintExpectation::new(reason, sp, false, tool_name),
843 );
844 }
845 }
846 Err((Some(ids), ref new_lint_name)) => {
847 let lint = builtin::RENAMED_AND_REMOVED_LINTS;
848 self.emit_spanned_lint(
849 lint,
850 sp.into(),
851 DeprecatedLintName {
852 name,
853 suggestion: sp,
854 replace: &new_lint_name,
855 },
856 );
857
858 let src = LintLevelSource::Node {
859 name: Symbol::intern(&new_lint_name),
860 span: sp,
861 reason,
862 };
863 for id in ids {
864 self.insert_spec(*id, (level, src));
865 }
866 if let Level::Expect(expect_id) = level {
867 self.provider.push_expectation(
868 expect_id,
869 LintExpectation::new(reason, sp, false, tool_name),
870 );
871 }
872 }
873 Err((None, _)) => {
874 // If Tool(Err(None, _)) is returned, then either the lint does not
875 // exist in the tool or the code was not compiled with the tool and
876 // therefore the lint was never added to the `LintStore`. To detect
877 // this is the responsibility of the lint tool.
878 }
879 }
880 }
881
882 &CheckLintNameResult::NoTool => {
883 sess.emit_err(UnknownToolInScopedLint {
884 span: tool_ident.map(|ident| ident.span),
885 tool_name: tool_name.unwrap(),
886 lint_name: pprust::path_to_string(&meta_item.path),
887 is_nightly_build: sess.is_nightly_build().then_some(()),
888 });
889 continue;
890 }
891
892 _ if !self.warn_about_weird_lints => {}
893
894 CheckLintNameResult::Warning(msg, renamed) => {
895 let suggestion =
896 renamed.as_ref().map(|replace| RenamedOrRemovedLintSuggestion {
897 suggestion: sp,
898 replace: replace.as_str(),
899 });
900 self.emit_spanned_lint(
901 RENAMED_AND_REMOVED_LINTS,
902 sp.into(),
903 RenamedOrRemovedLint { msg, suggestion },
904 );
905 }
906 CheckLintNameResult::NoLint(suggestion) => {
907 let name = if let Some(tool_ident) = tool_ident {
908 format!("{}::{}", tool_ident.name, name)
909 } else {
910 name.to_string()
911 };
912 let suggestion = suggestion
913 .map(|replace| UnknownLintSuggestion { suggestion: sp, replace });
914 self.emit_spanned_lint(
915 UNKNOWN_LINTS,
916 sp.into(),
917 UnknownLint { name, suggestion },
918 );
919 }
920 }
921 // If this lint was renamed, apply the new lint instead of ignoring the attribute.
922 // This happens outside of the match because the new lint should be applied even if
923 // we don't warn about the name change.
924 if let CheckLintNameResult::Warning(_, Some(new_name)) = lint_result {
925 // Ignore any errors or warnings that happen because the new name is inaccurate
926 // NOTE: `new_name` already includes the tool name, so we don't have to add it again.
927 if let CheckLintNameResult::Ok(ids) =
928 self.store.check_lint_name(&new_name, None, self.registered_tools)
929 {
930 let src = LintLevelSource::Node {
931 name: Symbol::intern(&new_name),
932 span: sp,
933 reason,
934 };
935 for &id in ids {
936 if self.check_gated_lint(id, attr.span) {
937 self.insert_spec(id, (level, src));
938 }
939 }
940 if let Level::Expect(expect_id) = level {
941 self.provider.push_expectation(
942 expect_id,
943 LintExpectation::new(reason, sp, false, tool_name),
944 );
945 }
946 } else {
947 panic!("renamed lint does not exist: {}", new_name);
948 }
949 }
950 }
951 }
952
953 if !is_crate_node {
954 for (id, &(level, ref src)) in self.current_specs().iter() {
955 if !id.lint.crate_level_only {
956 continue;
957 }
958
959 let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } = *src else {
960 continue
961 };
962
963 self.emit_spanned_lint(
964 UNUSED_ATTRIBUTES,
965 lint_attr_span.into(),
966 IgnoredUnlessCrateSpecified { level: level.as_str(), name: lint_attr_name },
967 );
968 // don't set a separate error for every lint in the group
969 break;
970 }
971 }
972 }
973
974 /// Checks if the lint is gated on a feature that is not enabled.
975 ///
976 /// Returns `true` if the lint's feature is enabled.
977 // FIXME only emit this once for each attribute, instead of repeating it 4 times for
978 // pre-expansion lints, post-expansion lints, `shallow_lint_levels_on` and `lint_expectations`.
check_gated_lint(&self, lint_id: LintId, span: Span) -> bool979 fn check_gated_lint(&self, lint_id: LintId, span: Span) -> bool {
980 if let Some(feature) = lint_id.lint.feature_gate {
981 if !self.sess.features_untracked().enabled(feature) {
982 let lint = builtin::UNKNOWN_LINTS;
983 let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS);
984 struct_lint_level(
985 self.sess,
986 lint,
987 level,
988 src,
989 Some(span.into()),
990 fluent::lint_unknown_gated_lint,
991 |lint| {
992 lint.set_arg("name", lint_id.lint.name_lower());
993 lint.note(fluent::lint_note);
994 add_feature_diagnostics(lint, &self.sess.parse_sess, feature);
995 lint
996 },
997 );
998 return false;
999 }
1000 }
1001 true
1002 }
1003
1004 /// Find the lint level for a lint.
lint_level(&self, lint: &'static Lint) -> LevelAndSource1005 pub fn lint_level(&self, lint: &'static Lint) -> LevelAndSource {
1006 self.provider.get_lint_level(lint, self.sess)
1007 }
1008
1009 /// Used to emit a lint-related diagnostic based on the current state of
1010 /// this lint context.
1011 ///
1012 /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
1013 ///
1014 /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
1015 #[rustc_lint_diagnostics]
struct_lint( &self, lint: &'static Lint, span: Option<MultiSpan>, msg: impl Into<DiagnosticMessage>, decorate: impl for<'a, 'b> FnOnce( &'b mut DiagnosticBuilder<'a, ()>, ) -> &'b mut DiagnosticBuilder<'a, ()>, )1016 pub(crate) fn struct_lint(
1017 &self,
1018 lint: &'static Lint,
1019 span: Option<MultiSpan>,
1020 msg: impl Into<DiagnosticMessage>,
1021 decorate: impl for<'a, 'b> FnOnce(
1022 &'b mut DiagnosticBuilder<'a, ()>,
1023 ) -> &'b mut DiagnosticBuilder<'a, ()>,
1024 ) {
1025 let (level, src) = self.lint_level(lint);
1026 struct_lint_level(self.sess, lint, level, src, span, msg, decorate)
1027 }
1028
emit_spanned_lint( &self, lint: &'static Lint, span: MultiSpan, decorate: impl for<'a> DecorateLint<'a, ()>, )1029 pub fn emit_spanned_lint(
1030 &self,
1031 lint: &'static Lint,
1032 span: MultiSpan,
1033 decorate: impl for<'a> DecorateLint<'a, ()>,
1034 ) {
1035 let (level, src) = self.lint_level(lint);
1036 struct_lint_level(self.sess, lint, level, src, Some(span), decorate.msg(), |lint| {
1037 decorate.decorate_lint(lint)
1038 });
1039 }
1040
emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> DecorateLint<'a, ()>)1041 pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> DecorateLint<'a, ()>) {
1042 let (level, src) = self.lint_level(lint);
1043 struct_lint_level(self.sess, lint, level, src, None, decorate.msg(), |lint| {
1044 decorate.decorate_lint(lint)
1045 });
1046 }
1047 }
1048
provide(providers: &mut Providers)1049 pub(crate) fn provide(providers: &mut Providers) {
1050 *providers = Providers { shallow_lint_levels_on, lint_expectations, ..*providers };
1051 }
1052