Skip to content

Commit 37d76a5

Browse files
Rollup merge of #153540 - scrabsha:sasha/drop-derive-helpers, r=jdonszelmann
drop derive helpers during attribute parsing
2 parents 83a0bd9 + 29e9273 commit 37d76a5

18 files changed

Lines changed: 383 additions & 328 deletions
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use rustc_errors::MultiSpan;
2+
use rustc_macros::{Diagnostic, Subdiagnostic};
3+
use rustc_span::{Span, Symbol};
4+
5+
#[derive(Diagnostic)]
6+
#[diag("`{$name}` attribute cannot be used at crate level")]
7+
pub(crate) struct InvalidAttrAtCrateLevel {
8+
#[primary_span]
9+
pub span: Span,
10+
#[suggestion(
11+
"perhaps you meant to use an outer attribute",
12+
code = "#[",
13+
applicability = "machine-applicable",
14+
style = "verbose"
15+
)]
16+
pub pound_to_opening_bracket: Span,
17+
pub name: Symbol,
18+
#[subdiagnostic]
19+
pub item: Option<ItemFollowingInnerAttr>,
20+
}
21+
22+
#[derive(Clone, Copy, Subdiagnostic)]
23+
#[label("the inner attribute doesn't annotate this item")]
24+
pub(crate) struct ItemFollowingInnerAttr {
25+
#[primary_span]
26+
pub span: Span,
27+
}
28+
29+
#[derive(Diagnostic)]
30+
#[diag("most attributes are not supported in `where` clauses")]
31+
#[help("only `#[cfg]` and `#[cfg_attr]` are supported")]
32+
pub(crate) struct UnsupportedAttributesInWhere {
33+
#[primary_span]
34+
pub span: MultiSpan,
35+
}

compiler/rustc_attr_parsing/src/interface.rs

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,11 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
269269
mut emit_lint: impl FnMut(LintId, Span, AttributeLintKind),
270270
) -> Vec<Attribute> {
271271
let mut attributes = Vec::new();
272+
// We store the attributes we intend to discard at the end of this function in order to
273+
// check they are applied to the right target and error out if necessary. In practice, we
274+
// end up dropping only derive attributes and derive helpers, both being fully processed
275+
// at macro expansion.
276+
let mut dropped_attributes = Vec::new();
272277
let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
273278
let mut early_parsed_state = EarlyParsedState::default();
274279

@@ -304,7 +309,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
304309
kind: DocFragmentKind::Sugared(*comment_kind),
305310
span: attr_span,
306311
comment: *symbol,
307-
}))
312+
}));
308313
}
309314
ast::AttrKind::Normal(n) => {
310315
attr_paths.push(PathParser(&n.item.path));
@@ -393,29 +398,33 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
393398
Self::check_target(&accept.allowed_targets, target, &mut cx);
394399
}
395400
} else {
396-
// If we're here, we must be compiling a tool attribute... Or someone
397-
// forgot to parse their fancy new attribute. Let's warn them in any case.
398-
// If you are that person, and you really think your attribute should
399-
// remain unparsed, carefully read the documentation in this module and if
400-
// you still think so you can add an exception to this assertion.
401-
402-
// FIXME(jdonszelmann): convert other attributes, and check with this that
403-
// we caught em all
404-
// const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg];
405-
// assert!(
406-
// self.tools.contains(&parts[0]) || true,
407-
// // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]),
408-
// "attribute {path} wasn't parsed and isn't a know tool attribute",
409-
// );
410-
411-
attributes.push(Attribute::Unparsed(Box::new(AttrItem {
401+
let attr = AttrItem {
412402
path: attr_path.clone(),
413403
args: self
414404
.lower_attr_args(n.item.args.unparsed_ref().unwrap(), lower_span),
415405
id: HashIgnoredAttrId { attr_id: attr.id },
416406
style: attr.style,
417407
span: attr_span,
418-
})));
408+
};
409+
410+
if !matches!(self.stage.should_emit(), ShouldEmit::Nothing)
411+
&& target == Target::Crate
412+
{
413+
self.check_invalid_crate_level_attr_item(&attr, n.item.span());
414+
}
415+
416+
let attr = Attribute::Unparsed(Box::new(attr));
417+
418+
if self.tools.contains(&parts[0])
419+
// FIXME: this can be removed once #152369 has been merged.
420+
// https://github.com/rust-lang/rust/pull/152369
421+
|| [sym::allow, sym::deny, sym::expect, sym::forbid, sym::warn]
422+
.contains(&parts[0])
423+
{
424+
attributes.push(attr);
425+
} else {
426+
dropped_attributes.push(attr);
427+
}
419428
}
420429
}
421430
}
@@ -431,6 +440,12 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
431440
}
432441
}
433442

