Skip to content

Commit 557f5e3

Browse files
committed
port #[allow], #[deny] to attribute parser
1 parent 6efa357 commit 557f5e3

26 files changed

Lines changed: 724 additions & 232 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3545,6 +3545,7 @@ dependencies = [
35453545
"rustc_hir",
35463546
"rustc_lexer",
35473547
"rustc_macros",
3548+
"rustc_middle",
35483549
"rustc_parse",
35493550
"rustc_session",
35503551
"rustc_span",

compiler/rustc_ast_lowering/src/delegation.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
297297
DUMMY_NODE_ID,
298298
Some(self.tcx.features()),
299299
ShouldEmit::Nothing,
300+
None,
300301
));
301302
}
302303
}

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ struct LoweringContext<'a, 'hir> {
151151

152152
impl<'a, 'hir> LoweringContext<'a, 'hir> {
153153
fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self {
154-
let registered_tools = tcx.registered_tools(()).iter().map(|x| x.name).collect();
154+
let registered_tools = tcx.registered_tools(());
155155
Self {
156156
// Pseudo-globals.
157157
tcx,
@@ -205,7 +205,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
205205
attribute_parser: AttributeParser::new(
206206
tcx.sess,
207207
tcx.features(),
208-
registered_tools,
208+
registered_tools.clone(),
209209
Late,
210210
),
211211
delayed_lints: Vec::new(),

compiler/rustc_attr_parsing/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ rustc_feature = { path = "../rustc_feature" }
1414
rustc_hir = { path = "../rustc_hir" }
1515
rustc_lexer = { path = "../rustc_lexer" }
1616
rustc_macros = { path = "../rustc_macros" }
17+
rustc_middle = {path = "../rustc_middle"}
1718
rustc_parse = { path = "../rustc_parse" }
1819
rustc_session = { path = "../rustc_session" }
1920
rustc_span = { path = "../rustc_span" }

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ impl<S: Stage> AttributeParser<S> for NakedParser {
262262
// conditional compilation
263263
sym::cfg_trace,
264264
sym::cfg_attr_trace,
265+
sym::cfg,
265266
// testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
266267
sym::test,
267268
sym::ignore,
@@ -294,10 +295,11 @@ impl<S: Stage> AttributeParser<S> for NakedParser {
294295

295296
let span = self.span?;
296297

298+
let tools = cx.tools.iter().map(|tool| tool.name).collect::<Vec<_>>();
297299
// only if we found a naked attribute do we do the somewhat expensive check
298300
'outer: for other_attr in cx.all_attrs {
299301
for allowed_attr in ALLOW_LIST {
300-
if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
302+
if other_attr.segments().next().is_some_and(|i| tools.contains(&i.name)) {
301303
// effectively skips the error message being emitted below
302304
// if it's a tool attribute
303305
continue 'outer;
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
2+
use rustc_hir::lints::{AttributeLintKind, RenamedLintSuggestion};
3+
use rustc_middle::bug;
4+
use rustc_session::lint::builtin::UNKNOWN_LINTS;
5+
use rustc_session::{lint::builtin::RENAMED_AND_REMOVED_LINTS};
6+
use rustc_session::lint::{CheckLintNameResult};
7+
use crate::session_diagnostics::UnknownToolInScopedLint;
8+
9+
use super::prelude::*;
10+
11+
#[derive(Default)]
12+
pub(crate) struct AllowParser {
13+
lint_ids: ThinVec<(Symbol, Span)>,
14+
reason: Option<Symbol>,
15+
errored: bool,
16+
}
17+
18+
// Needs to be manually impl:ed as `AttributeParser`, because otherwise deduplication occurs
19+
impl<S: Stage> AttributeParser<S> for AllowParser {
20+
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
21+
&[sym::allow],
22+
template!(
23+
List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#],
24+
"https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes"
25+
),
26+
|this, cx, args| {
27+
validate_lint_attr(cx, args, &mut this.lint_ids, &mut this.errored, &mut this.reason);
28+
},
29+
)];
30+
31+
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
32+
if !self.lint_ids.is_empty() && !self.errored {
33+
Some(AttributeKind::Allow { lint_ids: self.lint_ids, reason: self.reason })
34+
} else {
35+
None
36+
}
37+
}
38+
39+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
40+
}
41+
#[derive(Default)]
42+
pub(crate) struct DenyParser {
43+
lint_ids: ThinVec<(Symbol, Span)>,
44+
reason: Option<Symbol>,
45+
errored: bool,
46+
}
47+
impl<S: Stage> AttributeParser<S> for DenyParser {
48+
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
49+
&[sym::deny],
50+
template!(
51+
List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#],
52+
"https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes"
53+
),
54+
|this, cx, args| {
55+
validate_lint_attr(cx, args, &mut this.lint_ids, &mut this.errored, &mut this.reason);
56+
},
57+
)];
58+
59+
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
60+
if !self.lint_ids.is_empty() && !self.errored {
61+
Some(AttributeKind::Deny { lint_ids: self.lint_ids, reason: self.reason })
62+
} else {
63+
None
64+
}
65+
}
66+
67+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
68+
}
69+
70+
71+
fn validate_lint_attr<S: Stage>(
72+
cx: &mut AcceptContext<'_, '_, S>,
73+
args: &ArgParser,
74+
lint_ids: &mut ThinVec<(Symbol, Span)>,
75+
errored: &mut bool,
76+
reason: &mut Option<Symbol>,
77+
) {
78+
79+
let Some(list) = args.list().map(MetaItemListParser::mixed) else {
80+
cx.expected_list(cx.inner_span, args);
81+
return;
82+
};
83+
84+
let mut list = list.peekable();
85+
86+
while let Some(item) = list.next() {
87+
let Some(meta_item) = item.meta_item() else {
88+
cx.expected_string_literal(item.span(), item.lit());
89+
*errored = true;
90+
return;
91+
};
92+
if let Some(args) = meta_item.word_is(sym::reason) {
93+
let ArgParser::NameValue(nv) = args else {
94+
cx.expected_name_value(meta_item.span(), Some(sym::reason));
95+
*errored = true;
96+
continue;
97+
};
98+
if list.peek().is_some() {
99+
// TODO: proper error
100+
*errored = true;
101+
continue;
102+
}
103+
let Some(s) = nv.value_as_str() else {
104+
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
105+
*errored = true;
106+
continue;
107+
};
108+
*reason = Some(s);
109+
} else {
110+
let mut segments = meta_item.path().segments();
111+
112+
let Some(tool_or_name) = segments.next() else {
113+
bug!("first segment should always exist");
114+
};
115+
116+
let rest = segments.collect::<Vec<_>>();
117+
let (tool_name, tool_span, name): (Option<Symbol>, Option<Span>, _) = if rest.is_empty() {
118+
let name = tool_or_name.name;
119+
(None,None, name.to_string())
120+
} else {
121+
let tool = tool_or_name;
122+
let name =
123+
rest.into_iter().map(|ident| ident.to_string()).collect::<Vec<_>>().join("::");
124+
(Some(tool.name), Some(tool.span), name)
125+
};
126+
127+
if let Some(ids) = check_lint(cx, &name, tool_name, tool_span, meta_item.span()) {
128+
lint_ids.extend(ids);
129+
}
130+
}
131+
}
132+
}
133+
134+
fn check_lint<S: Stage>(
135+
cx: &mut AcceptContext<'_, '_, S>,
136+
name: &str,
137+
tool_name: Option<Symbol>,
138+
tool_span: Option<Span>,
139+
meta_item_span: Span,
140+
) -> Option<Vec<(Symbol, Span)>> {
141+
let Some(lint_store) = &cx.sess.lint_store else {
142+
bug!("lint_store required while parsing attributes");
143+
};
144+
let mut lint_ids = Vec::new();
145+
match lint_store.check_lint_name(name, tool_name, &cx.tools) {
146+
CheckLintNameResult::Ok(ids) => {
147+
for id in ids {
148+
lint_ids.push((Symbol::intern(&id.to_string()), meta_item_span));
149+
}
150+
}
151+
// TODO fix tools
152+
CheckLintNameResult::Tool(ids, new_lint_name) => {
153+
let _name = match new_lint_name {
154+
None => {
155+
let complete_name = &format!("{}::{}", tool_name.unwrap(), name);
156+
Symbol::intern(complete_name)
157+
}
158+
Some(new_lint_name) => {
159+
let new_lint_name = Symbol::intern(&new_lint_name);
160+
cx.emit_lint(RENAMED_AND_REMOVED_LINTS,AttributeLintKind::DeprecatedLintName {
161+
name: Symbol::intern(name),
162+
suggestion: meta_item_span,
163+
replace: new_lint_name,
164+
},
165+
meta_item_span,);
166+
new_lint_name
167+
168+
}
169+
};
170+
for id in ids {
171+
lint_ids.push((Symbol::intern(&id.to_string()), meta_item_span));
172+
}
173+
}
174+
175+
CheckLintNameResult::MissingTool => {
176+
// If `MissingTool` is returned, then either the lint does not
177+
// exist in the tool or the code was not compiled with the tool and
178+
// therefore the lint was never added to the `LintStore`. To detect
179+
// this is the responsibility of the lint tool.
180+
return None;
181+
}
182+
183+
CheckLintNameResult::NoTool => {
184+
cx.emit_err(UnknownToolInScopedLint {
185+
span: tool_span,
186+
tool_name: tool_name.unwrap(),
187+
lint_name: name.to_owned(),
188+
is_nightly_build: cx.sess.is_nightly_build(),
189+
});
190+
return None;
191+
}
192+
193+
CheckLintNameResult::Renamed(replace) => {
194+
let name =
195+
tool_name.map(|tool| format!("{tool}::{name}")).unwrap_or(name.to_string());
196+
cx.emit_lint(RENAMED_AND_REMOVED_LINTS, AttributeLintKind::RenamedLint {
197+
name: Symbol::intern(&name),
198+
replace,
199+
suggestion: RenamedLintSuggestion::WithSpan {suggestion: meta_item_span}
200+
}, meta_item_span);
201+
202+
// If this lint was renamed, apply the new lint instead of ignoring the
203+
// attribute. Ignore any errors or warnings that happen because the new
204+
// name is inaccurate.
205+
// NOTE: `new_name` already includes the tool name, so we don't
206+
// have to add it again.
207+
let CheckLintNameResult::Ok(ids) = lint_store.check_lint_name(replace.as_str(), None, &cx.tools)
208+
else {
209+
panic!("renamed lint does not exist: {replace}");
210+
};
211+
212+
for id in ids {
213+
let name = Symbol::intern(&id.to_string());
214+
lint_ids.push((name, meta_item_span));
215+
}
216+
}
217+
218+
CheckLintNameResult::Removed(reason) => {
219+
let name =
220+
tool_name.map(|tool| format!("{tool}::{name}")).unwrap_or(name.to_owned());
221+
cx.emit_lint(RENAMED_AND_REMOVED_LINTS, AttributeLintKind::RemovedLint {name, reason}, meta_item_span);
222+
return None;
223+
}
224+
225+
CheckLintNameResult::NoLint(suggestion) => {
226+
let name =
227+
tool_name.map(|tool| format!("{tool}::{name}")).unwrap_or(name.to_owned());
228+
cx.emit_lint(UNKNOWN_LINTS, AttributeLintKind::UnknownLint { name, suggestion, span: meta_item_span }, meta_item_span);
229+
return None;
230+
}
231+
}
232+
Some(lint_ids)
233+
}

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub(crate) mod dummy;
4545
pub(crate) mod inline;
4646
pub(crate) mod instruction_set;
4747
pub(crate) mod link_attrs;
48+
pub(crate) mod lint;
4849
pub(crate) mod lint_helpers;
4950
pub(crate) mod loop_match;
5051
pub(crate) mod macro_attrs;

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use crate::attributes::dummy::*;
3232
use crate::attributes::inline::*;
3333
use crate::attributes::instruction_set::*;
3434
use crate::attributes::link_attrs::*;
35+
use crate::attributes::lint::*;
3536
use crate::attributes::lint_helpers::*;
3637
use crate::attributes::loop_match::*;
3738
use crate::attributes::macro_attrs::*;
@@ -141,10 +142,12 @@ attribute_parsers!(
141142
// tidy-alphabetical-start
142143
AlignParser,
143144
AlignStaticParser,
145+
AllowParser,
144146
BodyStabilityParser,
145147
ConfusablesParser,
146148
ConstStabilityParser,
147149
DocParser,
150+
DenyParser,
148151
MacroUseParser,
149152
NakedParser,
150153
StabilityParser,

compiler/rustc_attr_parsing/src/interface.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_feature::{AttributeTemplate, Features};
88
use rustc_hir::attrs::AttributeKind;
99
use rustc_hir::lints::AttributeLintKind;
1010
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target};
11+
use rustc_middle::ty::RegisteredTools;
1112
use rustc_session::Session;
1213
use rustc_session::lint::{BuiltinLintDiag, LintId};
1314
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
@@ -21,7 +22,7 @@ use crate::{Early, Late, OmitDoc, ShouldEmit};
2122
/// Context created once, for example as part of the ast lowering
2223
/// context, through which all attributes can be lowered.
2324
pub struct AttributeParser<'sess, S: Stage = Late> {
24-
pub(crate) tools: Vec<Symbol>,
25+
pub(crate) tools: RegisteredTools,
2526
pub(crate) features: Option<&'sess Features>,
2627
pub(crate) sess: &'sess Session,
2728
pub(crate) stage: S,
@@ -86,6 +87,7 @@ impl<'sess> AttributeParser<'sess, Early> {
8687
target_node_id,
8788
features,
8889
should_emit,
90+
None,
8991
);
9092
assert!(parsed.len() <= 1);
9193
parsed.pop()
@@ -107,9 +109,15 @@ impl<'sess> AttributeParser<'sess, Early> {
107109
target_node_id: NodeId,
108110
features: Option<&'sess Features>,
109111
emit_errors: ShouldEmit,
112+
tools: Option<RegisteredTools>,
110113
) -> Vec<Attribute> {
111-
let mut p =
112-
Self { features, tools: Vec::new(), parse_only, sess, stage: Early { emit_errors } };
114+
let mut p = Self {
115+
features,
116+
tools: tools.unwrap_or_default(),
117+
parse_only,
118+
sess,
119+
stage: Early { emit_errors },
120+
};
113121
p.parse_attribute_list(
114122
attrs,
115123
target_span,
@@ -193,7 +201,7 @@ impl<'sess> AttributeParser<'sess, Early> {
193201
) -> T {
194202
let mut parser = Self {
195203
features,
196-
tools: Vec::new(),
204+
tools: RegisteredTools::default(),
197205
parse_only: None,
198206
sess,
199207
stage: Early { emit_errors },
@@ -231,7 +239,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
231239
pub fn new(
232240
sess: &'sess Session,
233241
features: &'sess Features,
234-
tools: Vec<Symbol>,
242+
tools: RegisteredTools,
235243
stage: S,
236244
) -> Self {
237245
Self { features: Some(features), tools, parse_only: None, sess, stage }

0 commit comments

Comments
 (0)