1use std::borrow::Cow;
2
3use rustc_ast::AttrStyle;
4use rustc_errors::{DiagArgValue, Diagnostic, MultiSpan, StashKey};
5use rustc_feature::Features;
6use rustc_hir::attrs::AttributeKind;
7use rustc_hir::{AttrItem, Attribute, MethodKind, Target};
8use rustc_span::{BytePos, FileName, RemapPathScopeComponents, Span, Symbol, sym};
9
10use crate::context::AcceptContext;
11use crate::errors::{
12 InvalidAttrAtCrateLevel, InvalidTargetLint, ItemFollowingInnerAttr,
13 UnsupportedAttributesInWhere,
14};
15use crate::session_diagnostics::InvalidTarget;
16use crate::target_checking::Policy::Allow;
17use crate::{AttributeParser, ShouldEmit};
18
19#[derive(#[automatically_derived]
impl ::core::fmt::Debug for AllowedTargets {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
AllowedTargets::AllowList(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"AllowList", &__self_0),
AllowedTargets::AllowListWarnRest(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"AllowListWarnRest", &__self_0),
}
}
}Debug)]
20pub(crate) enum AllowedTargets {
21 AllowList(&'static [Policy]),
22 AllowListWarnRest(&'static [Policy]),
23}
24
25pub(crate) enum AllowedResult {
26 Allowed,
27 Warn,
28 Error,
29}
30
31impl AllowedTargets {
32 pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult {
33 match self {
34 AllowedTargets::AllowList(list) => {
35 if list.contains(&Policy::Allow(target))
36 || list.contains(&Policy::AllowSilent(target))
37 {
38 AllowedResult::Allowed
39 } else if list.contains(&Policy::Warn(target)) {
40 AllowedResult::Warn
41 } else {
42 AllowedResult::Error
43 }
44 }
45 AllowedTargets::AllowListWarnRest(list) => {
46 if list.contains(&Policy::Allow(target))
47 || list.contains(&Policy::AllowSilent(target))
48 {
49 AllowedResult::Allowed
50 } else if list.contains(&Policy::Error(target)) {
51 AllowedResult::Error
52 } else {
53 AllowedResult::Warn
54 }
55 }
56 }
57 }
58
59 pub(crate) fn allowed_targets(&self) -> Vec<Target> {
60 match self {
61 AllowedTargets::AllowList(list) => list,
62 AllowedTargets::AllowListWarnRest(list) => list,
63 }
64 .iter()
65 .filter_map(|target| match target {
66 Policy::Allow(target) => Some(*target),
67 Policy::AllowSilent(_) => None, Policy::Warn(_) => None,
69 Policy::Error(_) => None,
70 })
71 .collect()
72 }
73}
74
75#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Policy {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Policy::Allow(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Allow",
&__self_0),
Policy::AllowSilent(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"AllowSilent", &__self_0),
Policy::Warn(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Warn",
&__self_0),
Policy::Error(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Error",
&__self_0),
}
}
}Debug, #[automatically_derived]
impl ::core::cmp::Eq for Policy {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<Target>;
}
}Eq, #[automatically_derived]
impl ::core::cmp::PartialEq for Policy {
#[inline]
fn eq(&self, other: &Policy) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(Policy::Allow(__self_0), Policy::Allow(__arg1_0)) =>
__self_0 == __arg1_0,
(Policy::AllowSilent(__self_0), Policy::AllowSilent(__arg1_0))
=> __self_0 == __arg1_0,
(Policy::Warn(__self_0), Policy::Warn(__arg1_0)) =>
__self_0 == __arg1_0,
(Policy::Error(__self_0), Policy::Error(__arg1_0)) =>
__self_0 == __arg1_0,
_ => unsafe { ::core::intrinsics::unreachable() }
}
}
}PartialEq)]
77pub(crate) enum Policy {
78 Allow(Target),
80 AllowSilent(Target),
83 Warn(Target),
86 Error(Target),
88}
89
90impl<'sess> AttributeParser<'sess> {
91 pub(crate) fn check_target(
92 allowed_targets: &AllowedTargets,
93 cx: &mut AcceptContext<'_, 'sess>,
94 ) {
95 if #[allow(non_exhaustive_omitted_patterns)] match cx.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(cx.should_emit, ShouldEmit::Nothing) {
96 return;
97 }
98
99 if let &AllowedTargets::AllowList(&[Allow(Target::Crate)]) = allowed_targets {
102 Self::check_crate_level(cx);
103 return;
104 }
105
106 if #[allow(non_exhaustive_omitted_patterns)] match cx.attr_path.segments.as_ref()
{
[sym::repr] => true,
_ => false,
}matches!(cx.attr_path.segments.as_ref(), [sym::repr]) && cx.target == Target::Crate {
107 let span = cx.attr_span;
110 let item =
111 cx.cx.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span });
112
113 let pound_to_opening_bracket = cx.attr_span.until(cx.inner_span);
114
115 cx.dcx()
116 .create_err(InvalidAttrAtCrateLevel {
117 span,
118 pound_to_opening_bracket,
119 name: sym::repr,
120 item,
121 })
122 .emit();
123 }
124
125 match allowed_targets.is_allowed(cx.target) {
126 AllowedResult::Allowed => {}
127 AllowedResult::Warn => {
128 let allowed_targets = allowed_targets.allowed_targets();
129 let (applied, only) =
130 allowed_targets_applied(allowed_targets, cx.target, cx.features);
131 let name = cx.attr_path.clone();
132
133 let lint = if name.segments[0] == sym::deprecated
134 && ![
135 Target::Closure,
136 Target::Expression,
137 Target::Statement,
138 Target::Arm,
139 Target::MacroCall,
140 ]
141 .contains(&cx.target)
142 {
143 rustc_session::lint::builtin::USELESS_DEPRECATED
144 } else {
145 rustc_session::lint::builtin::UNUSED_ATTRIBUTES
146 };
147
148 let attr_span = cx.attr_span;
149 let target = cx.target;
150 cx.emit_lint_with_sess(
151 lint,
152 move |dcx, level, _| {
153 InvalidTargetLint {
154 name: name.to_string(),
155 target: target.plural_name(),
156 only: if only { "only " } else { "" },
157 applied: DiagArgValue::StrListSepByAnd(
158 applied.iter().map(|i| Cow::Owned(i.to_string())).collect(),
159 ),
160 attr_span,
161 }
162 .into_diag(dcx, level)
163 },
164 attr_span,
165 );
166 }
167 AllowedResult::Error => {
168 let allowed_targets = allowed_targets.allowed_targets();
169 let (applied, only) =
170 allowed_targets_applied(allowed_targets, cx.target, cx.features);
171 let name = cx.attr_path.clone();
172 cx.dcx().emit_err(InvalidTarget {
173 span: cx.attr_span.clone(),
174 name,
175 target: cx.target.plural_name(),
176 only: if only { "only " } else { "" },
177 applied: DiagArgValue::StrListSepByAnd(
178 applied.into_iter().map(Cow::Owned).collect(),
179 ),
180 });
181 }
182 }
183 }
184
185 pub(crate) fn check_crate_level(cx: &mut AcceptContext<'_, 'sess>) {
186 if cx.target == Target::Crate {
187 return;
188 }
189
190 let name = cx.attr_path.to_string();
191 let is_used_as_inner = cx.attr_style == AttrStyle::Inner;
192 let target_span = cx.target_span;
193 let attr_span = cx.attr_span;
194
195 let (show_crate_root_help, crate_root_path) = is_used_as_inner
196 .then(|| cx.cx.sess.local_crate_source_file())
197 .flatten()
198 .filter(|src| {
199 !#[allow(non_exhaustive_omitted_patterns)] match cx.cx.sess.source_map().span_to_filename(attr_span)
{
FileName::Real(ref name) if name == src => true,
_ => false,
}matches!(
200 cx.cx.sess.source_map().span_to_filename(attr_span),
201 FileName::Real(ref name) if name == src
202 )
203 })
204 .map(|src| {
205 (true, src.path(RemapPathScopeComponents::DIAGNOSTICS).display().to_string())
206 })
207 .unwrap_or_default();
208
209 let target = cx.target;
210 cx.emit_lint(
211 rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
212 crate::errors::InvalidAttrStyle {
213 name,
214 is_used_as_inner,
215 target_span: (!is_used_as_inner).then_some(target_span),
216 target: target.name(),
217 crate_root_path,
218 show_crate_root_help,
219 },
220 attr_span,
221 );
222 }
223
224 pub(crate) fn check_invalid_crate_level_attr_item(&self, attr: &AttrItem, inner_span: Span) {
227 const ATTRS_TO_CHECK: &[Symbol] =
231 &[sym::derive, sym::test, sym::test_case, sym::global_allocator, sym::bench];
232
233 if let Some(name) = ATTRS_TO_CHECK.iter().find(|attr_to_check| #[allow(non_exhaustive_omitted_patterns)] match attr.path.segments.as_ref() {
[segment] if segment == *attr_to_check => true,
_ => false,
}matches!(attr.path.segments.as_ref(), [segment] if segment == *attr_to_check)) {
235 let span = attr.span;
236 let name = *name;
237
238 let item = self.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span });
239
240 let err = self.dcx().create_err(InvalidAttrAtCrateLevel {
241 span,
242 pound_to_opening_bracket: span.until(inner_span),
243 name,
244 item,
245 });
246
247 self.dcx().try_steal_replace_and_emit_err(
248 attr.path.span,
249 StashKey::UndeterminedMacroResolution,
250 err,
251 );
252 }
253 }
254
255 fn first_line_of_next_item(&self, span: Span) -> Option<Span> {
256 self.sess()
261 .source_map()
262 .span_to_source(span, |content, _, span_end| {
263 let mut source = &content[span_end..];
264 let initial_source_len = source.len();
265 let span = try {
266 loop {
267 let first = source.chars().next()?;
268
269 if first.is_whitespace() {
270 let split_idx = source.find(|c: char| !c.is_whitespace())?;
271 source = &source[split_idx..];
272 } else if source.starts_with("//") {
273 let line_idx = source.find('\n')?;
274 source = &source[line_idx + '\n'.len_utf8()..];
275 } else if source.starts_with("/*") {
276 let close_idx = source.find("*/")?;
278 source = &source[close_idx + "*/".len()..];
279 } else if first == '#' {
280 let close_idx = source.find(']')?;
284 source = &source[close_idx + ']'.len_utf8()..];
285 } else {
286 let lo = span_end + initial_source_len - source.len();
287 let last_line = source.split('\n').next().map(|s| s.trim_end())?;
288
289 let hi = lo + last_line.len();
290 let lo = BytePos(lo as u32);
291 let hi = BytePos(hi as u32);
292 let next_item_span = Span::new(lo, hi, span.ctxt(), None);
293
294 break next_item_span;
295 }
296 }
297 };
298
299 Ok(span)
300 })
301 .ok()
302 .flatten()
303 }
304
305 pub(crate) fn check_invalid_where_predicate_attrs<'attr>(
306 &self,
307 attrs: impl IntoIterator<Item = &'attr Attribute>,
308 ) {
309 let spans = attrs
314 .into_iter()
315 .filter_map(|attr| {
316 match attr {
317 Attribute::Parsed(AttributeKind::DocComment { span, .. }) => Some(*span),
318 Attribute::Parsed(AttributeKind::Doc(attr)) => Some(attr.first_span),
320 Attribute::Parsed(_) => None,
322 Attribute::Unparsed(attr) => Some(attr.span),
323 }
324 })
325 .collect::<Vec<_>>();
326 if !spans.is_empty() {
327 self.dcx()
328 .emit_err(UnsupportedAttributesInWhere { span: MultiSpan::from_spans(spans) });
329 }
330 }
331}
332
333pub(crate) fn allowed_targets_applied(
336 mut allowed_targets: Vec<Target>,
337 target: Target,
338 features: Option<&Features>,
339) -> (Vec<String>, bool) {
340 if let Some(features) = features {
342 if !features.fn_delegation() {
343 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::Delegation { .. } => true,
_ => false,
}matches!(t, Target::Delegation { .. }));
344 }
345 if !features.stmt_expr_attributes() {
346 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::Expression | Target::Statement => true,
_ => false,
}matches!(t, Target::Expression | Target::Statement));
347 }
348 if !features.extern_types() {
349 allowed_targets.retain(|t| !#[allow(non_exhaustive_omitted_patterns)] match t {
Target::ForeignTy => true,
_ => false,
}matches!(t, Target::ForeignTy));
350 }
351 }
352
353 const FUNCTION_LIKE: &[Target] = &[
357 Target::Fn,
358 Target::Closure,
359 Target::ForeignFn,
360 Target::Method(MethodKind::Inherent),
361 Target::Method(MethodKind::Trait { body: false }),
362 Target::Method(MethodKind::Trait { body: true }),
363 Target::Method(MethodKind::TraitImpl),
364 ];
365 const METHOD_LIKE: &[Target] = &[
366 Target::Method(MethodKind::Inherent),
367 Target::Method(MethodKind::Trait { body: false }),
368 Target::Method(MethodKind::Trait { body: true }),
369 Target::Method(MethodKind::TraitImpl),
370 ];
371 const IMPL_LIKE: &[Target] =
372 &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
373 const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum, Target::Union];
374
375 let mut added_fake_targets = Vec::new();
376 filter_targets(
377 &mut allowed_targets,
378 FUNCTION_LIKE,
379 "functions",
380 target,
381 &mut added_fake_targets,
382 );
383 filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets);
384 filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets);
385 filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets);
386
387 let mut target_strings: Vec<_> = added_fake_targets
388 .iter()
389 .copied()
390 .chain(allowed_targets.iter().map(|t| t.plural_name()))
391 .map(|i| i.to_string())
392 .collect();
393
394 target_strings.sort();
396
397 let only_target = target_strings.len() == 1;
399
400 (target_strings, only_target)
401}
402
403fn filter_targets(
404 allowed_targets: &mut Vec<Target>,
405 target_group: &'static [Target],
406 target_group_name: &'static str,
407 target: Target,
408 added_fake_targets: &mut Vec<&'static str>,
409) {
410 if target_group.contains(&target) {
411 return;
412 }
413 if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 {
414 return;
415 }
416 allowed_targets.retain(|t| !target_group.contains(t));
417 added_fake_targets.push(target_group_name);
418}
419
420pub(crate) const ALL_TARGETS: &'static [Policy] = {
425 use Policy::Allow;
426 &[
427 Allow(Target::ExternCrate),
428 Allow(Target::Use),
429 Allow(Target::Static),
430 Allow(Target::Const),
431 Allow(Target::Fn),
432 Allow(Target::Closure),
433 Allow(Target::Mod),
434 Allow(Target::ForeignMod),
435 Allow(Target::GlobalAsm),
436 Allow(Target::TyAlias),
437 Allow(Target::Enum),
438 Allow(Target::Variant),
439 Allow(Target::Struct),
440 Allow(Target::Field),
441 Allow(Target::Union),
442 Allow(Target::Trait),
443 Allow(Target::TraitAlias),
444 Allow(Target::Impl { of_trait: false }),
445 Allow(Target::Impl { of_trait: true }),
446 Allow(Target::Expression),
447 Allow(Target::Statement),
448 Allow(Target::Arm),
449 Allow(Target::AssocConst),
450 Allow(Target::Method(MethodKind::Inherent)),
451 Allow(Target::Method(MethodKind::Trait { body: false })),
452 Allow(Target::Method(MethodKind::Trait { body: true })),
453 Allow(Target::Method(MethodKind::TraitImpl)),
454 Allow(Target::AssocTy),
455 Allow(Target::ForeignFn),
456 Allow(Target::ForeignStatic),
457 Allow(Target::ForeignTy),
458 Allow(Target::MacroDef),
459 Allow(Target::Param),
460 Allow(Target::PatField),
461 Allow(Target::ExprField),
462 Allow(Target::WherePredicate),
463 Allow(Target::MacroCall),
464 Allow(Target::Crate),
465 Allow(Target::Delegation { mac: false }),
466 Allow(Target::Delegation { mac: true }),
467 Allow(Target::GenericParam {
468 kind: rustc_hir::target::GenericParamKind::Const,
469 has_default: false,
470 }),
471 Allow(Target::GenericParam {
472 kind: rustc_hir::target::GenericParamKind::Const,
473 has_default: true,
474 }),
475 Allow(Target::GenericParam {
476 kind: rustc_hir::target::GenericParamKind::Lifetime,
477 has_default: false,
478 }),
479 Allow(Target::GenericParam {
480 kind: rustc_hir::target::GenericParamKind::Lifetime,
481 has_default: true,
482 }),
483 Allow(Target::GenericParam {
484 kind: rustc_hir::target::GenericParamKind::Type,
485 has_default: false,
486 }),
487 Allow(Target::GenericParam {
488 kind: rustc_hir::target::GenericParamKind::Type,
489 has_default: true,
490 }),
491 ]
492};