Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use rustc_ast::{self as ast, AttrVec, GenericBound, NodeId, PatKind, attr, token
use rustc_attr_parsing::AttributeParser;
use rustc_errors::msg;
use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features};
use rustc_hir::Attribute;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::Attribute;
use rustc_session::Session;
use rustc_session::parse::{feature_err, feature_warn};
use rustc_span::{DUMMY_SP, Span, Spanned, Symbol, sym};
Expand Down
35 changes: 35 additions & 0 deletions compiler/rustc_attr_parsing/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use rustc_errors::MultiSpan;
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};

#[derive(Diagnostic)]
#[diag("`{$name}` attribute cannot be used at crate level")]
pub(crate) struct InvalidAttrAtCrateLevel {
#[primary_span]
pub span: Span,
#[suggestion(
"perhaps you meant to use an outer attribute",
code = "#[",
applicability = "machine-applicable",
style = "verbose"
)]
pub pound_to_opening_bracket: Span,
pub name: Symbol,
#[subdiagnostic]
pub item: Option<ItemFollowingInnerAttr>,
}

#[derive(Clone, Copy, Subdiagnostic)]
#[label("the inner attribute doesn't annotate this item")]
pub(crate) struct ItemFollowingInnerAttr {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("most attributes are not supported in `where` clauses")]
#[help("only `#[cfg]` and `#[cfg_attr]` are supported")]
pub(crate) struct UnsupportedAttributesInWhere {
#[primary_span]
pub span: MultiSpan,
}
51 changes: 33 additions & 18 deletions compiler/rustc_attr_parsing/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,11 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
mut emit_lint: impl FnMut(LintId, Span, AttributeLintKind),
) -> Vec<Attribute> {
let mut attributes = Vec::new();
// We store the attributes we intend to discard at the end of this function in order to
// check they are applied to the right target and error out if necessary. In practice, we
// end up dropping only derive attributes and derive helpers, both being fully processed
// at macro expansion.
let mut dropped_attributes = Vec::new();
let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
let mut early_parsed_state = EarlyParsedState::default();

Expand Down Expand Up @@ -302,7 +307,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
kind: DocFragmentKind::Sugared(*comment_kind),
span: attr_span,
comment: *symbol,
}))
}));
}
ast::AttrKind::Normal(n) => {
attr_paths.push(PathParser(&n.item.path));
Expand Down Expand Up @@ -390,29 +395,33 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
Self::check_target(&accept.allowed_targets, target, &mut cx);
}
} else {
// If we're here, we must be compiling a tool attribute... Or someone
// forgot to parse their fancy new attribute. Let's warn them in any case.
// If you are that person, and you really think your attribute should
// remain unparsed, carefully read the documentation in this module and if
// you still think so you can add an exception to this assertion.

// FIXME(jdonszelmann): convert other attributes, and check with this that
// we caught em all
// const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg];
// assert!(
// self.tools.contains(&parts[0]) || true,
// // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]),
// "attribute {path} wasn't parsed and isn't a know tool attribute",
// );

attributes.push(Attribute::Unparsed(Box::new(AttrItem {
let attr = AttrItem {
path: attr_path.clone(),
args: self
.lower_attr_args(n.item.args.unparsed_ref().unwrap(), lower_span),
id: HashIgnoredAttrId { attr_id: attr.id },
style: attr.style,
span: attr_span,
})));
};

if !matches!(self.stage.should_emit(), ShouldEmit::Nothing)
&& target == Target::Crate
{
self.check_invalid_crate_level_attr_item(&attr, n.item.span());
}

let attr = Attribute::Unparsed(Box::new(attr));

if self.tools.contains(&parts[0])
// FIXME: this can be removed once #152369 has been merged.
// https://github.com/rust-lang/rust/pull/152369
|| [sym::allow, sym::deny, sym::expect, sym::forbid, sym::warn]
.contains(&parts[0])
{
attributes.push(attr);
} else {
dropped_attributes.push(attr);
}
}
}
}
Expand All @@ -428,6 +437,12 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
}
}

if !matches!(self.stage.should_emit(), ShouldEmit::Nothing)
&& target == Target::WherePredicate
{
self.check_invalid_where_predicate_attrs(attributes.iter().chain(&dropped_attributes));
}

attributes
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_attr_parsing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
// tidy-alphabetical-start
#![feature(decl_macro)]
#![feature(iter_intersperse)]
#![feature(try_blocks)]
#![recursion_limit = "256"]
// tidy-alphabetical-end

Expand All @@ -99,6 +100,7 @@ mod interface;
pub mod parser;

mod early_parsed;
mod errors;
mod safety;
mod session_diagnostics;
mod target_checking;
Expand Down
137 changes: 134 additions & 3 deletions compiler/rustc_attr_parsing/src/target_checking.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use std::borrow::Cow;

use rustc_ast::AttrStyle;
use rustc_errors::DiagArgValue;
use rustc_errors::{DiagArgValue, MultiSpan, StashKey};
use rustc_feature::Features;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::AttributeLintKind;
use rustc_hir::{MethodKind, Target};
use rustc_span::sym;
use rustc_hir::{AttrItem, Attribute, MethodKind, Target};
use rustc_span::{BytePos, Span, Symbol, sym};

use crate::AttributeParser;
use crate::context::{AcceptContext, Stage};
use crate::errors::{InvalidAttrAtCrateLevel, ItemFollowingInnerAttr, UnsupportedAttributesInWhere};
use crate::session_diagnostics::InvalidTarget;
use crate::target_checking::Policy::Allow;

Expand Down Expand Up @@ -96,6 +98,27 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
return;
}

