]>
Commit | Line | Data |
---|---|---|
041b39d2 XL |
1 | //! This module implements some validity checks for attributes. |
2 | //! In particular it verifies that `#[inline]` and `#[repr]` attributes are | |
3 | //! attached to items that actually support them and if there are | |
4 | //! conflicts between multiple such attributes attached to the same | |
5 | //! item. | |
6 | ||
2b03887a FG |
7 | use crate::errors::{ |
8 | self, AttrApplication, DebugVisualizerUnreadable, InvalidAttrAtCrateLevel, ObjectLifetimeErr, | |
9 | OnlyHasEffectOn, TransparentIncompatible, UnrecognizedReprHint, | |
10 | }; | |
487cf647 | 11 | use rustc_ast::{ast, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem}; |
3c0e092e | 12 | use rustc_data_structures::fx::FxHashMap; |
2b03887a | 13 | use rustc_errors::{fluent, Applicability, MultiSpan}; |
923072b8 | 14 | use rustc_expand::base::resolve_path; |
3c0e092e | 15 | use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; |
dfeec247 | 16 | use rustc_hir as hir; |
2b03887a | 17 | use rustc_hir::def_id::LocalDefId; |
5099ac24 | 18 | use rustc_hir::intravisit::{self, Visitor}; |
2b03887a FG |
19 | use rustc_hir::{ |
20 | self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID, | |
21 | }; | |
74b04a01 | 22 | use rustc_hir::{MethodKind, Target}; |
5099ac24 | 23 | use rustc_middle::hir::nested_filter; |
f2b60f7d | 24 | use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault; |
5099ac24 FG |
25 | use rustc_middle::ty::query::Providers; |
26 | use rustc_middle::ty::TyCtxt; | |
6a06907d XL |
27 | use rustc_session::lint::builtin::{ |
28 | CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES, | |
29 | }; | |
74b04a01 | 30 | use rustc_session::parse::feature_err; |
04454e1e FG |
31 | use rustc_span::symbol::{kw, sym, Symbol}; |
32 | use rustc_span::{Span, DUMMY_SP}; | |
923072b8 | 33 | use rustc_target::spec::abi::Abi; |
3c0e092e | 34 | use std::collections::hash_map::Entry; |
b039eaaf | 35 | |
f035d41b XL |
36 | pub(crate) fn target_from_impl_item<'tcx>( |
37 | tcx: TyCtxt<'tcx>, | |
38 | impl_item: &hir::ImplItem<'_>, | |
39 | ) -> Target { | |
74b04a01 XL |
40 | match impl_item.kind { |
41 | hir::ImplItemKind::Const(..) => Target::AssocConst, | |
ba9703b0 | 42 | hir::ImplItemKind::Fn(..) => { |
2b03887a FG |
43 | let parent_def_id = tcx.hir().get_parent_item(impl_item.hir_id()).def_id; |
44 | let containing_item = tcx.hir().expect_item(parent_def_id); | |
74b04a01 | 45 | let containing_impl_is_for_trait = match &containing_item.kind { |
5869c6ff | 46 | hir::ItemKind::Impl(impl_) => impl_.of_trait.is_some(), |
74b04a01 XL |
47 | _ => bug!("parent of an ImplItem must be an Impl"), |
48 | }; | |
49 | if containing_impl_is_for_trait { | |
e74abb32 | 50 | Target::Method(MethodKind::Trait { body: true }) |
74b04a01 XL |
51 | } else { |
52 | Target::Method(MethodKind::Inherent) | |
e74abb32 | 53 | } |
b039eaaf | 54 | } |
2b03887a | 55 | hir::ImplItemKind::Type(..) => Target::AssocTy, |
b039eaaf SL |
56 | } |
57 | } | |
58 | ||
1b1a35ee XL |
59 | #[derive(Clone, Copy)] |
60 | enum ItemLike<'tcx> { | |
61 | Item(&'tcx Item<'tcx>), | |
064997fb | 62 | ForeignItem, |
1b1a35ee XL |
63 | } |
64 | ||
dc9dc135 XL |
65 | struct CheckAttrVisitor<'tcx> { |
66 | tcx: TyCtxt<'tcx>, | |
b039eaaf SL |
67 | } |
68 | ||
a2a8927a | 69 | impl CheckAttrVisitor<'_> { |
9fa01778 | 70 | /// Checks any attribute. |
e74abb32 XL |
71 | fn check_attributes( |
72 | &self, | |
73 | hir_id: HirId, | |
5099ac24 | 74 | span: Span, |
e74abb32 | 75 | target: Target, |
1b1a35ee | 76 | item: Option<ItemLike<'_>>, |
e74abb32 | 77 | ) { |
c295e0f8 | 78 | let mut doc_aliases = FxHashMap::default(); |
e74abb32 | 79 | let mut is_valid = true; |
17df50a5 | 80 | let mut specified_inline = None; |
3c0e092e | 81 | let mut seen = FxHashMap::default(); |
6a06907d | 82 | let attrs = self.tcx.hir().attrs(hir_id); |
e74abb32 | 83 | for attr in attrs { |
94222f64 | 84 | let attr_is_valid = match attr.name_or_empty() { |
cdc7bbd5 | 85 | sym::inline => self.check_inline(hir_id, attr, span, target), |
923072b8 | 86 | sym::no_coverage => self.check_no_coverage(hir_id, attr, span, target), |
cdc7bbd5 XL |
87 | sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target), |
88 | sym::marker => self.check_marker(hir_id, attr, span, target), | |
5099ac24 FG |
89 | sym::rustc_must_implement_one_of => { |
90 | self.check_rustc_must_implement_one_of(attr, span, target) | |
91 | } | |
cdc7bbd5 | 92 | sym::target_feature => self.check_target_feature(hir_id, attr, span, target), |
04454e1e | 93 | sym::thread_local => self.check_thread_local(attr, span, target), |
cdc7bbd5 | 94 | sym::track_caller => { |
5099ac24 | 95 | self.check_track_caller(hir_id, attr.span, attrs, span, target) |
1b1a35ee | 96 | } |
c295e0f8 XL |
97 | sym::doc => self.check_doc_attrs( |
98 | attr, | |
99 | hir_id, | |
100 | target, | |
101 | &mut specified_inline, | |
102 | &mut doc_aliases, | |
103 | ), | |
cdc7bbd5 XL |
104 | sym::no_link => self.check_no_link(hir_id, &attr, span, target), |
105 | sym::export_name => self.check_export_name(hir_id, &attr, span, target), | |
cdc7bbd5 XL |
106 | sym::rustc_layout_scalar_valid_range_start |
107 | | sym::rustc_layout_scalar_valid_range_end => { | |
108 | self.check_rustc_layout_scalar_valid_range(&attr, span, target) | |
109 | } | |
110 | sym::allow_internal_unstable => { | |
111 | self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs) | |
112 | } | |
04454e1e | 113 | sym::debugger_visualizer => self.check_debugger_visualizer(&attr, target), |
cdc7bbd5 XL |
114 | sym::rustc_allow_const_fn_unstable => { |
115 | self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target) | |
116 | } | |
04454e1e FG |
117 | sym::rustc_std_internal_symbol => { |
118 | self.check_rustc_std_internal_symbol(&attr, span, target) | |
119 | } | |
cdc7bbd5 XL |
120 | sym::naked => self.check_naked(hir_id, attr, span, target), |
121 | sym::rustc_legacy_const_generics => { | |
487cf647 | 122 | self.check_rustc_legacy_const_generics(hir_id, &attr, span, target, item) |
cdc7bbd5 | 123 | } |
5099ac24 | 124 | sym::rustc_lint_query_instability => { |
487cf647 | 125 | self.check_rustc_lint_query_instability(hir_id, &attr, span, target) |
5099ac24 | 126 | } |
923072b8 | 127 | sym::rustc_lint_diagnostics => { |
487cf647 | 128 | self.check_rustc_lint_diagnostics(hir_id, &attr, span, target) |
923072b8 | 129 | } |
064997fb FG |
130 | sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(&attr, span, target), |
131 | sym::rustc_lint_opt_deny_field_access => { | |
132 | self.check_rustc_lint_opt_deny_field_access(&attr, span, target) | |
133 | } | |
cdc7bbd5 XL |
134 | sym::rustc_clean |
135 | | sym::rustc_dirty | |
136 | | sym::rustc_if_this_changed | |
137 | | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr), | |
487cf647 FG |
138 | sym::cmse_nonsecure_entry => { |
139 | self.check_cmse_nonsecure_entry(hir_id, attr, span, target) | |
140 | } | |
f2b60f7d | 141 | sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target), |
923072b8 | 142 | sym::const_trait => self.check_const_trait(attr, span, target), |
c295e0f8 | 143 | sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target), |
487cf647 | 144 | sym::must_use => self.check_must_use(hir_id, &attr, target), |
5099ac24 | 145 | sym::rustc_pass_by_value => self.check_pass_by_value(&attr, span, target), |
5e7ed085 FG |
146 | sym::rustc_allow_incoherent_impl => { |
147 | self.check_allow_incoherent_impl(&attr, span, target) | |
148 | } | |
04454e1e FG |
149 | sym::rustc_has_incoherent_inherent_impls => { |
150 | self.check_has_incoherent_inherent_impls(&attr, span, target) | |
151 | } | |
94222f64 XL |
152 | sym::rustc_const_unstable |
153 | | sym::rustc_const_stable | |
154 | | sym::unstable | |
155 | | sym::stable | |
064997fb | 156 | | sym::rustc_allowed_through_unstable_modules |
94222f64 | 157 | | sym::rustc_promotable => self.check_stability_promotable(&attr, span, target), |
f2b60f7d | 158 | sym::link_ordinal => self.check_link_ordinal(&attr, span, target), |
cdc7bbd5 | 159 | _ => true, |
e74abb32 | 160 | }; |
94222f64 XL |
161 | is_valid &= attr_is_valid; |
162 | ||
cdc7bbd5 XL |
163 | // lint-only checks |
164 | match attr.name_or_empty() { | |
165 | sym::cold => self.check_cold(hir_id, attr, span, target), | |
5099ac24 | 166 | sym::link => self.check_link(hir_id, attr, span, target), |
cdc7bbd5 XL |
167 | sym::link_name => self.check_link_name(hir_id, attr, span, target), |
168 | sym::link_section => self.check_link_section(hir_id, attr, span, target), | |
169 | sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target), | |
923072b8 | 170 | sym::deprecated => self.check_deprecated(hir_id, attr, span, target), |
94222f64 | 171 | sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target), |
2b03887a | 172 | sym::path => self.check_generic_attr(hir_id, attr, target, Target::Mod), |
94222f64 XL |
173 | sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target), |
174 | sym::macro_export => self.check_macro_export(hir_id, attr, target), | |
175 | sym::ignore | sym::should_panic | sym::proc_macro_derive => { | |
2b03887a | 176 | self.check_generic_attr(hir_id, attr, target, Target::Fn) |
94222f64 XL |
177 | } |
178 | sym::automatically_derived => { | |
2b03887a | 179 | self.check_generic_attr(hir_id, attr, target, Target::Impl) |
94222f64 XL |
180 | } |
181 | sym::no_implicit_prelude => { | |
2b03887a | 182 | self.check_generic_attr(hir_id, attr, target, Target::Mod) |
94222f64 | 183 | } |
f2b60f7d | 184 | sym::rustc_object_lifetime_default => self.check_object_lifetime_default(hir_id), |
94222f64 XL |
185 | _ => {} |
186 | } | |
187 | ||
3c0e092e XL |
188 | let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); |
189 | ||
94222f64 | 190 | if hir_id != CRATE_HIR_ID { |
3c0e092e | 191 | if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = |
94222f64 XL |
192 | attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) |
193 | { | |
064997fb FG |
194 | match attr.style { |
195 | ast::AttrStyle::Outer => self.tcx.emit_spanned_lint( | |
196 | UNUSED_ATTRIBUTES, | |
197 | hir_id, | |
198 | attr.span, | |
199 | errors::OuterCrateLevelAttr, | |
200 | ), | |
201 | ast::AttrStyle::Inner => self.tcx.emit_spanned_lint( | |
202 | UNUSED_ATTRIBUTES, | |
203 | hir_id, | |
204 | attr.span, | |
205 | errors::InnerCrateLevelAttr, | |
206 | ), | |
207 | } | |
94222f64 XL |
208 | } |
209 | } | |
210 | ||
3c0e092e XL |
211 | if let Some(BuiltinAttribute { duplicates, .. }) = builtin { |
212 | check_duplicates(self.tcx, attr, hir_id, *duplicates, &mut seen); | |
213 | } | |
214 | ||
5e7ed085 | 215 | self.check_unused_attribute(hir_id, attr) |
041b39d2 | 216 | } |
2c00a5a8 | 217 | |
e74abb32 XL |
218 | if !is_valid { |
219 | return; | |
220 | } | |
221 | ||
dfeec247 | 222 | self.check_repr(attrs, span, target, item, hir_id); |
e74abb32 XL |
223 | self.check_used(attrs, target); |
224 | } | |
225 | ||
5869c6ff | 226 | fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { |
064997fb FG |
227 | self.tcx.emit_spanned_lint( |
228 | UNUSED_ATTRIBUTES, | |
229 | hir_id, | |
230 | attr.span, | |
231 | errors::IgnoredAttrWithMacro { sym }, | |
232 | ); | |
5869c6ff XL |
233 | } |
234 | ||
235 | fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { | |
064997fb FG |
236 | self.tcx.emit_spanned_lint( |
237 | UNUSED_ATTRIBUTES, | |
238 | hir_id, | |
239 | attr.span, | |
240 | errors::IgnoredAttr { sym }, | |
241 | ); | |
5869c6ff XL |
242 | } |
243 | ||
e74abb32 | 244 | /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. |
5099ac24 | 245 | fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { |
e74abb32 | 246 | match target { |
dfeec247 XL |
247 | Target::Fn |
248 | | Target::Closure | |
ba9703b0 | 249 | | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, |
e74abb32 | 250 | Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { |
064997fb FG |
251 | self.tcx.emit_spanned_lint( |
252 | UNUSED_ATTRIBUTES, | |
253 | hir_id, | |
254 | attr.span, | |
255 | errors::IgnoredInlineAttrFnProto, | |
256 | ); | |
e74abb32 XL |
257 | true |
258 | } | |
259 | // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with | |
260 | // just a lint, because we previously erroneously allowed it and some crates used it | |
2b03887a | 261 | // accidentally, to be compatible with crates depending on them, we can't throw an |
e74abb32 XL |
262 | // error here. |
263 | Target::AssocConst => { | |
064997fb FG |
264 | self.tcx.emit_spanned_lint( |
265 | UNUSED_ATTRIBUTES, | |
266 | hir_id, | |
267 | attr.span, | |
268 | errors::IgnoredInlineAttrConstants, | |
269 | ); | |
e74abb32 XL |
270 | true |
271 | } | |
5869c6ff XL |
272 | // FIXME(#80564): Same for fields, arms, and macro defs |
273 | Target::Field | Target::Arm | Target::MacroDef => { | |
274 | self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline"); | |
275 | true | |
276 | } | |
e74abb32 | 277 | _ => { |
064997fb FG |
278 | self.tcx.sess.emit_err(errors::InlineNotFnOrClosure { |
279 | attr_span: attr.span, | |
280 | defn_span: span, | |
281 | }); | |
e74abb32 XL |
282 | false |
283 | } | |
284 | } | |
041b39d2 XL |
285 | } |
286 | ||
923072b8 FG |
287 | /// Checks if a `#[no_coverage]` is applied directly to a function |
288 | fn check_no_coverage( | |
289 | &self, | |
290 | hir_id: HirId, | |
291 | attr: &Attribute, | |
292 | span: Span, | |
293 | target: Target, | |
294 | ) -> bool { | |
295 | match target { | |
296 | // no_coverage on function is fine | |
297 | Target::Fn | |
298 | | Target::Closure | |
299 | | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, | |
300 | ||
301 | // function prototypes can't be covered | |
302 | Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { | |
064997fb FG |
303 | self.tcx.emit_spanned_lint( |
304 | UNUSED_ATTRIBUTES, | |
305 | hir_id, | |
306 | attr.span, | |
307 | errors::IgnoredNoCoverageFnProto, | |
308 | ); | |
923072b8 FG |
309 | true |
310 | } | |
311 | ||
312 | Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => { | |
064997fb FG |
313 | self.tcx.emit_spanned_lint( |
314 | UNUSED_ATTRIBUTES, | |
315 | hir_id, | |
316 | attr.span, | |
317 | errors::IgnoredNoCoveragePropagate, | |
318 | ); | |
923072b8 FG |
319 | true |
320 | } | |
321 | ||
322 | Target::Expression | Target::Statement | Target::Arm => { | |
064997fb FG |
323 | self.tcx.emit_spanned_lint( |
324 | UNUSED_ATTRIBUTES, | |
325 | hir_id, | |
326 | attr.span, | |
327 | errors::IgnoredNoCoverageFnDefn, | |
328 | ); | |
923072b8 FG |
329 | true |
330 | } | |
331 | ||
332 | _ => { | |
064997fb FG |
333 | self.tcx.sess.emit_err(errors::IgnoredNoCoverageNotCoverable { |
334 | attr_span: attr.span, | |
335 | defn_span: span, | |
336 | }); | |
923072b8 FG |
337 | false |
338 | } | |
339 | } | |
340 | } | |
341 | ||
94222f64 XL |
342 | fn check_generic_attr( |
343 | &self, | |
344 | hir_id: HirId, | |
345 | attr: &Attribute, | |
346 | target: Target, | |
2b03887a | 347 | allowed_target: Target, |
94222f64 | 348 | ) { |
2b03887a FG |
349 | if target != allowed_target { |
350 | self.tcx.emit_spanned_lint( | |
351 | UNUSED_ATTRIBUTES, | |
352 | hir_id, | |
353 | attr.span, | |
354 | OnlyHasEffectOn { | |
355 | attr_name: attr.name_or_empty(), | |
487cf647 | 356 | target_name: allowed_target.name().replace(' ', "_"), |
2b03887a FG |
357 | }, |
358 | ); | |
94222f64 XL |
359 | } |
360 | } | |
361 | ||
fc512014 | 362 | /// Checks if `#[naked]` is applied to a function definition. |
5099ac24 | 363 | fn check_naked(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { |
fc512014 XL |
364 | match target { |
365 | Target::Fn | |
366 | | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, | |
5869c6ff XL |
367 | // FIXME(#80564): We permit struct fields, match arms and macro defs to have an |
368 | // `#[allow_internal_unstable]` attribute with just a lint, because we previously | |
2b03887a | 369 | // erroneously allowed it and some crates used it accidentally, to be compatible |
5869c6ff XL |
370 | // with crates depending on them, we can't throw an error here. |
371 | Target::Field | Target::Arm | Target::MacroDef => { | |
372 | self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked"); | |
373 | true | |
374 | } | |
fc512014 | 375 | _ => { |
064997fb FG |
376 | self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { |
377 | attr_span: attr.span, | |
378 | defn_span: span, | |
487cf647 | 379 | on_crate: hir_id == CRATE_HIR_ID, |
064997fb | 380 | }); |
fc512014 XL |
381 | false |
382 | } | |
383 | } | |
384 | } | |
385 | ||
136023e0 | 386 | /// Checks if `#[cmse_nonsecure_entry]` is applied to a function definition. |
487cf647 FG |
387 | fn check_cmse_nonsecure_entry( |
388 | &self, | |
389 | hir_id: HirId, | |
390 | attr: &Attribute, | |
391 | span: Span, | |
392 | target: Target, | |
393 | ) -> bool { | |
136023e0 XL |
394 | match target { |
395 | Target::Fn | |
396 | | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, | |
397 | _ => { | |
064997fb FG |
398 | self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { |
399 | attr_span: attr.span, | |
400 | defn_span: span, | |
487cf647 | 401 | on_crate: hir_id == CRATE_HIR_ID, |
064997fb | 402 | }); |
136023e0 XL |
403 | false |
404 | } | |
405 | } | |
406 | } | |
407 | ||
f2b60f7d FG |
408 | /// Debugging aid for `object_lifetime_default` query. |
409 | fn check_object_lifetime_default(&self, hir_id: HirId) { | |
410 | let tcx = self.tcx; | |
411 | if let Some(generics) = tcx.hir().get_generics(tcx.hir().local_def_id(hir_id)) { | |
412 | for p in generics.params { | |
413 | let hir::GenericParamKind::Type { .. } = p.kind else { continue }; | |
487cf647 | 414 | let default = tcx.object_lifetime_default(p.def_id); |
f2b60f7d FG |
415 | let repr = match default { |
416 | ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(), | |
417 | ObjectLifetimeDefault::Static => "'static".to_owned(), | |
418 | ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(), | |
419 | ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(), | |
420 | }; | |
2b03887a | 421 | tcx.sess.emit_err(ObjectLifetimeErr { span: p.span, repr }); |
f2b60f7d FG |
422 | } |
423 | } | |
424 | } | |
425 | ||
426 | /// Checks if `#[collapse_debuginfo]` is applied to a macro. | |
427 | fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) -> bool { | |
428 | match target { | |
429 | Target::MacroDef => true, | |
430 | _ => { | |
431 | self.tcx | |
432 | .sess | |
433 | .emit_err(errors::CollapseDebuginfo { attr_span: attr.span, defn_span: span }); | |
434 | false | |
435 | } | |
436 | } | |
437 | } | |
438 | ||
e74abb32 XL |
439 | /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid. |
440 | fn check_track_caller( | |
441 | &self, | |
5869c6ff | 442 | hir_id: HirId, |
5099ac24 | 443 | attr_span: Span, |
a2a8927a | 444 | attrs: &[Attribute], |
5099ac24 | 445 | span: Span, |
e74abb32 XL |
446 | target: Target, |
447 | ) -> bool { | |
448 | match target { | |
fc512014 | 449 | _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => { |
064997fb | 450 | self.tcx.sess.emit_err(errors::NakedTrackedCaller { attr_span }); |
e74abb32 XL |
451 | false |
452 | } | |
1b1a35ee | 453 | Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true, |
5869c6ff XL |
454 | // FIXME(#80564): We permit struct fields, match arms and macro defs to have an |
455 | // `#[track_caller]` attribute with just a lint, because we previously | |
2b03887a | 456 | // erroneously allowed it and some crates used it accidentally, to be compatible |
5869c6ff XL |
457 | // with crates depending on them, we can't throw an error here. |
458 | Target::Field | Target::Arm | Target::MacroDef => { | |
459 | for attr in attrs { | |
460 | self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller"); | |
461 | } | |
462 | true | |
463 | } | |
e74abb32 | 464 | _ => { |
487cf647 FG |
465 | self.tcx.sess.emit_err(errors::TrackedCallerWrongLocation { |
466 | attr_span, | |
467 | defn_span: span, | |
468 | on_crate: hir_id == CRATE_HIR_ID, | |
469 | }); | |
e74abb32 XL |
470 | false |
471 | } | |
83c7162d XL |
472 | } |
473 | } | |
474 | ||
e74abb32 | 475 | /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid. |
5869c6ff XL |
476 | fn check_non_exhaustive( |
477 | &self, | |
478 | hir_id: HirId, | |
479 | attr: &Attribute, | |
5099ac24 | 480 | span: Span, |
5869c6ff XL |
481 | target: Target, |
482 | ) -> bool { | |
83c7162d | 483 | match target { |
29967ef6 | 484 | Target::Struct | Target::Enum | Target::Variant => true, |
5869c6ff XL |
485 | // FIXME(#80564): We permit struct fields, match arms and macro defs to have an |
486 | // `#[non_exhaustive]` attribute with just a lint, because we previously | |
2b03887a | 487 | // erroneously allowed it and some crates used it accidentally, to be compatible |
5869c6ff XL |
488 | // with crates depending on them, we can't throw an error here. |
489 | Target::Field | Target::Arm | Target::MacroDef => { | |
490 | self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive"); | |
491 | true | |
492 | } | |
83c7162d | 493 | _ => { |
064997fb FG |
494 | self.tcx.sess.emit_err(errors::NonExhaustiveWrongLocation { |
495 | attr_span: attr.span, | |
496 | defn_span: span, | |
497 | }); | |
e74abb32 | 498 | false |
83c7162d XL |
499 | } |
500 | } | |
b039eaaf SL |
501 | } |
502 | ||
e74abb32 | 503 | /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid. |
5099ac24 | 504 | fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { |
0bf4aa26 | 505 | match target { |
e74abb32 | 506 | Target::Trait => true, |
5869c6ff XL |
507 | // FIXME(#80564): We permit struct fields, match arms and macro defs to have an |
508 | // `#[marker]` attribute with just a lint, because we previously | |
2b03887a | 509 | // erroneously allowed it and some crates used it accidentally, to be compatible |
5869c6ff XL |
510 | // with crates depending on them, we can't throw an error here. |
511 | Target::Field | Target::Arm | Target::MacroDef => { | |
512 | self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker"); | |
513 | true | |
514 | } | |
0bf4aa26 | 515 | _ => { |
064997fb FG |
516 | self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait { |
517 | attr_span: attr.span, | |
518 | defn_span: span, | |
519 | }); | |
5099ac24 FG |
520 | false |
521 | } | |
522 | } | |
523 | } | |
524 | ||
525 | /// Checks if the `#[rustc_must_implement_one_of]` attribute on a `target` is valid. Returns `true` if valid. | |
526 | fn check_rustc_must_implement_one_of( | |
527 | &self, | |
528 | attr: &Attribute, | |
529 | span: Span, | |
530 | target: Target, | |
531 | ) -> bool { | |
532 | match target { | |
533 | Target::Trait => true, | |
534 | _ => { | |
064997fb FG |
535 | self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait { |
536 | attr_span: attr.span, | |
537 | defn_span: span, | |
538 | }); | |
e74abb32 | 539 | false |
0bf4aa26 XL |
540 | } |
541 | } | |
0bf4aa26 XL |
542 | } |
543 | ||
e74abb32 | 544 | /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid. |
1b1a35ee XL |
545 | fn check_target_feature( |
546 | &self, | |
547 | hir_id: HirId, | |
548 | attr: &Attribute, | |
5099ac24 | 549 | span: Span, |
1b1a35ee XL |
550 | target: Target, |
551 | ) -> bool { | |
e74abb32 | 552 | match target { |
dfeec247 | 553 | Target::Fn |
ba9703b0 | 554 | | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, |
1b1a35ee XL |
555 | // FIXME: #[target_feature] was previously erroneously allowed on statements and some |
556 | // crates used this, so only emit a warning. | |
557 | Target::Statement => { | |
064997fb FG |
558 | self.tcx.emit_spanned_lint( |
559 | UNUSED_ATTRIBUTES, | |
560 | hir_id, | |
561 | attr.span, | |
562 | errors::TargetFeatureOnStatement, | |
563 | ); | |
1b1a35ee XL |
564 | true |
565 | } | |
5869c6ff XL |
566 | // FIXME(#80564): We permit struct fields, match arms and macro defs to have an |
567 | // `#[target_feature]` attribute with just a lint, because we previously | |
2b03887a | 568 | // erroneously allowed it and some crates used it accidentally, to be compatible |
5869c6ff XL |
569 | // with crates depending on them, we can't throw an error here. |
570 | Target::Field | Target::Arm | Target::MacroDef => { | |
571 | self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature"); | |
572 | true | |
573 | } | |
e74abb32 | 574 | _ => { |
064997fb FG |
575 | self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { |
576 | attr_span: attr.span, | |
577 | defn_span: span, | |
487cf647 | 578 | on_crate: hir_id == CRATE_HIR_ID, |
064997fb | 579 | }); |
e74abb32 | 580 | false |
dfeec247 | 581 | } |
e74abb32 XL |
582 | } |
583 | } | |
584 | ||
04454e1e FG |
585 | /// Checks if the `#[thread_local]` attribute on `item` is valid. Returns `true` if valid. |
586 | fn check_thread_local(&self, attr: &Attribute, span: Span, target: Target) -> bool { | |
587 | match target { | |
588 | Target::ForeignStatic | Target::Static => true, | |
589 | _ => { | |
064997fb FG |
590 | self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToStatic { |
591 | attr_span: attr.span, | |
592 | defn_span: span, | |
593 | }); | |
04454e1e FG |
594 | false |
595 | } | |
596 | } | |
597 | } | |
598 | ||
fc512014 | 599 | fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) { |
064997fb | 600 | self.tcx.sess.emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name }); |
1b1a35ee XL |
601 | } |
602 | ||
6a06907d XL |
603 | fn check_doc_alias_value( |
604 | &self, | |
605 | meta: &NestedMetaItem, | |
04454e1e | 606 | doc_alias: Symbol, |
6a06907d XL |
607 | hir_id: HirId, |
608 | target: Target, | |
609 | is_list: bool, | |
c295e0f8 | 610 | aliases: &mut FxHashMap<String, Span>, |
6a06907d XL |
611 | ) -> bool { |
612 | let tcx = self.tcx; | |
064997fb FG |
613 | let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span()); |
614 | let attr_str = | |
615 | &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" }); | |
04454e1e | 616 | if doc_alias == kw::Empty { |
064997fb FG |
617 | tcx.sess.emit_err(errors::DocAliasEmpty { span, attr_str }); |
618 | return false; | |
fc512014 | 619 | } |
04454e1e FG |
620 | |
621 | let doc_alias_str = doc_alias.as_str(); | |
622 | if let Some(c) = doc_alias_str | |
623 | .chars() | |
624 | .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' ')) | |
fc512014 | 625 | { |
064997fb | 626 | tcx.sess.emit_err(errors::DocAliasBadChar { span, attr_str, char_: c }); |
fc512014 XL |
627 | return false; |
628 | } | |
04454e1e | 629 | if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') { |
064997fb FG |
630 | tcx.sess.emit_err(errors::DocAliasStartEnd { span, attr_str }); |
631 | return false; | |
fc512014 | 632 | } |
064997fb FG |
633 | |
634 | let span = meta.span(); | |
635 | if let Some(location) = match target { | |
fc512014 | 636 | Target::AssocTy => { |
2b03887a FG |
637 | let parent_def_id = self.tcx.hir().get_parent_item(hir_id).def_id; |
638 | let containing_item = self.tcx.hir().expect_item(parent_def_id); | |
fc512014 XL |
639 | if Target::from_item(containing_item) == Target::Impl { |
640 | Some("type alias in implementation block") | |
641 | } else { | |
642 | None | |
643 | } | |
644 | } | |
645 | Target::AssocConst => { | |
2b03887a FG |
646 | let parent_def_id = self.tcx.hir().get_parent_item(hir_id).def_id; |
647 | let containing_item = self.tcx.hir().expect_item(parent_def_id); | |
fc512014 XL |
648 | // We can't link to trait impl's consts. |
649 | let err = "associated constant in trait implementation block"; | |
650 | match containing_item.kind { | |
5869c6ff | 651 | ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => Some(err), |
fc512014 XL |
652 | _ => None, |
653 | } | |
654 | } | |
136023e0 XL |
655 | // we check the validity of params elsewhere |
656 | Target::Param => return false, | |
064997fb FG |
657 | Target::Expression |
658 | | Target::Statement | |
659 | | Target::Arm | |
660 | | Target::ForeignMod | |
661 | | Target::Closure | |
662 | | Target::Impl => Some(target.name()), | |
663 | Target::ExternCrate | |
664 | | Target::Use | |
665 | | Target::Static | |
666 | | Target::Const | |
667 | | Target::Fn | |
668 | | Target::Mod | |
669 | | Target::GlobalAsm | |
670 | | Target::TyAlias | |
671 | | Target::OpaqueTy | |
f2b60f7d | 672 | | Target::ImplTraitPlaceholder |
064997fb FG |
673 | | Target::Enum |
674 | | Target::Variant | |
675 | | Target::Struct | |
676 | | Target::Field | |
677 | | Target::Union | |
678 | | Target::Trait | |
679 | | Target::TraitAlias | |
680 | | Target::Method(..) | |
681 | | Target::ForeignFn | |
682 | | Target::ForeignStatic | |
683 | | Target::ForeignTy | |
684 | | Target::GenericParam(..) | |
f2b60f7d FG |
685 | | Target::MacroDef |
686 | | Target::PatField | |
687 | | Target::ExprField => None, | |
fc512014 | 688 | } { |
064997fb FG |
689 | tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location }); |
690 | return false; | |
fc512014 | 691 | } |
5869c6ff | 692 | let item_name = self.tcx.hir().name(hir_id); |
04454e1e | 693 | if item_name == doc_alias { |
064997fb FG |
694 | tcx.sess.emit_err(errors::DocAliasNotAnAlias { span, attr_str }); |
695 | return false; | |
6a06907d | 696 | } |
04454e1e | 697 | if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) { |
064997fb FG |
698 | self.tcx.emit_spanned_lint( |
699 | UNUSED_ATTRIBUTES, | |
700 | hir_id, | |
701 | span, | |
702 | errors::DocAliasDuplicated { first_defn: *entry.entry.get() }, | |
703 | ); | |
c295e0f8 | 704 | } |
6a06907d XL |
705 | true |
706 | } | |
707 | ||
c295e0f8 XL |
708 | fn check_doc_alias( |
709 | &self, | |
710 | meta: &NestedMetaItem, | |
711 | hir_id: HirId, | |
712 | target: Target, | |
713 | aliases: &mut FxHashMap<String, Span>, | |
714 | ) -> bool { | |
6a06907d XL |
715 | if let Some(values) = meta.meta_item_list() { |
716 | let mut errors = 0; | |
717 | for v in values { | |
487cf647 | 718 | match v.lit() { |
6a06907d XL |
719 | Some(l) => match l.kind { |
720 | LitKind::Str(s, _) => { | |
04454e1e | 721 | if !self.check_doc_alias_value(v, s, hir_id, target, true, aliases) { |
6a06907d XL |
722 | errors += 1; |
723 | } | |
724 | } | |
725 | _ => { | |
726 | self.tcx | |
727 | .sess | |
064997fb | 728 | .emit_err(errors::DocAliasNotStringLiteral { span: v.span() }); |
6a06907d XL |
729 | errors += 1; |
730 | } | |
731 | }, | |
732 | None => { | |
064997fb | 733 | self.tcx.sess.emit_err(errors::DocAliasNotStringLiteral { span: v.span() }); |
6a06907d XL |
734 | errors += 1; |
735 | } | |
736 | } | |
737 | } | |
738 | errors == 0 | |
04454e1e FG |
739 | } else if let Some(doc_alias) = meta.value_str() { |
740 | self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases) | |
6a06907d | 741 | } else { |
064997fb | 742 | self.tcx.sess.emit_err(errors::DocAliasMalformed { span: meta.span() }); |
6a06907d | 743 | false |
5869c6ff | 744 | } |
fc512014 XL |
745 | } |
746 | ||
747 | fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { | |
04454e1e FG |
748 | let doc_keyword = meta.value_str().unwrap_or(kw::Empty); |
749 | if doc_keyword == kw::Empty { | |
fc512014 XL |
750 | self.doc_attr_str_error(meta, "keyword"); |
751 | return false; | |
752 | } | |
136023e0 XL |
753 | match self.tcx.hir().find(hir_id).and_then(|node| match node { |
754 | hir::Node::Item(item) => Some(&item.kind), | |
755 | _ => None, | |
756 | }) { | |
757 | Some(ItemKind::Mod(ref module)) => { | |
fc512014 | 758 | if !module.item_ids.is_empty() { |
064997fb | 759 | self.tcx.sess.emit_err(errors::DocKeywordEmptyMod { span: meta.span() }); |
fc512014 XL |
760 | return false; |
761 | } | |
762 | } | |
763 | _ => { | |
064997fb | 764 | self.tcx.sess.emit_err(errors::DocKeywordNotMod { span: meta.span() }); |
fc512014 XL |
765 | return false; |
766 | } | |
767 | } | |
04454e1e | 768 | if !rustc_lexer::is_ident(doc_keyword.as_str()) { |
064997fb FG |
769 | self.tcx.sess.emit_err(errors::DocKeywordInvalidIdent { |
770 | span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()), | |
771 | doc_keyword, | |
772 | }); | |
fc512014 XL |
773 | return false; |
774 | } | |
775 | true | |
776 | } | |
777 | ||
064997fb | 778 | fn check_doc_fake_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { |
923072b8 FG |
779 | match self.tcx.hir().find(hir_id).and_then(|node| match node { |
780 | hir::Node::Item(item) => Some(&item.kind), | |
781 | _ => None, | |
782 | }) { | |
783 | Some(ItemKind::Impl(ref i)) => { | |
064997fb FG |
784 | let is_valid = matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) |
785 | || if let hir::TyKind::BareFn(bare_fn_ty) = &i.self_ty.kind { | |
786 | bare_fn_ty.decl.inputs.len() == 1 | |
787 | } else { | |
788 | false | |
789 | }; | |
790 | if !is_valid { | |
791 | self.tcx.sess.emit_err(errors::DocFakeVariadicNotValid { span: meta.span() }); | |
923072b8 FG |
792 | return false; |
793 | } | |
794 | } | |
795 | _ => { | |
064997fb | 796 | self.tcx.sess.emit_err(errors::DocKeywordOnlyImpl { span: meta.span() }); |
923072b8 FG |
797 | return false; |
798 | } | |
799 | } | |
800 | true | |
801 | } | |
802 | ||
17df50a5 XL |
803 | /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. Returns `true` if valid. |
804 | /// | |
805 | /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or | |
806 | /// if there are conflicting attributes for one item. | |
807 | /// | |
808 | /// `specified_inline` is used to keep track of whether we have | |
809 | /// already seen an inlining attribute for this item. | |
810 | /// If so, `specified_inline` holds the value and the span of | |
811 | /// the first `inline`/`no_inline` attribute. | |
812 | fn check_doc_inline( | |
813 | &self, | |
814 | attr: &Attribute, | |
815 | meta: &NestedMetaItem, | |
816 | hir_id: HirId, | |
817 | target: Target, | |
818 | specified_inline: &mut Option<(bool, Span)>, | |
819 | ) -> bool { | |
820 | if target == Target::Use || target == Target::ExternCrate { | |
821 | let do_inline = meta.name_or_empty() == sym::inline; | |
822 | if let Some((prev_inline, prev_span)) = *specified_inline { | |
823 | if do_inline != prev_inline { | |
824 | let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]); | |
2b03887a FG |
825 | spans.push_span_label(prev_span, fluent::passes_doc_inline_conflict_first); |
826 | spans.push_span_label(meta.span(), fluent::passes_doc_inline_conflict_second); | |
064997fb | 827 | self.tcx.sess.emit_err(errors::DocKeywordConflict { spans }); |
17df50a5 XL |
828 | return false; |
829 | } | |
830 | true | |
831 | } else { | |
832 | *specified_inline = Some((do_inline, meta.span())); | |
833 | true | |
834 | } | |
835 | } else { | |
064997fb | 836 | self.tcx.emit_spanned_lint( |
17df50a5 XL |
837 | INVALID_DOC_ATTRIBUTES, |
838 | hir_id, | |
839 | meta.span(), | |
064997fb FG |
840 | errors::DocInlineOnlyUse { |
841 | attr_span: meta.span(), | |
842 | item_span: (attr.style == AttrStyle::Outer) | |
843 | .then(|| self.tcx.hir().span(hir_id)), | |
17df50a5 XL |
844 | }, |
845 | ); | |
846 | false | |
847 | } | |
848 | } | |
849 | ||
850 | /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid. | |
851 | fn check_attr_not_crate_level( | |
fc512014 XL |
852 | &self, |
853 | meta: &NestedMetaItem, | |
854 | hir_id: HirId, | |
855 | attr_name: &str, | |
856 | ) -> bool { | |
857 | if CRATE_HIR_ID == hir_id { | |
064997fb | 858 | self.tcx.sess.emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name }); |
fc512014 XL |
859 | return false; |
860 | } | |
861 | true | |
862 | } | |
863 | ||
17df50a5 XL |
864 | /// Checks that an attribute is used at the crate level. Returns `true` if valid. |
865 | fn check_attr_crate_level( | |
866 | &self, | |
867 | attr: &Attribute, | |
868 | meta: &NestedMetaItem, | |
869 | hir_id: HirId, | |
870 | ) -> bool { | |
871 | if hir_id != CRATE_HIR_ID { | |
2b03887a FG |
872 | self.tcx.struct_span_lint_hir( |
873 | INVALID_DOC_ATTRIBUTES, | |
874 | hir_id, | |
875 | meta.span(), | |
876 | fluent::passes_attr_crate_level, | |
877 | |err| { | |
878 | if attr.style == AttrStyle::Outer | |
879 | && self.tcx.hir().get_parent_item(hir_id) == CRATE_OWNER_ID | |
880 | { | |
881 | if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) { | |
882 | src.insert(1, '!'); | |
883 | err.span_suggestion_verbose( | |
884 | attr.span, | |
885 | fluent::suggestion, | |
886 | src, | |
887 | Applicability::MaybeIncorrect, | |
888 | ); | |
889 | } else { | |
890 | err.span_help(attr.span, fluent::help); | |
891 | } | |
17df50a5 | 892 | } |
2b03887a FG |
893 | err.note(fluent::note); |
894 | err | |
895 | }, | |
896 | ); | |
17df50a5 XL |
897 | return false; |
898 | } | |
899 | true | |
900 | } | |
901 | ||
94222f64 XL |
902 | /// Checks that `doc(test(...))` attribute contains only valid attributes. Returns `true` if |
903 | /// valid. | |
904 | fn check_test_attr(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { | |
905 | let mut is_valid = true; | |
906 | if let Some(metas) = meta.meta_item_list() { | |
907 | for i_meta in metas { | |
908 | match i_meta.name_or_empty() { | |
909 | sym::attr | sym::no_crate_inject => {} | |
910 | _ => { | |
064997fb | 911 | self.tcx.emit_spanned_lint( |
94222f64 XL |
912 | INVALID_DOC_ATTRIBUTES, |
913 | hir_id, | |
914 | i_meta.span(), | |
064997fb FG |
915 | errors::DocTestUnknown { |
916 | path: rustc_ast_pretty::pprust::path_to_string( | |
917 | &i_meta.meta_item().unwrap().path, | |
918 | ), | |
94222f64 XL |
919 | }, |
920 | ); | |
921 | is_valid = false; | |
922 | } | |
923 | } | |
924 | } | |
925 | } else { | |
064997fb FG |
926 | self.tcx.emit_spanned_lint( |
927 | INVALID_DOC_ATTRIBUTES, | |
928 | hir_id, | |
929 | meta.span(), | |
930 | errors::DocTestTakesList, | |
931 | ); | |
94222f64 XL |
932 | is_valid = false; |
933 | } | |
934 | is_valid | |
935 | } | |
936 | ||
2b03887a FG |
937 | /// Check that the `#![doc(cfg_hide(...))]` attribute only contains a list of attributes. |
938 | /// Returns `true` if valid. | |
939 | fn check_doc_cfg_hide(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { | |
940 | if meta.meta_item_list().is_some() { | |
941 | true | |
942 | } else { | |
943 | self.tcx.emit_spanned_lint( | |
944 | INVALID_DOC_ATTRIBUTES, | |
945 | hir_id, | |
946 | meta.span(), | |
947 | errors::DocCfgHideTakesList, | |
948 | ); | |
949 | false | |
950 | } | |
951 | } | |
952 | ||
17df50a5 XL |
953 | /// Runs various checks on `#[doc]` attributes. Returns `true` if valid. |
954 | /// | |
955 | /// `specified_inline` should be initialized to `None` and kept for the scope | |
956 | /// of one item. Read the documentation of [`check_doc_inline`] for more information. | |
957 | /// | |
958 | /// [`check_doc_inline`]: Self::check_doc_inline | |
959 | fn check_doc_attrs( | |
960 | &self, | |
961 | attr: &Attribute, | |
962 | hir_id: HirId, | |
963 | target: Target, | |
964 | specified_inline: &mut Option<(bool, Span)>, | |
c295e0f8 | 965 | aliases: &mut FxHashMap<String, Span>, |
17df50a5 | 966 | ) -> bool { |
6a06907d XL |
967 | let mut is_valid = true; |
968 | ||
04454e1e FG |
969 | if let Some(mi) = attr.meta() && let Some(list) = mi.meta_item_list() { |
970 | for meta in list { | |
6a06907d XL |
971 | if let Some(i_meta) = meta.meta_item() { |
972 | match i_meta.name_or_empty() { | |
973 | sym::alias | |
04454e1e FG |
974 | if !self.check_attr_not_crate_level(meta, hir_id, "alias") |
975 | || !self.check_doc_alias(meta, hir_id, target, aliases) => | |
3dfed10e | 976 | { |
6a06907d | 977 | is_valid = false |
29967ef6 | 978 | } |
6a06907d XL |
979 | |
980 | sym::keyword | |
04454e1e FG |
981 | if !self.check_attr_not_crate_level(meta, hir_id, "keyword") |
982 | || !self.check_doc_keyword(meta, hir_id) => | |
fc512014 | 983 | { |
6a06907d XL |
984 | is_valid = false |
985 | } | |
986 | ||
064997fb FG |
987 | sym::fake_variadic |
988 | if !self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") | |
989 | || !self.check_doc_fake_variadic(meta, hir_id) => | |
923072b8 FG |
990 | { |
991 | is_valid = false | |
992 | } | |
993 | ||
17df50a5 XL |
994 | sym::html_favicon_url |
995 | | sym::html_logo_url | |
996 | | sym::html_playground_url | |
997 | | sym::issue_tracker_base_url | |
998 | | sym::html_root_url | |
999 | | sym::html_no_source | |
1000 | | sym::test | |
04454e1e | 1001 | if !self.check_attr_crate_level(attr, meta, hir_id) => |
17df50a5 XL |
1002 | { |
1003 | is_valid = false; | |
1004 | } | |
1005 | ||
2b03887a FG |
1006 | sym::cfg_hide |
1007 | if !self.check_attr_crate_level(attr, meta, hir_id) | |
1008 | || !self.check_doc_cfg_hide(meta, hir_id) => | |
1009 | { | |
1010 | is_valid = false; | |
1011 | } | |
1012 | ||
17df50a5 XL |
1013 | sym::inline | sym::no_inline |
1014 | if !self.check_doc_inline( | |
04454e1e FG |
1015 | attr, |
1016 | meta, | |
6a06907d | 1017 | hir_id, |
17df50a5 XL |
1018 | target, |
1019 | specified_inline, | |
1020 | ) => | |
1021 | { | |
6a06907d XL |
1022 | is_valid = false; |
1023 | } | |
1024 | ||
1025 | // no_default_passes: deprecated | |
1026 | // passes: deprecated | |
1027 | // plugins: removed, but rustdoc warns about it itself | |
1028 | sym::alias | |
1029 | | sym::cfg | |
c295e0f8 | 1030 | | sym::cfg_hide |
6a06907d XL |
1031 | | sym::hidden |
1032 | | sym::html_favicon_url | |
1033 | | sym::html_logo_url | |
1034 | | sym::html_no_source | |
1035 | | sym::html_playground_url | |
1036 | | sym::html_root_url | |
6a06907d XL |
1037 | | sym::inline |
1038 | | sym::issue_tracker_base_url | |
1039 | | sym::keyword | |
1040 | | sym::masked | |
1041 | | sym::no_default_passes | |
1042 | | sym::no_inline | |
cdc7bbd5 | 1043 | | sym::notable_trait |
6a06907d | 1044 | | sym::passes |
923072b8 | 1045 | | sym::plugins |
064997fb | 1046 | | sym::fake_variadic => {} |
94222f64 XL |
1047 | |
1048 | sym::test => { | |
04454e1e | 1049 | if !self.check_test_attr(meta, hir_id) { |
94222f64 XL |
1050 | is_valid = false; |
1051 | } | |
1052 | } | |
1053 | ||
1054 | sym::primitive => { | |
3c0e092e | 1055 | if !self.tcx.features().rustdoc_internals { |
064997fb | 1056 | self.tcx.emit_spanned_lint( |
94222f64 XL |
1057 | INVALID_DOC_ATTRIBUTES, |
1058 | hir_id, | |
1059 | i_meta.span, | |
064997fb | 1060 | errors::DocPrimitive, |
94222f64 XL |
1061 | ); |
1062 | } | |
1063 | } | |
6a06907d XL |
1064 | |
1065 | _ => { | |
064997fb FG |
1066 | let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path); |
1067 | if i_meta.has_name(sym::spotlight) { | |
1068 | self.tcx.emit_spanned_lint( | |
1069 | INVALID_DOC_ATTRIBUTES, | |
1070 | hir_id, | |
1071 | i_meta.span, | |
1072 | errors::DocTestUnknownSpotlight { | |
1073 | path, | |
1074 | span: i_meta.span | |
cdc7bbd5 | 1075 | } |
064997fb FG |
1076 | ); |
1077 | } else if i_meta.has_name(sym::include) && | |
1078 | let Some(value) = i_meta.value_str() { | |
1079 | let applicability = if list.len() == 1 { | |
1080 | Applicability::MachineApplicable | |
1081 | } else { | |
1082 | Applicability::MaybeIncorrect | |
1083 | }; | |
1084 | // If there are multiple attributes, the suggestion would suggest | |
1085 | // deleting all of them, which is incorrect. | |
1086 | self.tcx.emit_spanned_lint( | |
1087 | INVALID_DOC_ATTRIBUTES, | |
1088 | hir_id, | |
1089 | i_meta.span, | |
1090 | errors::DocTestUnknownInclude { | |
1091 | path, | |
1092 | value: value.to_string(), | |
1093 | inner: (attr.style == AttrStyle::Inner) | |
1094 | .then_some("!") | |
1095 | .unwrap_or(""), | |
1096 | sugg: (attr.meta().unwrap().span, applicability), | |
17df50a5 | 1097 | } |
064997fb FG |
1098 | ); |
1099 | } else { | |
1100 | self.tcx.emit_spanned_lint( | |
1101 | INVALID_DOC_ATTRIBUTES, | |
1102 | hir_id, | |
1103 | i_meta.span, | |
1104 | errors::DocTestUnknownAny { path } | |
1105 | ); | |
1106 | } | |
6a06907d | 1107 | is_valid = false; |
29967ef6 | 1108 | } |
3dfed10e | 1109 | } |
6a06907d | 1110 | } else { |
064997fb | 1111 | self.tcx.emit_spanned_lint( |
6a06907d XL |
1112 | INVALID_DOC_ATTRIBUTES, |
1113 | hir_id, | |
1114 | meta.span(), | |
064997fb | 1115 | errors::DocInvalid, |
6a06907d XL |
1116 | ); |
1117 | is_valid = false; | |
3dfed10e XL |
1118 | } |
1119 | } | |
1120 | } | |
6a06907d XL |
1121 | |
1122 | is_valid | |
3dfed10e XL |
1123 | } |
1124 | ||
5099ac24 FG |
1125 | /// Warns against some misuses of `#[pass_by_value]` |
1126 | fn check_pass_by_value(&self, attr: &Attribute, span: Span, target: Target) -> bool { | |
1127 | match target { | |
1128 | Target::Struct | Target::Enum | Target::TyAlias => true, | |
1129 | _ => { | |
064997fb | 1130 | self.tcx.sess.emit_err(errors::PassByValue { attr_span: attr.span, span }); |
5099ac24 FG |
1131 | false |
1132 | } | |
1133 | } | |
1134 | } | |
1135 | ||
5e7ed085 FG |
1136 | fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) -> bool { |
1137 | match target { | |
1138 | Target::Method(MethodKind::Inherent) => true, | |
1139 | _ => { | |
064997fb | 1140 | self.tcx.sess.emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span }); |
5e7ed085 | 1141 | false |
3c0e092e XL |
1142 | } |
1143 | } | |
5e7ed085 FG |
1144 | } |
1145 | ||
04454e1e FG |
1146 | fn check_has_incoherent_inherent_impls( |
1147 | &self, | |
1148 | attr: &Attribute, | |
1149 | span: Span, | |
1150 | target: Target, | |
1151 | ) -> bool { | |
1152 | match target { | |
1153 | Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => { | |
1154 | true | |
1155 | } | |
1156 | _ => { | |
1157 | self.tcx | |
1158 | .sess | |
064997fb | 1159 | .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span }); |
04454e1e FG |
1160 | false |
1161 | } | |
1162 | } | |
1163 | } | |
1164 | ||
5e7ed085 | 1165 | /// Warns against some misuses of `#[must_use]` |
487cf647 | 1166 | fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) -> bool { |
5e7ed085 FG |
1167 | if !matches!( |
1168 | target, | |
1169 | Target::Fn | |
1170 | | Target::Enum | |
1171 | | Target::Struct | |
1172 | | Target::Union | |
1173 | | Target::Method(_) | |
1174 | | Target::ForeignFn | |
1175 | // `impl Trait` in return position can trip | |
1176 | // `unused_must_use` if `Trait` is marked as | |
1177 | // `#[must_use]` | |
1178 | | Target::Trait | |
1179 | ) { | |
1180 | let article = match target { | |
1181 | Target::ExternCrate | |
1182 | | Target::OpaqueTy | |
1183 | | Target::Enum | |
1184 | | Target::Impl | |
1185 | | Target::Expression | |
1186 | | Target::Arm | |
1187 | | Target::AssocConst | |
1188 | | Target::AssocTy => "an", | |
1189 | _ => "a", | |
1190 | }; | |
1191 | ||
064997fb FG |
1192 | self.tcx.emit_spanned_lint( |
1193 | UNUSED_ATTRIBUTES, | |
1194 | hir_id, | |
1195 | attr.span, | |
1196 | errors::MustUseNoEffect { article, target }, | |
1197 | ); | |
5e7ed085 | 1198 | } |
3c0e092e XL |
1199 | |
1200 | // For now, its always valid | |
1201 | true | |
1202 | } | |
1203 | ||
c295e0f8 | 1204 | /// Checks if `#[must_not_suspend]` is applied to a function. Returns `true` if valid. |
5099ac24 | 1205 | fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) -> bool { |
c295e0f8 XL |
1206 | match target { |
1207 | Target::Struct | Target::Enum | Target::Union | Target::Trait => true, | |
1208 | _ => { | |
064997fb | 1209 | self.tcx.sess.emit_err(errors::MustNotSuspend { attr_span: attr.span, span }); |
c295e0f8 XL |
1210 | false |
1211 | } | |
1212 | } | |
1213 | } | |
1214 | ||
1b1a35ee | 1215 | /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid. |
5099ac24 | 1216 | fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { |
1b1a35ee XL |
1217 | match target { |
1218 | Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {} | |
5869c6ff XL |
1219 | // FIXME(#80564): We permit struct fields, match arms and macro defs to have an |
1220 | // `#[cold]` attribute with just a lint, because we previously | |
2b03887a | 1221 | // erroneously allowed it and some crates used it accidentally, to be compatible |
5869c6ff XL |
1222 | // with crates depending on them, we can't throw an error here. |
1223 | Target::Field | Target::Arm | Target::MacroDef => { | |
1224 | self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold"); | |
1225 | } | |
1b1a35ee XL |
1226 | _ => { |
1227 | // FIXME: #[cold] was previously allowed on non-functions and some crates used | |
1228 | // this, so only emit a warning. | |
064997fb FG |
1229 | self.tcx.emit_spanned_lint( |
1230 | UNUSED_ATTRIBUTES, | |
1231 | hir_id, | |
1232 | attr.span, | |
487cf647 | 1233 | errors::Cold { span, on_crate: hir_id == CRATE_HIR_ID }, |
064997fb | 1234 | ); |
1b1a35ee XL |
1235 | } |
1236 | } | |
1237 | } | |
1238 | ||
5099ac24 FG |
1239 | /// Checks if `#[link]` is applied to an item other than a foreign module. |
1240 | fn check_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { | |
923072b8 FG |
1241 | if target == Target::ForeignMod |
1242 | && let hir::Node::Item(item) = self.tcx.hir().get(hir_id) | |
1243 | && let Item { kind: ItemKind::ForeignMod { abi, .. }, .. } = item | |
1244 | && !matches!(abi, Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic) | |
1245 | { | |
1246 | return; | |
1247 | } | |
5099ac24 | 1248 | |
064997fb FG |
1249 | self.tcx.emit_spanned_lint( |
1250 | UNUSED_ATTRIBUTES, | |
1251 | hir_id, | |
1252 | attr.span, | |
1253 | errors::Link { span: (target != Target::ForeignMod).then_some(span) }, | |
1254 | ); | |
5099ac24 FG |
1255 | } |
1256 | ||
1b1a35ee | 1257 | /// Checks if `#[link_name]` is applied to an item other than a foreign function or static. |
5099ac24 | 1258 | fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { |
1b1a35ee XL |
1259 | match target { |
1260 | Target::ForeignFn | Target::ForeignStatic => {} | |
5869c6ff XL |
1261 | // FIXME(#80564): We permit struct fields, match arms and macro defs to have an |
1262 | // `#[link_name]` attribute with just a lint, because we previously | |
2b03887a | 1263 | // erroneously allowed it and some crates used it accidentally, to be compatible |
5869c6ff XL |
1264 | // with crates depending on them, we can't throw an error here. |
1265 | Target::Field | Target::Arm | Target::MacroDef => { | |
1266 | self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name"); | |
1267 | } | |
1b1a35ee XL |
1268 | _ => { |
1269 | // FIXME: #[cold] was previously allowed on non-functions/statics and some crates | |
1270 | // used this, so only emit a warning. | |
064997fb FG |
1271 | let attr_span = matches!(target, Target::ForeignMod).then_some(attr.span); |
1272 | if let Some(s) = attr.value_str() { | |
1273 | self.tcx.emit_spanned_lint( | |
1274 | UNUSED_ATTRIBUTES, | |
1275 | hir_id, | |
1276 | attr.span, | |
1277 | errors::LinkName { span, attr_span, value: s.as_str() }, | |
1b1a35ee | 1278 | ); |
064997fb FG |
1279 | } else { |
1280 | self.tcx.emit_spanned_lint( | |
1281 | UNUSED_ATTRIBUTES, | |
1282 | hir_id, | |
1283 | attr.span, | |
1284 | errors::LinkName { span, attr_span, value: "..." }, | |
1285 | ); | |
1286 | }; | |
1b1a35ee XL |
1287 | } |
1288 | } | |
1289 | } | |
1290 | ||
1291 | /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid. | |
5099ac24 | 1292 | fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { |
5869c6ff XL |
1293 | match target { |
1294 | Target::ExternCrate => true, | |
1295 | // FIXME(#80564): We permit struct fields, match arms and macro defs to have an | |
1296 | // `#[no_link]` attribute with just a lint, because we previously | |
2b03887a | 1297 | // erroneously allowed it and some crates used it accidentally, to be compatible |
5869c6ff XL |
1298 | // with crates depending on them, we can't throw an error here. |
1299 | Target::Field | Target::Arm | Target::MacroDef => { | |
1300 | self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link"); | |
1301 | true | |
1302 | } | |
1303 | _ => { | |
064997fb | 1304 | self.tcx.sess.emit_err(errors::NoLink { attr_span: attr.span, span }); |
5869c6ff XL |
1305 | false |
1306 | } | |
1b1a35ee XL |
1307 | } |
1308 | } | |
1309 | ||
94222f64 XL |
1310 | fn is_impl_item(&self, hir_id: HirId) -> bool { |
1311 | matches!(self.tcx.hir().get(hir_id), hir::Node::ImplItem(..)) | |
1312 | } | |
1313 | ||
1b1a35ee | 1314 | /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid. |
5869c6ff XL |
1315 | fn check_export_name( |
1316 | &self, | |
1317 | hir_id: HirId, | |
1318 | attr: &Attribute, | |
5099ac24 | 1319 | span: Span, |
5869c6ff XL |
1320 | target: Target, |
1321 | ) -> bool { | |
1b1a35ee | 1322 | match target { |
94222f64 XL |
1323 | Target::Static | Target::Fn => true, |
1324 | Target::Method(..) if self.is_impl_item(hir_id) => true, | |
5869c6ff XL |
1325 | // FIXME(#80564): We permit struct fields, match arms and macro defs to have an |
1326 | // `#[export_name]` attribute with just a lint, because we previously | |
2b03887a | 1327 | // erroneously allowed it and some crates used it accidentally, to be compatible |
5869c6ff XL |
1328 | // with crates depending on them, we can't throw an error here. |
1329 | Target::Field | Target::Arm | Target::MacroDef => { | |
1330 | self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name"); | |
1331 | true | |
1332 | } | |
1b1a35ee | 1333 | _ => { |
064997fb | 1334 | self.tcx.sess.emit_err(errors::ExportName { attr_span: attr.span, span }); |
1b1a35ee XL |
1335 | false |
1336 | } | |
1337 | } | |
1338 | } | |
1339 | ||
6a06907d XL |
1340 | fn check_rustc_layout_scalar_valid_range( |
1341 | &self, | |
1342 | attr: &Attribute, | |
5099ac24 | 1343 | span: Span, |
6a06907d XL |
1344 | target: Target, |
1345 | ) -> bool { | |
1346 | if target != Target::Struct { | |
064997fb FG |
1347 | self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeNotStruct { |
1348 | attr_span: attr.span, | |
1349 | span, | |
1350 | }); | |
6a06907d XL |
1351 | return false; |
1352 | } | |
1353 | ||
5e7ed085 FG |
1354 | let Some(list) = attr.meta_item_list() else { |
1355 | return false; | |
6a06907d XL |
1356 | }; |
1357 | ||
487cf647 | 1358 | if matches!(&list[..], &[NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Int(..), .. })]) { |
6a06907d XL |
1359 | true |
1360 | } else { | |
064997fb | 1361 | self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span }); |
6a06907d XL |
1362 | false |
1363 | } | |
1364 | } | |
1365 | ||
1366 | /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument. | |
1367 | fn check_rustc_legacy_const_generics( | |
1368 | &self, | |
487cf647 | 1369 | hir_id: HirId, |
6a06907d | 1370 | attr: &Attribute, |
5099ac24 | 1371 | span: Span, |
6a06907d XL |
1372 | target: Target, |
1373 | item: Option<ItemLike<'_>>, | |
1374 | ) -> bool { | |
5e7ed085 | 1375 | let is_function = matches!(target, Target::Fn); |
6a06907d | 1376 | if !is_function { |
064997fb FG |
1377 | self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { |
1378 | attr_span: attr.span, | |
1379 | defn_span: span, | |
487cf647 | 1380 | on_crate: hir_id == CRATE_HIR_ID, |
064997fb | 1381 | }); |
6a06907d XL |
1382 | return false; |
1383 | } | |
1384 | ||
5e7ed085 | 1385 | let Some(list) = attr.meta_item_list() else { |
6a06907d | 1386 | // The attribute form is validated on AST. |
5e7ed085 | 1387 | return false; |
6a06907d XL |
1388 | }; |
1389 | ||
5e7ed085 FG |
1390 | let Some(ItemLike::Item(Item { |
1391 | kind: ItemKind::Fn(FnSig { decl, .. }, generics, _), | |
1392 | .. | |
1393 | })) = item else { | |
1394 | bug!("should be a function item"); | |
6a06907d XL |
1395 | }; |
1396 | ||
1397 | for param in generics.params { | |
1398 | match param.kind { | |
1399 | hir::GenericParamKind::Const { .. } => {} | |
1400 | _ => { | |
064997fb FG |
1401 | self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsOnly { |
1402 | attr_span: attr.span, | |
1403 | param_span: param.span, | |
1404 | }); | |
6a06907d XL |
1405 | return false; |
1406 | } | |
1407 | } | |
1408 | } | |
1409 | ||
1410 | if list.len() != generics.params.len() { | |
064997fb FG |
1411 | self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndex { |
1412 | attr_span: attr.span, | |
1413 | generics_span: generics.span, | |
1414 | }); | |
6a06907d XL |
1415 | return false; |
1416 | } | |
1417 | ||
1418 | let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128; | |
1419 | let mut invalid_args = vec![]; | |
1420 | for meta in list { | |
487cf647 | 1421 | if let Some(LitKind::Int(val, _)) = meta.lit().map(|lit| &lit.kind) { |
6a06907d XL |
1422 | if *val >= arg_count { |
1423 | let span = meta.span(); | |
064997fb FG |
1424 | self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexExceed { |
1425 | span, | |
1426 | arg_count: arg_count as usize, | |
1427 | }); | |
6a06907d XL |
1428 | return false; |
1429 | } | |
1430 | } else { | |
1431 | invalid_args.push(meta.span()); | |
1432 | } | |
1433 | } | |
1434 | ||
1435 | if !invalid_args.is_empty() { | |
064997fb | 1436 | self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexNegative { invalid_args }); |
6a06907d XL |
1437 | false |
1438 | } else { | |
1439 | true | |
1440 | } | |
1441 | } | |
1442 | ||
923072b8 FG |
1443 | /// Helper function for checking that the provided attribute is only applied to a function or |
1444 | /// method. | |
487cf647 FG |
1445 | fn check_applied_to_fn_or_method( |
1446 | &self, | |
1447 | hir_id: HirId, | |
1448 | attr: &Attribute, | |
1449 | span: Span, | |
1450 | target: Target, | |
1451 | ) -> bool { | |
5099ac24 FG |
1452 | let is_function = matches!(target, Target::Fn | Target::Method(..)); |
1453 | if !is_function { | |
064997fb FG |
1454 | self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { |
1455 | attr_span: attr.span, | |
1456 | defn_span: span, | |
487cf647 | 1457 | on_crate: hir_id == CRATE_HIR_ID, |
064997fb | 1458 | }); |
5099ac24 FG |
1459 | false |
1460 | } else { | |
1461 | true | |
1462 | } | |
1463 | } | |
1464 | ||
923072b8 FG |
1465 | /// Checks that the `#[rustc_lint_query_instability]` attribute is only applied to a function |
1466 | /// or method. | |
1467 | fn check_rustc_lint_query_instability( | |
1468 | &self, | |
487cf647 | 1469 | hir_id: HirId, |
923072b8 FG |
1470 | attr: &Attribute, |
1471 | span: Span, | |
1472 | target: Target, | |
1473 | ) -> bool { | |
487cf647 | 1474 | self.check_applied_to_fn_or_method(hir_id, attr, span, target) |
923072b8 FG |
1475 | } |
1476 | ||
1477 | /// Checks that the `#[rustc_lint_diagnostics]` attribute is only applied to a function or | |
1478 | /// method. | |
487cf647 FG |
1479 | fn check_rustc_lint_diagnostics( |
1480 | &self, | |
1481 | hir_id: HirId, | |
1482 | attr: &Attribute, | |
1483 | span: Span, | |
1484 | target: Target, | |
1485 | ) -> bool { | |
1486 | self.check_applied_to_fn_or_method(hir_id, attr, span, target) | |
923072b8 FG |
1487 | } |
1488 | ||
064997fb FG |
1489 | /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct. |
1490 | fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) -> bool { | |
1491 | match target { | |
1492 | Target::Struct => true, | |
1493 | _ => { | |
1494 | self.tcx.sess.emit_err(errors::RustcLintOptTy { attr_span: attr.span, span }); | |
1495 | false | |
1496 | } | |
1497 | } | |
1498 | } | |
1499 | ||
1500 | /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field. | |
1501 | fn check_rustc_lint_opt_deny_field_access( | |
1502 | &self, | |
1503 | attr: &Attribute, | |
1504 | span: Span, | |
1505 | target: Target, | |
1506 | ) -> bool { | |
1507 | match target { | |
1508 | Target::Field => true, | |
1509 | _ => { | |
1510 | self.tcx | |
1511 | .sess | |
1512 | .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span }); | |
1513 | false | |
1514 | } | |
1515 | } | |
1516 | } | |
1517 | ||
cdc7bbd5 XL |
1518 | /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph |
1519 | /// option is passed to the compiler. | |
1520 | fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool { | |
064997fb | 1521 | if self.tcx.sess.opts.unstable_opts.query_dep_graph { |
cdc7bbd5 XL |
1522 | true |
1523 | } else { | |
064997fb | 1524 | self.tcx.sess.emit_err(errors::RustcDirtyClean { span: attr.span }); |
cdc7bbd5 XL |
1525 | false |
1526 | } | |
1527 | } | |
1528 | ||
1b1a35ee | 1529 | /// Checks if `#[link_section]` is applied to a function or static. |
5099ac24 | 1530 | fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { |
1b1a35ee XL |
1531 | match target { |
1532 | Target::Static | Target::Fn | Target::Method(..) => {} | |
5869c6ff XL |
1533 | // FIXME(#80564): We permit struct fields, match arms and macro defs to have an |
1534 | // `#[link_section]` attribute with just a lint, because we previously | |
2b03887a | 1535 | // erroneously allowed it and some crates used it accidentally, to be compatible |
5869c6ff XL |
1536 | // with crates depending on them, we can't throw an error here. |
1537 | Target::Field | Target::Arm | Target::MacroDef => { | |
1538 | self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section"); | |
1539 | } | |
1b1a35ee XL |
1540 | _ => { |
1541 | // FIXME: #[link_section] was previously allowed on non-functions/statics and some | |
1542 | // crates used this, so only emit a warning. | |
064997fb FG |
1543 | self.tcx.emit_spanned_lint( |
1544 | UNUSED_ATTRIBUTES, | |
1545 | hir_id, | |
1546 | attr.span, | |
1547 | errors::LinkSection { span }, | |
1548 | ); | |
1b1a35ee XL |
1549 | } |
1550 | } | |
1551 | } | |
1552 | ||
1553 | /// Checks if `#[no_mangle]` is applied to a function or static. | |
5099ac24 | 1554 | fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { |
1b1a35ee | 1555 | match target { |
94222f64 XL |
1556 | Target::Static | Target::Fn => {} |
1557 | Target::Method(..) if self.is_impl_item(hir_id) => {} | |
5869c6ff XL |
1558 | // FIXME(#80564): We permit struct fields, match arms and macro defs to have an |
1559 | // `#[no_mangle]` attribute with just a lint, because we previously | |
2b03887a | 1560 | // erroneously allowed it and some crates used it accidentally, to be compatible |
5869c6ff XL |
1561 | // with crates depending on them, we can't throw an error here. |
1562 | Target::Field | Target::Arm | Target::MacroDef => { | |
1563 | self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle"); | |
1564 | } | |
94222f64 XL |
1565 | // FIXME: #[no_mangle] was previously allowed on non-functions/statics, this should be an error |
1566 | // The error should specify that the item that is wrong is specifically a *foreign* fn/static | |
1567 | // otherwise the error seems odd | |
1568 | Target::ForeignFn | Target::ForeignStatic => { | |
1569 | let foreign_item_kind = match target { | |
1570 | Target::ForeignFn => "function", | |
1571 | Target::ForeignStatic => "static", | |
1572 | _ => unreachable!(), | |
1573 | }; | |
064997fb FG |
1574 | self.tcx.emit_spanned_lint( |
1575 | UNUSED_ATTRIBUTES, | |
1576 | hir_id, | |
1577 | attr.span, | |
1578 | errors::NoMangleForeign { span, attr_span: attr.span, foreign_item_kind }, | |
1579 | ); | |
94222f64 | 1580 | } |
1b1a35ee XL |
1581 | _ => { |
1582 | // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some | |
1583 | // crates used this, so only emit a warning. | |
064997fb FG |
1584 | self.tcx.emit_spanned_lint( |
1585 | UNUSED_ATTRIBUTES, | |
1586 | hir_id, | |
1587 | attr.span, | |
1588 | errors::NoMangle { span }, | |
1589 | ); | |
1b1a35ee XL |
1590 | } |
1591 | } | |
1592 | } | |
1593 | ||
9fa01778 | 1594 | /// Checks if the `#[repr]` attributes on `item` are valid. |
e74abb32 XL |
1595 | fn check_repr( |
1596 | &self, | |
a2a8927a | 1597 | attrs: &[Attribute], |
5099ac24 | 1598 | span: Span, |
e74abb32 | 1599 | target: Target, |
1b1a35ee | 1600 | item: Option<ItemLike<'_>>, |
dfeec247 | 1601 | hir_id: HirId, |
e74abb32 | 1602 | ) { |
2c00a5a8 XL |
1603 | // Extract the names of all repr hints, e.g., [foo, bar, align] for: |
1604 | // ``` | |
1605 | // #[repr(foo)] | |
1606 | // #[repr(bar, align(8))] | |
1607 | // ``` | |
e74abb32 | 1608 | let hints: Vec<_> = attrs |
2c00a5a8 | 1609 | .iter() |
cdc7bbd5 | 1610 | .filter(|attr| attr.has_name(sym::repr)) |
2c00a5a8 | 1611 | .filter_map(|attr| attr.meta_item_list()) |
b7449926 | 1612 | .flatten() |
2c00a5a8 | 1613 | .collect(); |
9e0c209e | 1614 | |
ff7c6d11 XL |
1615 | let mut int_reprs = 0; |
1616 | let mut is_c = false; | |
1617 | let mut is_simd = false; | |
2c00a5a8 XL |
1618 | let mut is_transparent = false; |
1619 | ||
1620 | for hint in &hints { | |
136023e0 | 1621 | if !hint.is_meta_item() { |
064997fb | 1622 | self.tcx.sess.emit_err(errors::ReprIdent { span: hint.span() }); |
136023e0 XL |
1623 | continue; |
1624 | } | |
1625 | ||
2b03887a | 1626 | match hint.name_or_empty() { |
136023e0 XL |
1627 | sym::C => { |
1628 | is_c = true; | |
dc9dc135 XL |
1629 | match target { |
1630 | Target::Struct | Target::Union | Target::Enum => continue, | |
2b03887a FG |
1631 | _ => { |
1632 | self.tcx.sess.emit_err(AttrApplication::StructEnumUnion { | |
1633 | hint_span: hint.span(), | |
1634 | span, | |
1635 | }); | |
1636 | } | |
9e0c209e SL |
1637 | } |
1638 | } | |
136023e0 | 1639 | sym::align => { |
04454e1e | 1640 | if let (Target::Fn, false) = (target, self.tcx.features().fn_align) { |
136023e0 XL |
1641 | feature_err( |
1642 | &self.tcx.sess.parse_sess, | |
1643 | sym::fn_align, | |
1644 | hint.span(), | |
1645 | "`repr(align)` attributes on functions are unstable", | |
1646 | ) | |
1647 | .emit(); | |
1648 | } | |
1649 | ||
1650 | match target { | |
1651 | Target::Struct | Target::Union | Target::Enum | Target::Fn => continue, | |
2b03887a FG |
1652 | _ => { |
1653 | self.tcx.sess.emit_err(AttrApplication::StructEnumFunctionUnion { | |
1654 | hint_span: hint.span(), | |
1655 | span, | |
1656 | }); | |
1657 | } | |
136023e0 XL |
1658 | } |
1659 | } | |
48663c56 | 1660 | sym::packed => { |
dfeec247 | 1661 | if target != Target::Struct && target != Target::Union { |
2b03887a FG |
1662 | self.tcx.sess.emit_err(AttrApplication::StructUnion { |
1663 | hint_span: hint.span(), | |
1664 | span, | |
1665 | }); | |
92a42be0 | 1666 | } else { |
dfeec247 | 1667 | continue; |
b039eaaf SL |
1668 | } |
1669 | } | |
48663c56 | 1670 | sym::simd => { |
ff7c6d11 | 1671 | is_simd = true; |
1b1a35ee | 1672 | if target != Target::Struct { |
2b03887a FG |
1673 | self.tcx |
1674 | .sess | |
1675 | .emit_err(AttrApplication::Struct { hint_span: hint.span(), span }); | |
1b1a35ee XL |
1676 | } else { |
1677 | continue; | |
1678 | } | |
b039eaaf | 1679 | } |
48663c56 | 1680 | sym::transparent => { |
2c00a5a8 | 1681 | is_transparent = true; |
dc9dc135 XL |
1682 | match target { |
1683 | Target::Struct | Target::Union | Target::Enum => continue, | |
2b03887a FG |
1684 | _ => { |
1685 | self.tcx.sess.emit_err(AttrApplication::StructEnumUnion { | |
1686 | hint_span: hint.span(), | |
1687 | span, | |
1688 | }); | |
1689 | } | |
cc61c64b XL |
1690 | } |
1691 | } | |
dfeec247 XL |
1692 | sym::i8 |
1693 | | sym::u8 | |
1694 | | sym::i16 | |
1695 | | sym::u16 | |
1696 | | sym::i32 | |
1697 | | sym::u32 | |
1698 | | sym::i64 | |
1699 | | sym::u64 | |
f035d41b XL |
1700 | | sym::i128 |
1701 | | sym::u128 | |
dfeec247 XL |
1702 | | sym::isize |
1703 | | sym::usize => { | |
ff7c6d11 | 1704 | int_reprs += 1; |
1b1a35ee | 1705 | if target != Target::Enum { |
2b03887a FG |
1706 | self.tcx |
1707 | .sess | |
1708 | .emit_err(AttrApplication::Enum { hint_span: hint.span(), span }); | |
1b1a35ee XL |
1709 | } else { |
1710 | continue; | |
1711 | } | |
b039eaaf | 1712 | } |
136023e0 | 1713 | _ => { |
2b03887a | 1714 | self.tcx.sess.emit_err(UnrecognizedReprHint { span: hint.span() }); |
136023e0 XL |
1715 | continue; |
1716 | } | |
92a42be0 | 1717 | }; |
9e0c209e | 1718 | } |
ff7c6d11 | 1719 | |
2c00a5a8 XL |
1720 | // Just point at all repr hints if there are any incompatibilities. |
1721 | // This is not ideal, but tracking precisely which ones are at fault is a huge hassle. | |
532ac7d7 | 1722 | let hint_spans = hints.iter().map(|hint| hint.span()); |
2c00a5a8 | 1723 | |
064997fb FG |
1724 | // Error on repr(transparent, <anything else>). |
1725 | if is_transparent && hints.len() > 1 { | |
2c00a5a8 | 1726 | let hint_spans: Vec<_> = hint_spans.clone().collect(); |
2b03887a FG |
1727 | self.tcx |
1728 | .sess | |
1729 | .emit_err(TransparentIncompatible { hint_spans, target: target.to_string() }); | |
2c00a5a8 | 1730 | } |
ff7c6d11 XL |
1731 | // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8) |
1732 | if (int_reprs > 1) | |
dfeec247 | 1733 | || (is_simd && is_c) |
1b1a35ee XL |
1734 | || (int_reprs == 1 |
1735 | && is_c | |
1736 | && item.map_or(false, |item| { | |
1737 | if let ItemLike::Item(item) = item { | |
1738 | return is_c_like_enum(item); | |
1739 | } | |
1740 | return false; | |
1741 | })) | |
dfeec247 | 1742 | { |
064997fb | 1743 | self.tcx.emit_spanned_lint( |
74b04a01 XL |
1744 | CONFLICTING_REPR_HINTS, |
1745 | hir_id, | |
1746 | hint_spans.collect::<Vec<Span>>(), | |
064997fb | 1747 | errors::ReprConflicting, |
74b04a01 | 1748 | ); |
b039eaaf SL |
1749 | } |
1750 | } | |
0531ce1d | 1751 | |
a2a8927a | 1752 | fn check_used(&self, attrs: &[Attribute], target: Target) { |
5099ac24 FG |
1753 | let mut used_linker_span = None; |
1754 | let mut used_compiler_span = None; | |
5e7ed085 FG |
1755 | for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) { |
1756 | if target != Target::Static { | |
064997fb | 1757 | self.tcx.sess.emit_err(errors::UsedStatic { span: attr.span }); |
0531ce1d | 1758 | } |
5099ac24 FG |
1759 | let inner = attr.meta_item_list(); |
1760 | match inner.as_deref() { | |
1761 | Some([item]) if item.has_name(sym::linker) => { | |
1762 | if used_linker_span.is_none() { | |
1763 | used_linker_span = Some(attr.span); | |
1764 | } | |
1765 | } | |
1766 | Some([item]) if item.has_name(sym::compiler) => { | |
1767 | if used_compiler_span.is_none() { | |
1768 | used_compiler_span = Some(attr.span); | |
1769 | } | |
1770 | } | |
1771 | Some(_) => { | |
2b03887a | 1772 | // This error case is handled in rustc_hir_analysis::collect. |
5099ac24 FG |
1773 | } |
1774 | None => { | |
1775 | // Default case (compiler) when arg isn't defined. | |
1776 | if used_compiler_span.is_none() { | |
1777 | used_compiler_span = Some(attr.span); | |
1778 | } | |
1779 | } | |
1780 | } | |
1781 | } | |
1782 | if let (Some(linker_span), Some(compiler_span)) = (used_linker_span, used_compiler_span) { | |
5099ac24 FG |
1783 | self.tcx |
1784 | .sess | |
064997fb | 1785 | .emit_err(errors::UsedCompilerLinker { spans: vec![linker_span, compiler_span] }); |
0531ce1d XL |
1786 | } |
1787 | } | |
1788 | ||
29967ef6 XL |
1789 | /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. |
1790 | /// (Allows proc_macro functions) | |
1791 | fn check_allow_internal_unstable( | |
1792 | &self, | |
5869c6ff | 1793 | hir_id: HirId, |
29967ef6 | 1794 | attr: &Attribute, |
5099ac24 | 1795 | span: Span, |
29967ef6 XL |
1796 | target: Target, |
1797 | attrs: &[Attribute], | |
1798 | ) -> bool { | |
1799 | debug!("Checking target: {:?}", target); | |
5869c6ff XL |
1800 | match target { |
1801 | Target::Fn => { | |
1802 | for attr in attrs { | |
1803 | if self.tcx.sess.is_proc_macro_attr(attr) { | |
1804 | debug!("Is proc macro attr"); | |
1805 | return true; | |
1806 | } | |
29967ef6 | 1807 | } |
5869c6ff XL |
1808 | debug!("Is not proc macro attr"); |
1809 | false | |
1810 | } | |
1811 | Target::MacroDef => true, | |
1812 | // FIXME(#80564): We permit struct fields and match arms to have an | |
1813 | // `#[allow_internal_unstable]` attribute with just a lint, because we previously | |
2b03887a | 1814 | // erroneously allowed it and some crates used it accidentally, to be compatible |
5869c6ff XL |
1815 | // with crates depending on them, we can't throw an error here. |
1816 | Target::Field | Target::Arm => { | |
1817 | self.inline_attr_str_error_without_macro_def( | |
1818 | hir_id, | |
1819 | attr, | |
1820 | "allow_internal_unstable", | |
1821 | ); | |
1822 | true | |
1823 | } | |
1824 | _ => { | |
1825 | self.tcx | |
1826 | .sess | |
064997fb | 1827 | .emit_err(errors::AllowInternalUnstable { attr_span: attr.span, span }); |
5869c6ff | 1828 | false |
0531ce1d XL |
1829 | } |
1830 | } | |
1831 | } | |
83c7162d | 1832 | |
04454e1e FG |
1833 | /// Checks if the items on the `#[debugger_visualizer]` attribute are valid. |
1834 | fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) -> bool { | |
1835 | match target { | |
1836 | Target::Mod => {} | |
1837 | _ => { | |
064997fb | 1838 | self.tcx.sess.emit_err(errors::DebugVisualizerPlacement { span: attr.span }); |
04454e1e FG |
1839 | return false; |
1840 | } | |
1841 | } | |
1842 | ||
923072b8 | 1843 | let Some(hints) = attr.meta_item_list() else { |
064997fb | 1844 | self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span }); |
923072b8 | 1845 | return false; |
04454e1e FG |
1846 | }; |
1847 | ||
1848 | let hint = match hints.len() { | |
1849 | 1 => &hints[0], | |
1850 | _ => { | |
064997fb | 1851 | self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span }); |
04454e1e FG |
1852 | return false; |
1853 | } | |
1854 | }; | |
1855 | ||
923072b8 | 1856 | let Some(meta_item) = hint.meta_item() else { |
064997fb | 1857 | self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span }); |
04454e1e | 1858 | return false; |
923072b8 | 1859 | }; |
04454e1e | 1860 | |
923072b8 FG |
1861 | let visualizer_path = match (meta_item.name_or_empty(), meta_item.value_str()) { |
1862 | (sym::natvis_file, Some(value)) => value, | |
1863 | (sym::gdb_script_file, Some(value)) => value, | |
1864 | (_, _) => { | |
064997fb | 1865 | self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: meta_item.span }); |
04454e1e FG |
1866 | return false; |
1867 | } | |
1868 | }; | |
1869 | ||
923072b8 FG |
1870 | let file = |
1871 | match resolve_path(&self.tcx.sess.parse_sess, visualizer_path.as_str(), attr.span) { | |
1872 | Ok(file) => file, | |
1873 | Err(mut err) => { | |
1874 | err.emit(); | |
1875 | return false; | |
1876 | } | |
1877 | }; | |
1878 | ||
1879 | match std::fs::File::open(&file) { | |
1880 | Ok(_) => true, | |
2b03887a FG |
1881 | Err(error) => { |
1882 | self.tcx.sess.emit_err(DebugVisualizerUnreadable { | |
1883 | span: meta_item.span, | |
1884 | file: &file, | |
1885 | error, | |
1886 | }); | |
04454e1e FG |
1887 | false |
1888 | } | |
1889 | } | |
1890 | } | |
1891 | ||
29967ef6 XL |
1892 | /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. |
1893 | /// (Allows proc_macro functions) | |
1894 | fn check_rustc_allow_const_fn_unstable( | |
1895 | &self, | |
1896 | hir_id: HirId, | |
1897 | attr: &Attribute, | |
5099ac24 | 1898 | span: Span, |
29967ef6 XL |
1899 | target: Target, |
1900 | ) -> bool { | |
5869c6ff XL |
1901 | match target { |
1902 | Target::Fn | Target::Method(_) | |
5e7ed085 | 1903 | if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id).to_def_id()) => |
5869c6ff XL |
1904 | { |
1905 | true | |
1906 | } | |
1907 | // FIXME(#80564): We permit struct fields and match arms to have an | |
1908 | // `#[allow_internal_unstable]` attribute with just a lint, because we previously | |
2b03887a | 1909 | // erroneously allowed it and some crates used it accidentally, to be compatible |
5869c6ff XL |
1910 | // with crates depending on them, we can't throw an error here. |
1911 | Target::Field | Target::Arm | Target::MacroDef => { | |
1912 | self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable"); | |
1913 | true | |
1914 | } | |
1915 | _ => { | |
1916 | self.tcx | |
1917 | .sess | |
064997fb | 1918 | .emit_err(errors::RustcAllowConstFnUnstable { attr_span: attr.span, span }); |
5869c6ff | 1919 | false |
83c7162d XL |
1920 | } |
1921 | } | |
1922 | } | |
136023e0 | 1923 | |
04454e1e FG |
1924 | fn check_rustc_std_internal_symbol( |
1925 | &self, | |
1926 | attr: &Attribute, | |
1927 | span: Span, | |
1928 | target: Target, | |
1929 | ) -> bool { | |
1930 | match target { | |
1931 | Target::Fn | Target::Static => true, | |
1932 | _ => { | |
1933 | self.tcx | |
1934 | .sess | |
064997fb | 1935 | .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span, span }); |
04454e1e FG |
1936 | false |
1937 | } | |
1938 | } | |
1939 | } | |
1940 | ||
923072b8 FG |
1941 | /// `#[const_trait]` only applies to traits. |
1942 | fn check_const_trait(&self, attr: &Attribute, _span: Span, target: Target) -> bool { | |
136023e0 | 1943 | match target { |
923072b8 | 1944 | Target::Trait => true, |
136023e0 | 1945 | _ => { |
064997fb | 1946 | self.tcx.sess.emit_err(errors::ConstTrait { attr_span: attr.span }); |
136023e0 XL |
1947 | false |
1948 | } | |
1949 | } | |
1950 | } | |
94222f64 | 1951 | |
5099ac24 | 1952 | fn check_stability_promotable(&self, attr: &Attribute, _span: Span, target: Target) -> bool { |
94222f64 XL |
1953 | match target { |
1954 | Target::Expression => { | |
064997fb | 1955 | self.tcx.sess.emit_err(errors::StabilityPromotable { attr_span: attr.span }); |
94222f64 XL |
1956 | false |
1957 | } | |
1958 | _ => true, | |
1959 | } | |
1960 | } | |
1961 | ||
f2b60f7d FG |
1962 | fn check_link_ordinal(&self, attr: &Attribute, _span: Span, target: Target) -> bool { |
1963 | match target { | |
1964 | Target::ForeignFn | Target::ForeignStatic => true, | |
1965 | _ => { | |
1966 | self.tcx.sess.emit_err(errors::LinkOrdinal { attr_span: attr.span }); | |
1967 | false | |
1968 | } | |
1969 | } | |
1970 | } | |
1971 | ||
5099ac24 | 1972 | fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: Span, target: Target) { |
94222f64 XL |
1973 | match target { |
1974 | Target::Closure | Target::Expression | Target::Statement | Target::Arm => { | |
064997fb FG |
1975 | self.tcx.emit_spanned_lint( |
1976 | UNUSED_ATTRIBUTES, | |
1977 | hir_id, | |
1978 | attr.span, | |
1979 | errors::Deprecated, | |
1980 | ); | |
94222f64 XL |
1981 | } |
1982 | _ => {} | |
1983 | } | |
1984 | } | |
1985 | ||
1986 | fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) { | |
1987 | let name = attr.name_or_empty(); | |
1988 | match target { | |
1989 | Target::ExternCrate | Target::Mod => {} | |
1990 | _ => { | |
064997fb FG |
1991 | self.tcx.emit_spanned_lint( |
1992 | UNUSED_ATTRIBUTES, | |
1993 | hir_id, | |
1994 | attr.span, | |
1995 | errors::MacroUse { name }, | |
1996 | ); | |
94222f64 XL |
1997 | } |
1998 | } | |
1999 | } | |
2000 | ||
2001 | fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) { | |
2002 | if target != Target::MacroDef { | |
064997fb | 2003 | self.tcx.emit_spanned_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::MacroExport); |
94222f64 XL |
2004 | } |
2005 | } | |
2006 | ||
94222f64 XL |
2007 | fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) { |
2008 | if target != Target::Fn { | |
064997fb FG |
2009 | self.tcx.emit_spanned_lint( |
2010 | UNUSED_ATTRIBUTES, | |
2011 | hir_id, | |
2012 | attr.span, | |
2013 | errors::PluginRegistrar, | |
2014 | ); | |
94222f64 XL |
2015 | } |
2016 | } | |
5e7ed085 FG |
2017 | |
2018 | fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) { | |
2019 | // Warn on useless empty attributes. | |
2020 | let note = if matches!( | |
2021 | attr.name_or_empty(), | |
2022 | sym::macro_use | |
2023 | | sym::allow | |
2024 | | sym::expect | |
2025 | | sym::warn | |
2026 | | sym::deny | |
2027 | | sym::forbid | |
2028 | | sym::feature | |
2029 | | sym::repr | |
2030 | | sym::target_feature | |
2031 | ) && attr.meta_item_list().map_or(false, |list| list.is_empty()) | |
2032 | { | |
064997fb | 2033 | errors::UnusedNote::EmptyList { name: attr.name_or_empty() } |
5e7ed085 FG |
2034 | } else if matches!( |
2035 | attr.name_or_empty(), | |
2036 | sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect | |
2037 | ) && let Some(meta) = attr.meta_item_list() | |
2038 | && meta.len() == 1 | |
2039 | && let Some(item) = meta[0].meta_item() | |
2040 | && let MetaItemKind::NameValue(_) = &item.kind | |
2041 | && item.path == sym::reason | |
2042 | { | |
064997fb | 2043 | errors::UnusedNote::NoLints { name: attr.name_or_empty() } |
923072b8 | 2044 | } else if attr.name_or_empty() == sym::default_method_body_is_const { |
064997fb | 2045 | errors::UnusedNote::DefaultMethodBodyConst |
5e7ed085 FG |
2046 | } else { |
2047 | return; | |
2048 | }; | |
2049 | ||
064997fb FG |
2050 | self.tcx.emit_spanned_lint( |
2051 | UNUSED_ATTRIBUTES, | |
2052 | hir_id, | |
2053 | attr.span, | |
2054 | errors::Unused { attr_span: attr.span, note }, | |
2055 | ); | |
5e7ed085 | 2056 | } |
b039eaaf SL |
2057 | } |
2058 | ||
a2a8927a | 2059 | impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { |
5099ac24 | 2060 | type NestedFilter = nested_filter::OnlyBodies; |
dfeec247 | 2061 | |
5099ac24 FG |
2062 | fn nested_visit_map(&mut self) -> Self::Map { |
2063 | self.tcx.hir() | |
2c00a5a8 XL |
2064 | } |
2065 | ||
dfeec247 | 2066 | fn visit_item(&mut self, item: &'tcx Item<'tcx>) { |
94222f64 XL |
2067 | // Historically we've run more checks on non-exported than exported macros, |
2068 | // so this lets us continue to run them while maintaining backwards compatibility. | |
2069 | // In the long run, the checks should be harmonized. | |
5e7ed085 | 2070 | if let ItemKind::Macro(ref macro_def, _) = item.kind { |
2b03887a | 2071 | let def_id = item.owner_id.to_def_id(); |
94222f64 XL |
2072 | if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) { |
2073 | check_non_exported_macro_for_invalid_attrs(self.tcx, item); | |
2074 | } | |
2075 | } | |
2076 | ||
b039eaaf | 2077 | let target = Target::from_item(item); |
5099ac24 | 2078 | self.check_attributes(item.hir_id(), item.span, target, Some(ItemLike::Item(item))); |
0531ce1d XL |
2079 | intravisit::walk_item(self, item) |
2080 | } | |
2081 | ||
fc512014 XL |
2082 | fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) { |
2083 | let target = Target::from_generic_param(generic_param); | |
5099ac24 | 2084 | self.check_attributes(generic_param.hir_id, generic_param.span, target, None); |
fc512014 XL |
2085 | intravisit::walk_generic_param(self, generic_param) |
2086 | } | |
2087 | ||
dfeec247 | 2088 | fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) { |
e74abb32 | 2089 | let target = Target::from_trait_item(trait_item); |
5099ac24 | 2090 | self.check_attributes(trait_item.hir_id(), trait_item.span, target, None); |
e74abb32 XL |
2091 | intravisit::walk_trait_item(self, trait_item) |
2092 | } | |
2093 | ||
6a06907d | 2094 | fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) { |
5099ac24 | 2095 | self.check_attributes(struct_field.hir_id, struct_field.span, Target::Field, None); |
6a06907d | 2096 | intravisit::walk_field_def(self, struct_field); |
5869c6ff XL |
2097 | } |
2098 | ||
2099 | fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) { | |
5099ac24 | 2100 | self.check_attributes(arm.hir_id, arm.span, Target::Arm, None); |
5869c6ff XL |
2101 | intravisit::walk_arm(self, arm); |
2102 | } | |
2103 | ||
1b1a35ee | 2104 | fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) { |
e74abb32 | 2105 | let target = Target::from_foreign_item(f_item); |
064997fb | 2106 | self.check_attributes(f_item.hir_id(), f_item.span, target, Some(ItemLike::ForeignItem)); |
e74abb32 XL |
2107 | intravisit::walk_foreign_item(self, f_item) |
2108 | } | |
2109 | ||
dfeec247 | 2110 | fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { |
74b04a01 | 2111 | let target = target_from_impl_item(self.tcx, impl_item); |
5099ac24 | 2112 | self.check_attributes(impl_item.hir_id(), impl_item.span, target, None); |
e74abb32 XL |
2113 | intravisit::walk_impl_item(self, impl_item) |
2114 | } | |
0531ce1d | 2115 | |
dfeec247 | 2116 | fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { |
29967ef6 XL |
2117 | // When checking statements ignore expressions, they will be checked later. |
2118 | if let hir::StmtKind::Local(ref l) = stmt.kind { | |
5099ac24 | 2119 | self.check_attributes(l.hir_id, stmt.span, Target::Statement, None); |
29967ef6 | 2120 | } |
0531ce1d XL |
2121 | intravisit::walk_stmt(self, stmt) |
2122 | } | |
2123 | ||
dfeec247 | 2124 | fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { |
29967ef6 | 2125 | let target = match expr.kind { |
923072b8 | 2126 | hir::ExprKind::Closure { .. } => Target::Closure, |
29967ef6 XL |
2127 | _ => Target::Expression, |
2128 | }; | |
2129 | ||
5099ac24 | 2130 | self.check_attributes(expr.hir_id, expr.span, target, None); |
0531ce1d | 2131 | intravisit::walk_expr(self, expr) |
b039eaaf | 2132 | } |
29967ef6 | 2133 | |
f2b60f7d FG |
2134 | fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) { |
2135 | self.check_attributes(field.hir_id, field.span, Target::ExprField, None); | |
2136 | intravisit::walk_expr_field(self, field) | |
2137 | } | |
2138 | ||
2139 | fn visit_variant(&mut self, variant: &'tcx hir::Variant<'tcx>) { | |
487cf647 | 2140 | self.check_attributes(variant.hir_id, variant.span, Target::Variant, None); |
f2b60f7d | 2141 | intravisit::walk_variant(self, variant) |
29967ef6 | 2142 | } |
6a06907d | 2143 | |
6a06907d | 2144 | fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { |
5099ac24 | 2145 | self.check_attributes(param.hir_id, param.span, Target::Param, None); |
6a06907d XL |
2146 | |
2147 | intravisit::walk_param(self, param); | |
2148 | } | |
f2b60f7d FG |
2149 | |
2150 | fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) { | |
2151 | self.check_attributes(field.hir_id, field.span, Target::PatField, None); | |
2152 | intravisit::walk_pat_field(self, field); | |
2153 | } | |
b039eaaf SL |
2154 | } |
2155 | ||
dfeec247 | 2156 | fn is_c_like_enum(item: &Item<'_>) -> bool { |
e74abb32 | 2157 | if let ItemKind::Enum(ref def, _) = item.kind { |
dfeec247 | 2158 | for variant in def.variants { |
e1599b0c | 2159 | match variant.data { |
9fa01778 | 2160 | hir::VariantData::Unit(..) => { /* continue */ } |
e74abb32 | 2161 | _ => return false, |
ff7c6d11 XL |
2162 | } |
2163 | } | |
2164 | true | |
2165 | } else { | |
2166 | false | |
2167 | } | |
2168 | } | |
0731742a | 2169 | |
a2a8927a XL |
2170 | // FIXME: Fix "Cannot determine resolution" error and remove built-in macros |
2171 | // from this check. | |
29967ef6 | 2172 | fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { |
a2a8927a XL |
2173 | // Check for builtin attributes at the crate level |
2174 | // which were unsuccessfully resolved due to cannot determine | |
2175 | // resolution for the attribute macro error. | |
29967ef6 XL |
2176 | const ATTRS_TO_CHECK: &[Symbol] = &[ |
2177 | sym::macro_export, | |
2178 | sym::repr, | |
2179 | sym::path, | |
2180 | sym::automatically_derived, | |
2181 | sym::start, | |
cdc7bbd5 | 2182 | sym::rustc_main, |
f2b60f7d | 2183 | sym::unix_sigpipe, |
a2a8927a XL |
2184 | sym::derive, |
2185 | sym::test, | |
2186 | sym::test_case, | |
2187 | sym::global_allocator, | |
2188 | sym::bench, | |
29967ef6 XL |
2189 | ]; |
2190 | ||
2191 | for attr in attrs { | |
a2a8927a XL |
2192 | // This function should only be called with crate attributes |
2193 | // which are inner attributes always but lets check to make sure | |
2194 | if attr.style == AttrStyle::Inner { | |
2195 | for attr_to_check in ATTRS_TO_CHECK { | |
2196 | if attr.has_name(*attr_to_check) { | |
2b03887a FG |
2197 | tcx.sess.emit_err(InvalidAttrAtCrateLevel { |
2198 | span: attr.span, | |
2199 | snippet: tcx.sess.source_map().span_to_snippet(attr.span).ok(), | |
2200 | name: *attr_to_check, | |
2201 | }); | |
a2a8927a | 2202 | } |
29967ef6 XL |
2203 | } |
2204 | } | |
2205 | } | |
2206 | } | |
2207 | ||
94222f64 XL |
2208 | fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) { |
2209 | let attrs = tcx.hir().attrs(item.hir_id()); | |
2210 | ||
5869c6ff | 2211 | for attr in attrs { |
94222f64 | 2212 | if attr.has_name(sym::inline) { |
064997fb | 2213 | tcx.sess.emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span }); |
5869c6ff XL |
2214 | } |
2215 | } | |
2216 | } | |
2217 | ||
f035d41b | 2218 | fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { |
5869c6ff | 2219 | let check_attr_visitor = &mut CheckAttrVisitor { tcx }; |
064997fb | 2220 | tcx.hir().visit_item_likes_in_module(module_def_id, check_attr_visitor); |
29967ef6 | 2221 | if module_def_id.is_top_level_module() { |
5099ac24 | 2222 | check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None); |
29967ef6 XL |
2223 | check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs()); |
2224 | } | |
0731742a XL |
2225 | } |
2226 | ||
f035d41b | 2227 | pub(crate) fn provide(providers: &mut Providers) { |
dfeec247 | 2228 | *providers = Providers { check_mod_attrs, ..*providers }; |
0731742a | 2229 | } |
3c0e092e XL |
2230 | |
2231 | fn check_duplicates( | |
2232 | tcx: TyCtxt<'_>, | |
2233 | attr: &Attribute, | |
2234 | hir_id: HirId, | |
2235 | duplicates: AttributeDuplicates, | |
2236 | seen: &mut FxHashMap<Symbol, Span>, | |
2237 | ) { | |
2238 | use AttributeDuplicates::*; | |
2239 | if matches!(duplicates, WarnFollowingWordOnly) && !attr.is_word() { | |
2240 | return; | |
2241 | } | |
2242 | match duplicates { | |
2243 | DuplicatesOk => {} | |
2244 | WarnFollowing | FutureWarnFollowing | WarnFollowingWordOnly | FutureWarnPreceding => { | |
2245 | match seen.entry(attr.name_or_empty()) { | |
2246 | Entry::Occupied(mut entry) => { | |
2247 | let (this, other) = if matches!(duplicates, FutureWarnPreceding) { | |
2248 | let to_remove = entry.insert(attr.span); | |
2249 | (to_remove, attr.span) | |
2250 | } else { | |
2251 | (attr.span, *entry.get()) | |
2252 | }; | |
064997fb FG |
2253 | tcx.emit_spanned_lint( |
2254 | UNUSED_ATTRIBUTES, | |
2255 | hir_id, | |
2256 | this, | |
2257 | errors::UnusedDuplicate { | |
3c0e092e | 2258 | this, |
064997fb FG |
2259 | other, |
2260 | warning: matches!( | |
2261 | duplicates, | |
2262 | FutureWarnFollowing | FutureWarnPreceding | |
2263 | ) | |
2264 | .then_some(()), | |
2265 | }, | |
2266 | ); | |
3c0e092e XL |
2267 | } |
2268 | Entry::Vacant(entry) => { | |
2269 | entry.insert(attr.span); | |
2270 | } | |
2271 | } | |
2272 | } | |
2273 | ErrorFollowing | ErrorPreceding => match seen.entry(attr.name_or_empty()) { | |
2274 | Entry::Occupied(mut entry) => { | |
2275 | let (this, other) = if matches!(duplicates, ErrorPreceding) { | |
2276 | let to_remove = entry.insert(attr.span); | |
2277 | (to_remove, attr.span) | |
2278 | } else { | |
2279 | (attr.span, *entry.get()) | |
2280 | }; | |
064997fb FG |
2281 | tcx.sess.emit_err(errors::UnusedMultiple { |
2282 | this, | |
2283 | other, | |
2284 | name: attr.name_or_empty(), | |
2285 | }); | |
3c0e092e XL |
2286 | } |
2287 | Entry::Vacant(entry) => { | |
2288 | entry.insert(attr.span); | |
2289 | } | |
2290 | }, | |
2291 | } | |
2292 | } |