rustc_attr_parsing/
target_checking.rs

1use std::borrow::Cow;
2
3use rustc_errors::DiagArgValue;
4use rustc_feature::Features;
5use rustc_hir::lints::{AttributeLint, AttributeLintKind};
6use rustc_hir::{AttrPath, MethodKind, Target};
7use rustc_span::Span;
8
9use crate::AttributeParser;
10use crate::context::Stage;
11use crate::session_diagnostics::InvalidTarget;
12
13#[derive(Debug)]
14pub(crate) enum AllowedTargets {
15    AllowList(&'static [Policy]),
16    AllowListWarnRest(&'static [Policy]),
17}
18
19pub(crate) enum AllowedResult {
20    Allowed,
21    Warn,
22    Error,
23}
24
25impl AllowedTargets {
26    pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult {
27        match self {
28            AllowedTargets::AllowList(list) => {
29                if list.contains(&Policy::Allow(target)) {
30                    AllowedResult::Allowed
31                } else if list.contains(&Policy::Warn(target)) {
32                    AllowedResult::Warn
33                } else {
34                    AllowedResult::Error
35                }
36            }
37            AllowedTargets::AllowListWarnRest(list) => {
38                if list.contains(&Policy::Allow(target)) {
39                    AllowedResult::Allowed
40                } else if list.contains(&Policy::Error(target)) {
41                    AllowedResult::Error
42                } else {
43                    AllowedResult::Warn
44                }
45            }
46        }
47    }
48
49    pub(crate) fn allowed_targets(&self) -> Vec<Target> {
50        match self {
51            AllowedTargets::AllowList(list) => list,
52            AllowedTargets::AllowListWarnRest(list) => list,
53        }
54        .iter()
55        .filter_map(|target| match target {
56            Policy::Allow(target) => Some(*target),
57            Policy::Warn(_) => None,
58            Policy::Error(_) => None,
59        })
60        .collect()
61    }
62}
63
64#[derive(Debug, Eq, PartialEq)]
65pub(crate) enum Policy {
66    Allow(Target),
67    Warn(Target),
68    Error(Target),
69}
70
71impl<S: Stage> AttributeParser<'_, S> {
72    pub(crate) fn check_target(
73        &self,
74        attr_name: AttrPath,
75        attr_span: Span,
76        allowed_targets: &AllowedTargets,
77        target: Target,
78        target_id: S::Id,
79        mut emit_lint: impl FnMut(AttributeLint<S::Id>),
80    ) {
81        match allowed_targets.is_allowed(target) {
82            AllowedResult::Allowed => {}
83            AllowedResult::Warn => {
84                let allowed_targets = allowed_targets.allowed_targets();
85                let (applied, only) =
86                    allowed_targets_applied(allowed_targets, target, self.features);
87                emit_lint(AttributeLint {
88                    id: target_id,
89                    span: attr_span,
90                    kind: AttributeLintKind::InvalidTarget {
91                        name: attr_name,
92                        target,
93                        only: if only { "only " } else { "" },
94                        applied,
95                    },
96                });
97            }
98            AllowedResult::Error => {
99                let allowed_targets = allowed_targets.allowed_targets();
100                let (applied, only) =
101                    allowed_targets_applied(allowed_targets, target, self.features);
102                self.dcx().emit_err(InvalidTarget {
103                    span: attr_span,
104                    name: attr_name,
105                    target: target.plural_name(),
106                    only: if only { "only " } else { "" },
107                    applied: DiagArgValue::StrListSepByAnd(
108                        applied.into_iter().map(Cow::Owned).collect(),
109                    ),
110                });
111            }
112        }
113    }
114}
115
116/// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to.
117/// Does some heuristic-based filtering to remove uninteresting targets, and formats the targets into a string
118pub(crate) fn allowed_targets_applied(
119    mut allowed_targets: Vec<Target>,
120    target: Target,
121    features: Option<&Features>,
122) -> (Vec<String>, bool) {
123    // Remove unstable targets from `allowed_targets` if their features are not enabled
124    if let Some(features) = features {
125        if !features.fn_delegation() {
126            allowed_targets.retain(|t| !matches!(t, Target::Delegation { .. }));
127        }
128        if !features.stmt_expr_attributes() {
129            allowed_targets.retain(|t| !matches!(t, Target::Expression | Target::Statement));
130        }
131        if !features.extern_types() {
132            allowed_targets.retain(|t| !matches!(t, Target::ForeignTy));
133        }
134    }
135
136    // We define groups of "similar" targets.
137    // If at least two of the targets are allowed, and the `target` is not in the group,
138    // we collapse the entire group to a single entry to simplify the target list
139    const FUNCTION_LIKE: &[Target] = &[
140        Target::Fn,
141        Target::Closure,
142        Target::ForeignFn,
143        Target::Method(MethodKind::Inherent),
144        Target::Method(MethodKind::Trait { body: false }),
145        Target::Method(MethodKind::Trait { body: true }),
146        Target::Method(MethodKind::TraitImpl),
147    ];
148    const METHOD_LIKE: &[Target] = &[
149        Target::Method(MethodKind::Inherent),
150        Target::Method(MethodKind::Trait { body: false }),
151        Target::Method(MethodKind::Trait { body: true }),
152        Target::Method(MethodKind::TraitImpl),
153    ];
154    const IMPL_LIKE: &[Target] =
155        &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
156    const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum];
157
158    let mut added_fake_targets = Vec::new();
159    filter_targets(
160        &mut allowed_targets,
161        FUNCTION_LIKE,
162        "functions",
163        target,
164        &mut added_fake_targets,
165    );
166    filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets);
167    filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets);
168    filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets);
169
170    // If there is now only 1 target left, show that as the only possible target
171    (
172        added_fake_targets
173            .iter()
174            .copied()
175            .chain(allowed_targets.iter().map(|t| t.plural_name()))
176            .map(|i| i.to_string())
177            .collect(),
178        allowed_targets.len() + added_fake_targets.len() == 1,
179    )
180}
181
182fn filter_targets(
183    allowed_targets: &mut Vec<Target>,
184    target_group: &'static [Target],
185    target_group_name: &'static str,
186    target: Target,
187    added_fake_targets: &mut Vec<&'static str>,
188) {
189    if target_group.contains(&target) {
190        return;
191    }
192    if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 {
193        return;
194    }
195    allowed_targets.retain(|t| !target_group.contains(t));
196    added_fake_targets.push(target_group_name);
197}
198
199/// This is the list of all targets to which a attribute can be applied
200/// This is used for:
201/// - `rustc_dummy`, which can be applied to all targets
202/// - Attributes that are not parted to the new target system yet can use this list as a placeholder
203pub(crate) const ALL_TARGETS: &'static [Policy] = {
204    use Policy::Allow;
205    &[
206        Allow(Target::ExternCrate),
207        Allow(Target::Use),
208        Allow(Target::Static),
209        Allow(Target::Const),
210        Allow(Target::Fn),
211        Allow(Target::Closure),
212        Allow(Target::Mod),
213        Allow(Target::ForeignMod),
214        Allow(Target::GlobalAsm),
215        Allow(Target::TyAlias),
216        Allow(Target::Enum),
217        Allow(Target::Variant),
218        Allow(Target::Struct),
219        Allow(Target::Field),
220        Allow(Target::Union),
221        Allow(Target::Trait),
222        Allow(Target::TraitAlias),
223        Allow(Target::Impl { of_trait: false }),
224        Allow(Target::Impl { of_trait: true }),
225        Allow(Target::Expression),
226        Allow(Target::Statement),
227        Allow(Target::Arm),
228        Allow(Target::AssocConst),
229        Allow(Target::Method(MethodKind::Inherent)),
230        Allow(Target::Method(MethodKind::Trait { body: false })),
231        Allow(Target::Method(MethodKind::Trait { body: true })),
232        Allow(Target::Method(MethodKind::TraitImpl)),
233        Allow(Target::AssocTy),
234        Allow(Target::ForeignFn),
235        Allow(Target::ForeignStatic),
236        Allow(Target::ForeignTy),
237        Allow(Target::MacroDef),
238        Allow(Target::Param),
239        Allow(Target::PatField),
240        Allow(Target::ExprField),
241        Allow(Target::WherePredicate),
242        Allow(Target::MacroCall),
243        Allow(Target::Crate),
244        Allow(Target::Delegation { mac: false }),
245        Allow(Target::Delegation { mac: true }),
246    ]
247};