if matches!(cx.attr_path.segments.as_ref(), [sym::repr]) && target == Target::Crate {
// The allowed targets of `repr` depend on its arguments. They can't be checked using
// the `AttributeParser` code.
let span = cx.attr_span;
let item = cx
.cx
.first_line_of_next_item(span)
.map(|span| ItemFollowingInnerAttr { span });

let pound_to_opening_bracket = cx.attr_span.until(cx.inner_span);

cx.dcx()
.create_err(InvalidAttrAtCrateLevel {
span,
pound_to_opening_bracket,
name: sym::repr,
item,
})
.emit();
}

match allowed_targets.is_allowed(target) {
AllowedResult::Allowed => {}
AllowedResult::Warn => {
Expand Down Expand Up @@ -163,6 +186,114 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {

cx.emit_lint(rustc_session::lint::builtin::UNUSED_ATTRIBUTES, kind, attr_span);
}

// FIXME: Fix "Cannot determine resolution" error and remove built-in macros
// from this check.
pub(crate) fn check_invalid_crate_level_attr_item(&self, attr: &AttrItem, inner_span: Span) {
// Check for builtin attributes at the crate level
// which were unsuccessfully resolved due to cannot determine
// resolution for the attribute macro error.
const ATTRS_TO_CHECK: &[Symbol] =
&[sym::derive, sym::test, sym::test_case, sym::global_allocator, sym::bench];

// FIXME(jdonszelmann): all attrs should be combined here cleaning this up some day.
if let Some(name) = ATTRS_TO_CHECK.iter().find(|attr_to_check| matches!(attr.path.segments.as_ref(), [segment] if segment == *attr_to_check)) {
let span = attr.span;
let name = *name;

let item = self.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span });

let err = self.dcx().create_err(InvalidAttrAtCrateLevel {
span,
pound_to_opening_bracket: span.until(inner_span),
name,
item,
});

self.dcx().try_steal_replace_and_emit_err(
attr.path.span,
StashKey::UndeterminedMacroResolution,
err,
);
}
}

fn first_line_of_next_item(&self, span: Span) -> Option<Span> {
// We can't exactly call `tcx.hir_free_items()` here because it's too early and querying
// this would create a circular dependency. Instead, we resort to getting the original
// source code that follows `span` and find the next item from here.

self.sess()
.source_map()
.span_to_source(span, |content, _, span_end| {
let mut source = &content[span_end..];
let initial_source_len = source.len();
let span = try {
loop {
let first = source.chars().next()?;

if first.is_whitespace() {
let split_idx = source.find(|c: char| !c.is_whitespace())?;
source = &source[split_idx..];
} else if source.starts_with("//") {
let line_idx = source.find('\n')?;
source = &source[line_idx + '\n'.len_utf8()..];
} else if source.starts_with("/*") {
// FIXME: support nested comments.
let close_idx = source.find("*/")?;
source = &source[close_idx + "*/".len()..];
} else if first == '#' {
// FIXME: properly find the end of the attributes in order to accurately
// skip them. This version just consumes the source code until the next
// `]`.
let close_idx = source.find(']')?;
source = &source[close_idx + ']'.len_utf8()..];
} else {
let lo = span_end + initial_source_len - source.len();
let last_line = source.split('\n').next().map(|s| s.trim_end())?;

let hi = lo + last_line.len();
let lo = BytePos(lo as u32);
let hi = BytePos(hi as u32);
let next_item_span = Span::new(lo, hi, span.ctxt(), None);

break next_item_span;
}
}
};

Ok(span)
})
.ok()
.flatten()
}

pub(crate) fn check_invalid_where_predicate_attrs<'attr>(
&self,
attrs: impl IntoIterator<Item = &'attr Attribute>,
) {
// FIXME(where_clause_attrs): Currently, as the following check shows,
// only `#[cfg]` and `#[cfg_attr]` are allowed, but it should be removed
// if we allow more attributes (e.g., tool attributes and `allow/deny/warn`)
// in where clauses. After that, this function would become useless.
let spans = attrs
.into_iter()
// FIXME: We shouldn't need to special-case `doc`!
.filter(|attr| {
matches!(
attr,
Attribute::Parsed(AttributeKind::DocComment { .. } | AttributeKind::Doc(_))
| Attribute::Unparsed(_)
)
})
.map(|attr| attr.span())
.collect::<Vec<_>>();
if !spans.is_empty() {
self.dcx().emit_err(UnsupportedAttributesInWhere {
span: MultiSpan::from_spans(spans),
});
}
}
}

/// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ use rustc_ast::{
};
use rustc_attr_parsing::AttributeParser;
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_hir::Attribute;
use rustc_hir::attrs::{AttributeKind, ReprPacked};
use rustc_hir::Attribute;
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
use thin_vec::{ThinVec, thin_vec};
use ty::{Bounds, Path, Ref, Self_, Ty};
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use rustc_ast_pretty::pprust;
use rustc_attr_parsing::AttributeParser;
use rustc_errors::{Applicability, Diag, Level};
use rustc_expand::base::*;
use rustc_hir::Attribute;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::Attribute;
use rustc_span::{ErrorGuaranteed, Ident, RemapPathScopeComponents, Span, Symbol, sym};
use thin_vec::{ThinVec, thin_vec};
use tracing::debug;
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ use rustc_hir::attrs::{AttributeKind, DocAttribute};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
use rustc_hir::intravisit::FnKind as HirFnKind;
use rustc_hir::{self as hir, Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr};
use rustc_hir::{
self as hir, Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr,
};
use rustc_middle::bug;
use rustc_middle::lint::LevelAndSource;
use rustc_middle::ty::layout::LayoutOf;
Expand Down
Loading
Loading