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
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3995,6 +3995,7 @@ dependencies = [
"rustc_ast_pretty",
"rustc_data_structures",
"rustc_error_messages",
"rustc_errors",
"rustc_hashes",
"rustc_hir_id",
"rustc_index",
Expand Down
28 changes: 19 additions & 9 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use std::sync::Arc;

use rustc_ast::node_id::NodeMap;
use rustc_ast::{self as ast, *};
use rustc_attr_parsing::{AttributeParser, Late, OmitDoc};
use rustc_attr_parsing::{AttributeParser, EmitAttribute, Late, OmitDoc};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::sorted_map::SortedMap;
Expand All @@ -51,7 +51,7 @@ use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle};
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
use rustc_hir::definitions::{DefPathData, DisambiguatorState};
use rustc_hir::lints::{AttributeLint, DelayedLint};
use rustc_hir::lints::{AttributeLint, DelayedLint, DynAttribute};
use rustc_hir::{
self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LifetimeSource,
LifetimeSyntax, ParamName, Target, TraitCandidate, find_attr,
Expand Down Expand Up @@ -1166,13 +1166,23 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
target,
OmitDoc::Lower,
|s| l.lower(s),
|lint_id, span, kind| {
self.delayed_lints.push(DelayedLint::AttributeParsing(AttributeLint {
lint_id,
id: target_hir_id,
span,
kind,
}));
|lint_id, span, kind| match kind {
EmitAttribute::Static(attr_kind) => {
self.delayed_lints.push(DelayedLint::AttributeParsing(AttributeLint {
lint_id,
id: target_hir_id,
span,
kind: attr_kind,
}));
}
EmitAttribute::Dynamic(callback) => {
self.delayed_lints.push(DelayedLint::Dynamic(DynAttribute {
lint_id,
id: target_hir_id,
span,
callback,
}));
}
},
)
}
Expand Down
15 changes: 9 additions & 6 deletions compiler/rustc_attr_parsing/src/attributes/doc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit};
use rustc_errors::msg;
use rustc_errors::{Diagnostic, msg};
use rustc_feature::template;
use rustc_hir::Target;
use rustc_hir::attrs::{
Expand Down Expand Up @@ -171,12 +171,15 @@ impl DocParser {

if let Some(used_span) = self.attribute.no_crate_inject {
let unused_span = path.span();
cx.emit_lint(
cx.emit_dyn_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::UnusedDuplicate {
this: unused_span,
other: used_span,
warning: true,
move |dcx, level| {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really like that a closure is needed here, I experimented a bit locally and this seems to be because we store the result in a query, and to call IntoDiagnostic we need ownership of this thing, which the query can't provide.

We could theoretically work around that with Steal but that seems a bit too extreme tool for such a small annoyance

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe taking a diagnostic and requiring it to be Clone solves it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be checked but it's an idea I had in mind, just didn't try it out yet.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After testing out a bit, I think the current approach is the best one we can have because Box<dyn Diagnostic> is unhappy because dyn Diagnostic is not Sized (obviously) and keeping a &dyn Diagnostic creates more issues than it solves. I think it's (sadly) the best "form" we can have for now.

rustc_errors::lints::UnusedDuplicate {
this: unused_span,
other: used_span,
warning: true,
}
.into_diag(dcx, level)
},
unused_span,
);
Expand Down
51 changes: 38 additions & 13 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use std::sync::LazyLock;

use private::Sealed;
use rustc_ast::{AttrStyle, MetaItemLit, NodeId};
use rustc_errors::{Diag, Diagnostic, Level};
use rustc_data_structures::sync::{DynSend, DynSync};
use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level};
use rustc_feature::{AttrSuggestionStyle, AttributeTemplate};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::AttributeLintKind;
Expand All @@ -16,7 +17,6 @@ use rustc_session::Session;
use rustc_session::lint::{Lint, LintId};
use rustc_span::{ErrorGuaranteed, Span, Symbol};

use crate::AttributeParser;
// Glob imports to avoid big, bitrotty import lists
use crate::attributes::allow_unstable::*;
use crate::attributes::autodiff::*;
Expand Down Expand Up @@ -63,6 +63,7 @@ use crate::session_diagnostics::{
AttributeParseError, AttributeParseErrorReason, ParsedDescription,
};
use crate::target_checking::AllowedTargets;
use crate::{AttributeParser, EmitAttribute};
type GroupType<S> = LazyLock<GroupTypeInner<S>>;

pub(super) struct GroupTypeInner<S: Stage> {
Expand Down Expand Up @@ -448,6 +449,24 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
/// must be delayed until after HIR is built. This method will take care of the details of
/// that.
pub(crate) fn emit_lint(&mut self, lint: &'static Lint, kind: AttributeLintKind, span: Span) {
self.emit_lint_inner(lint, EmitAttribute::Static(kind), span);
}

/// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing
/// must be delayed until after HIR is built. This method will take care of the details of
/// that.
pub(crate) fn emit_dyn_lint<
F: for<'a> Fn(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + DynSync + 'static,
>(
&mut self,
lint: &'static Lint,
callback: F,
span: Span,
) {
self.emit_lint_inner(lint, EmitAttribute::Dynamic(Box::new(callback)), span);
}

fn emit_lint_inner(&mut self, lint: &'static Lint, kind: EmitAttribute, span: Span) {
if !matches!(
self.stage.should_emit(),
ShouldEmit::ErrorsAndLints { .. } | ShouldEmit::EarlyFatal { also_emit_lints: true }
Expand All @@ -458,12 +477,15 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
}

pub(crate) fn warn_unused_duplicate(&mut self, used_span: Span, unused_span: Span) {
self.emit_lint(
self.emit_dyn_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
AttributeLintKind::UnusedDuplicate {
this: unused_span,
other: used_span,
warning: false,
move |dcx, level| {
rustc_errors::lints::UnusedDuplicate {
this: unused_span,
other: used_span,
warning: false,
}
.into_diag(dcx, level)
},
unused_span,
)
Expand All @@ -474,12 +496,15 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
used_span: Span,
unused_span: Span,
) {
self.emit_lint(
self.emit_dyn_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
AttributeLintKind::UnusedDuplicate {
this: unused_span,
other: used_span,
warning: true,
move |dcx, level| {
rustc_errors::lints::UnusedDuplicate {
this: unused_span,
other: used_span,
warning: true,
}
.into_diag(dcx, level)
},
unused_span,
)
Expand Down Expand Up @@ -720,7 +745,7 @@ pub struct SharedContext<'p, 'sess, S: Stage> {

/// The second argument of the closure is a [`NodeId`] if `S` is `Early` and a [`HirId`] if `S`
/// is `Late` and is the ID of the syntactical component this attribute was applied to.
pub(crate) emit_lint: &'p mut dyn FnMut(LintId, Span, AttributeLintKind),
pub(crate) emit_lint: &'p mut dyn FnMut(LintId, Span, EmitAttribute),
}

/// Context given to every attribute parser during finalization.
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_attr_parsing/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,12 @@ pub(crate) struct UnreachableCfgSelectPredicateWildcard {
#[label("always matches")]
pub wildcard_span: Span,
}

#[derive(Diagnostic)]
#[diag("unsafe attribute used without unsafe")]
pub(crate) struct UnsafeAttrOutsideUnsafeLint {
#[label("usage of unsafe attribute")]
pub span: Span,
#[subdiagnostic]
pub suggestion: Option<crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion>,
}
54 changes: 37 additions & 17 deletions compiler/rustc_attr_parsing/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use std::convert::identity;
use rustc_ast as ast;
use rustc_ast::token::DocFragmentKind;
use rustc_ast::{AttrItemKind, AttrStyle, NodeId, Safety};
use rustc_errors::DiagCtxtHandle;
use rustc_data_structures::sync::{DynSend, DynSync};
use rustc_errors::{Diag, DiagCtxtHandle, Level};
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::AttributeLintKind;
Expand All @@ -18,6 +19,15 @@ use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser};
use crate::session_diagnostics::ParsedDescription;
use crate::{Early, Late, OmitDoc, ShouldEmit};

pub enum EmitAttribute {
Static(AttributeLintKind),
Dynamic(
Box<
dyn for<'a> Fn(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + DynSync + 'static,
>,
),
}

/// Context created once, for example as part of the ast lowering
/// context, through which all attributes can be lowered.
pub struct AttributeParser<'sess, S: Stage = Late> {
Expand Down Expand Up @@ -116,13 +126,18 @@ impl<'sess> AttributeParser<'sess, Early> {
target,
OmitDoc::Skip,
std::convert::identity,
|lint_id, span, kind| {
sess.psess.buffer_lint(
lint_id.lint,
span,
target_node_id,
BuiltinLintDiag::AttributeLint(kind),
)
|lint_id, span, kind| match kind {
EmitAttribute::Static(attr_kind) => {
sess.psess.buffer_lint(
lint_id.lint,
span,
target_node_id,
BuiltinLintDiag::AttributeLint(attr_kind),
);
}
EmitAttribute::Dynamic(callback) => {
sess.psess.dyn_buffer_lint(lint_id.lint, span, target_node_id, callback);
}
},
)
}
Expand Down Expand Up @@ -200,16 +215,21 @@ impl<'sess> AttributeParser<'sess, Early> {
sess,
stage: Early { emit_errors },
};
let mut emit_lint = |lint_id: LintId, span: Span, kind: AttributeLintKind| {
sess.psess.buffer_lint(
lint_id.lint,
span,
target_node_id,
BuiltinLintDiag::AttributeLint(kind),
)
let mut emit_lint = |lint_id: LintId, span: Span, kind: EmitAttribute| match kind {
EmitAttribute::Static(attr_kind) => {
sess.psess.buffer_lint(
lint_id.lint,
span,
target_node_id,
BuiltinLintDiag::AttributeLint(attr_kind),
);
}
EmitAttribute::Dynamic(callback) => {
sess.psess.dyn_buffer_lint(lint_id.lint, span, target_node_id, callback);
}
};
if let Some(safety) = attr_safety {
parser.check_attribute_safety(&attr_path, inner_span, safety, &mut emit_lint)
parser.check_attribute_safety(&attr_path, inner_span, safety, &mut emit_lint);
}
let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
shared: SharedContext {
Expand Down Expand Up @@ -266,7 +286,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
target: Target,
omit_doc: OmitDoc,
lower_span: impl Copy + Fn(Span) -> Span,
mut emit_lint: impl FnMut(LintId, Span, AttributeLintKind),
mut emit_lint: impl FnMut(LintId, Span, EmitAttribute),
) -> Vec<Attribute> {
let mut attributes = Vec::new();
// We store the attributes we intend to discard at the end of this function in order to
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,5 @@ pub use attributes::cfg::{
pub use attributes::cfg_select::*;
pub use attributes::util::{is_builtin_attr, parse_version};
pub use context::{Early, Late, OmitDoc, ShouldEmit};
pub use interface::AttributeParser;
pub use interface::{AttributeParser, EmitAttribute};
pub use session_diagnostics::ParsedDescription;
22 changes: 14 additions & 8 deletions compiler/rustc_attr_parsing/src/safety.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
use rustc_ast::Safety;
use rustc_errors::Diagnostic;
use rustc_feature::{AttributeSafety, BUILTIN_ATTRIBUTE_MAP};
use rustc_hir::AttrPath;
use rustc_hir::lints::AttributeLintKind;
use rustc_session::lint::LintId;
use rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE;
use rustc_span::Span;

use crate::context::Stage;
use crate::{AttributeParser, ShouldEmit};
use crate::{AttributeParser, EmitAttribute, ShouldEmit, errors};

impl<'sess, S: Stage> AttributeParser<'sess, S> {
pub fn check_attribute_safety(
&mut self,
attr_path: &AttrPath,
attr_span: Span,
attr_safety: Safety,
emit_lint: &mut impl FnMut(LintId, Span, AttributeLintKind),
emit_lint: &mut impl FnMut(LintId, Span, EmitAttribute),
) {
if matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
return;
Expand Down Expand Up @@ -84,11 +84,17 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
emit_lint(
LintId::of(UNSAFE_ATTR_OUTSIDE_UNSAFE),
path_span,
AttributeLintKind::UnsafeAttrOutsideUnsafe {
attribute_name_span: path_span,
sugg_spans: not_from_proc_macro
.then(|| (diag_span.shrink_to_lo(), diag_span.shrink_to_hi())),
},
EmitAttribute::Dynamic(Box::new(move |dcx, level| {
errors::UnsafeAttrOutsideUnsafeLint {
span: path_span,
suggestion: not_from_proc_macro
.then(|| (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()))
.map(|(left, right)| {
crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion { left, right }
}),
}
.into_diag(dcx, level)
})),
)
}
}
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_errors/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,18 @@ impl<'a> Diagnostic<'a, ()>
}
}

pub struct DiagCallback<'a>(
pub &'a Box<
dyn for<'b> Fn(DiagCtxtHandle<'b>, Level) -> Diag<'b, ()> + DynSend + DynSync + 'static,
>,
);

impl<'a, 'b> Diagnostic<'a, ()> for DiagCallback<'b> {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
(self.0)(dcx, level)
}
}

/// Type used to emit diagnostic through a closure instead of implementing the `Diagnostic` trait.
pub struct DiagDecorator<F: FnOnce(&mut Diag<'_, ()>)>(pub F);

Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ pub use anstyle::{
pub use codes::*;
pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer};
pub use diagnostic::{
BugAbort, Diag, DiagDecorator, DiagInner, DiagLocation, DiagStyledString, Diagnostic,
EmissionGuarantee, FatalAbort, StringPart, Subdiag, Subdiagnostic,
BugAbort, Diag, DiagCallback, DiagDecorator, DiagInner, DiagLocation, DiagStyledString,
Diagnostic, EmissionGuarantee, FatalAbort, StringPart, Subdiag, Subdiagnostic,
};
pub use diagnostic_impls::{
DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter,
Expand Down Expand Up @@ -77,6 +77,7 @@ mod diagnostic_impls;
pub mod emitter;
pub mod formatting;
pub mod json;
pub mod lints;
mod lock;
pub mod markdown;
pub mod timings;
Expand Down
15 changes: 15 additions & 0 deletions compiler/rustc_errors/src/lints.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use rustc_macros::Diagnostic;
use rustc_span::Span;

#[derive(Diagnostic)]
#[diag("unused attribute")]
pub struct UnusedDuplicate {
#[suggestion("remove this attribute", code = "", applicability = "machine-applicable")]
pub this: Span,
#[note("attribute also specified here")]
pub other: Span,
#[warning(
"this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!"
)]
pub warning: bool,
}
Loading
Loading