443+
if !matches!(self.stage.should_emit(), ShouldEmit::Nothing)
444+
&& target == Target::WherePredicate
445+
{
446+
self.check_invalid_where_predicate_attrs(attributes.iter().chain(&dropped_attributes));
447+
}
448+
434449
attributes
435450
}
436451

compiler/rustc_attr_parsing/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
// tidy-alphabetical-start
8080
#![feature(decl_macro)]
8181
#![feature(iter_intersperse)]
82+
#![feature(try_blocks)]
8283
#![recursion_limit = "256"]
8384
// tidy-alphabetical-end
8485

@@ -99,6 +100,7 @@ mod interface;
99100
pub mod parser;
100101

101102
mod early_parsed;
103+
mod errors;
102104
mod safety;
103105
mod session_diagnostics;
104106
mod target_checking;

compiler/rustc_attr_parsing/src/target_checking.rs

Lines changed: 133 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
use std::borrow::Cow;
22

33
use rustc_ast::AttrStyle;
4-
use rustc_errors::DiagArgValue;
4+
use rustc_errors::{DiagArgValue, MultiSpan, StashKey};
55
use rustc_feature::Features;
6+
use rustc_hir::attrs::AttributeKind;
67
use rustc_hir::lints::AttributeLintKind;
7-
use rustc_hir::{MethodKind, Target};
8-
use rustc_span::sym;
8+
use rustc_hir::{AttrItem, Attribute, MethodKind, Target};
9+
use rustc_span::{BytePos, Span, Symbol, sym};
910

1011
use crate::AttributeParser;
1112
use crate::context::{AcceptContext, Stage};
13+
use crate::errors::{
14+
InvalidAttrAtCrateLevel, ItemFollowingInnerAttr, UnsupportedAttributesInWhere,
15+
};
1216
use crate::session_diagnostics::InvalidTarget;
1317
use crate::target_checking::Policy::Allow;
1418

@@ -96,6 +100,25 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
96100
return;
97101
}
98102

