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