103+
if matches!(cx.attr_path.segments.as_ref(), [sym::repr]) && target == Target::Crate {
104+
// The allowed targets of `repr` depend on its arguments. They can't be checked using
105+
// the `AttributeParser` code.
106+
let span = cx.attr_span;
107+
let item =
108+
cx.cx.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span });
109+
110+
let pound_to_opening_bracket = cx.attr_span.until(cx.inner_span);
111+
112+
cx.dcx()
113+
.create_err(InvalidAttrAtCrateLevel {
114+
span,
115+
pound_to_opening_bracket,
116+
name: sym::repr,
117+
item,
118+
})
119+
.emit();
120+
}
121+
99122
match allowed_targets.is_allowed(target) {
100123
AllowedResult::Allowed => {}
101124
AllowedResult::Warn => {
@@ -163,6 +186,113 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
163186

164187
cx.emit_lint(rustc_session::lint::builtin::UNUSED_ATTRIBUTES, kind, attr_span);
165188
}
189+
190+
// FIXME: Fix "Cannot determine resolution" error and remove built-in macros
191+
// from this check.
192+
pub(crate) fn check_invalid_crate_level_attr_item(&self, attr: &AttrItem, inner_span: Span) {
193+
// Check for builtin attributes at the crate level
194+
// which were unsuccessfully resolved due to cannot determine
195+
// resolution for the attribute macro error.
196+
const ATTRS_TO_CHECK: &[Symbol] =
197+
&[sym::derive, sym::test, sym::test_case, sym::global_allocator, sym::bench];
198+
199+
// FIXME(jdonszelmann): all attrs should be combined here cleaning this up some day.
200+
if let Some(name) = ATTRS_TO_CHECK.iter().find(|attr_to_check| matches!(attr.path.segments.as_ref(), [segment] if segment == *attr_to_check)) {
201+
let span = attr.span;
202+
let name = *name;
203+
204+
let item = self.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span });
205+
206+
let err = self.dcx().create_err(InvalidAttrAtCrateLevel {
207+
span,
208+
pound_to_opening_bracket: span.until(inner_span),
209+
name,
210+
item,
211+
});
212+
213+
self.dcx().try_steal_replace_and_emit_err(
214+
attr.path.span,
215+
StashKey::UndeterminedMacroResolution,
216+
err,
217+
);
218+
}
219+
}
220+
221+
fn first_line_of_next_item(&self, span: Span) -> Option<Span> {
222+
// We can't exactly call `tcx.hir_free_items()` here because it's too early and querying
223+
// this would create a circular dependency. Instead, we resort to getting the original
224+
// source code that follows `span` and find the next item from here.
225+
226+
self.sess()
227+
.source_map()
228+
.span_to_source(span, |content, _, span_end| {
229+
let mut source = &content[span_end..];
230+
let initial_source_len = source.len();
231+
let span = try {
232+
loop {
233+
let first = source.chars().next()?;
234+
235+
if first.is_whitespace() {
236+
let split_idx = source.find(|c: char| !c.is_whitespace())?;
237+
source = &source[split_idx..];
238+
} else if source.starts_with("//") {
239+
let line_idx = source.find('\n')?;
240+
source = &source[line_idx + '\n'.len_utf8()..];
241+
} else if source.starts_with("/*") {
242+
// FIXME: support nested comments.
243+
let close_idx = source.find("*/")?;
244+
source = &source[close_idx + "*/".len()..];
245+
} else if first == '#' {
246+
// FIXME: properly find the end of the attributes in order to accurately
247+
// skip them. This version just consumes the source code until the next
248+
// `]`.
249+
let close_idx = source.find(']')?;
250+
source = &source[close_idx + ']'.len_utf8()..];
251+
} else {
252+
let lo = span_end + initial_source_len - source.len();
253+
let last_line = source.split('\n').next().map(|s| s.trim_end())?;
254+
255+
let hi = lo + last_line.len();
256+
let lo = BytePos(lo as u32);
257+
let hi = BytePos(hi as u32);
258+
let next_item_span = Span::new(lo, hi, span.ctxt(), None);
259+
260+
break next_item_span;
261+
}
262+
}
263+
};
264+
265+
Ok(span)
266+
})
267+
.ok()
268+
.flatten()
269+
}
270+
271+
pub(crate) fn check_invalid_where_predicate_attrs<'attr>(
272+
&self,
273+
attrs: impl IntoIterator<Item = &'attr Attribute>,
274+
) {
275+
// FIXME(where_clause_attrs): Currently, as the following check shows,
276+
// only `#[cfg]` and `#[cfg_attr]` are allowed, but it should be removed
277+
// if we allow more attributes (e.g., tool attributes and `allow/deny/warn`)
278+
// in where clauses. After that, this function would become useless.
279+
let spans = attrs
280+
.into_iter()
281+
// FIXME: We shouldn't need to special-case `doc`!
282+
.filter(|attr| {
283+
matches!(
284+
attr,
285+
Attribute::Parsed(AttributeKind::DocComment { .. } | AttributeKind::Doc(_))
286+
| Attribute::Unparsed(_)
287+
)
288+
})
289+
.map(|attr| attr.span())
290+
.collect::<Vec<_>>();
291+
if !spans.is_empty() {
292+
self.dcx()
293+
.emit_err(UnsupportedAttributesInWhere { span: MultiSpan::from_spans(spans) });
294+
}
295+
}
166296
}
167297

168298
/// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to.

0 commit comments

Comments
 (0)