Skip to content
Merged
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
21 changes: 10 additions & 11 deletions compiler/rustc_attr_parsing/src/attributes/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use thin_vec::ThinVec;
use super::prelude::{ALL_TARGETS, AllowedTargets};
use super::{AcceptMapping, AttributeParser};
use crate::context::{AcceptContext, FinalizeContext, Stage};
use crate::errors::{DocAliasDuplicated, DocAutoCfgExpectsHideOrShow, IllFormedAttributeInput};
use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser};
use crate::session_diagnostics::{
DocAliasBadChar, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, DocAttrNotCrateLevel,
Expand Down Expand Up @@ -255,9 +256,9 @@ impl DocParser {
}

if let Some(first_definition) = self.attribute.aliases.get(&alias).copied() {
cx.emit_lint(
cx.emit_dyn_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
AttributeLintKind::DuplicateDocAlias { first_definition },
move |dcx, level| DocAliasDuplicated { first_definition }.into_diag(dcx, level),
span,
);
}
Expand Down Expand Up @@ -343,9 +344,9 @@ impl DocParser {
ArgParser::List(list) => {
for meta in list.mixed() {
let MetaItemOrLitParser::MetaItemParser(item) = meta else {
cx.emit_lint(
cx.emit_dyn_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocAutoCfgExpectsHideOrShow,
|dcx, level| DocAutoCfgExpectsHideOrShow.into_diag(dcx, level),
meta.span(),
);
continue;
Expand All @@ -354,9 +355,9 @@ impl DocParser {
Some(sym::hide) => (HideOrShow::Hide, sym::hide),
Some(sym::show) => (HideOrShow::Show, sym::show),
_ => {
cx.emit_lint(
cx.emit_dyn_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocAutoCfgExpectsHideOrShow,
|dcx, level| DocAutoCfgExpectsHideOrShow.into_diag(dcx, level),
item.span(),
);
continue;
Expand Down Expand Up @@ -666,12 +667,10 @@ impl DocParser {
ArgParser::NoArgs => {
let suggestions = cx.adcx().suggestions();
let span = cx.attr_span;
cx.emit_lint(
cx.emit_dyn_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::IllFormedAttributeInput {
suggestions,
docs: None,
help: None,
move |dcx, level| {
IllFormedAttributeInput::new(&suggestions, None, None).into_diag(dcx, level)
},
span,
);
Expand Down
20 changes: 15 additions & 5 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -897,11 +897,18 @@ where
}

pub(crate) fn warn_empty_attribute(&mut self, span: Span) {
let attr_path = self.attr_path.clone().to_string();
let attr_path = self.attr_path.to_string();
let valid_without_list = self.template.word;
self.emit_lint(
self.emit_dyn_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
AttributeLintKind::EmptyAttribute { first_span: span, attr_path, valid_without_list },
move |dcx, level| {
crate::errors::EmptyAttributeList {
attr_span: span,
attr_path: &attr_path,
valid_without_list,
}
.into_diag(dcx, level)
},
span,
);
}
Expand All @@ -916,9 +923,12 @@ where
) {
let suggestions = self.suggestions();
let span = self.attr_span;
self.emit_lint(
self.emit_dyn_lint(
lint,
AttributeLintKind::IllFormedAttributeInput { suggestions, docs: None, help },
move |dcx, level| {
crate::errors::IllFormedAttributeInput::new(&suggestions, None, help.as_deref())
.into_diag(dcx, level)
},
span,
);
}
Expand Down
113 changes: 112 additions & 1 deletion compiler/rustc_attr_parsing/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_errors::MultiSpan;
use rustc_errors::{DiagArgValue, MultiSpan};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};

Expand Down Expand Up @@ -66,3 +66,114 @@ pub(crate) struct UnsafeAttrOutsideUnsafeLint {
#[subdiagnostic]
pub suggestion: Option<crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion>,
}

#[derive(Diagnostic)]
#[diag(
"{$num_suggestions ->
[1] attribute must be of the form {$suggestions}
*[other] valid forms for the attribute are {$suggestions}
}"
)]
pub(crate) struct IllFormedAttributeInput {
pub num_suggestions: usize,
pub suggestions: DiagArgValue,
#[note("for more information, visit <{$docs}>")]
pub has_docs: bool,
pub docs: &'static str,
#[subdiagnostic]
help: Option<IllFormedAttributeInputHelp>,
}

impl IllFormedAttributeInput {
pub(crate) fn new(
suggestions: &[String],
docs: Option<&'static str>,
help: Option<&str>,
) -> Self {
Self {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
),
has_docs: docs.is_some(),
docs: docs.unwrap_or(""),
help: help.map(|h| IllFormedAttributeInputHelp { lint: h.to_string() }),
}
}
}

#[derive(Subdiagnostic)]
#[help(
"if you meant to silence a warning, consider using #![allow({$lint})] or #![expect({$lint})]"
)]
struct IllFormedAttributeInputHelp {
pub lint: String,
}

#[derive(Diagnostic)]
#[diag("unused attribute")]
#[note(
"{$valid_without_list ->
[true] using `{$attr_path}` with an empty list is equivalent to not using a list at all
*[other] using `{$attr_path}` with an empty list has no effect
}"
)]
pub(crate) struct EmptyAttributeList<'a> {
#[suggestion(
"{$valid_without_list ->
[true] remove these parentheses
*[other] remove this attribute
}",
code = "",
applicability = "machine-applicable"
)]
pub attr_span: Span,
pub attr_path: &'a str,
pub valid_without_list: bool,
}

#[derive(Diagnostic)]
#[diag("`#[{$name}]` attribute cannot be used on {$target}")]
#[warning(
"this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!"
)]
#[help("`#[{$name}]` can {$only}be applied to {$applied}")]
pub(crate) struct InvalidTargetLint {
pub name: String,
pub target: &'static str,
pub applied: DiagArgValue,
pub only: &'static str,
#[suggestion(
"remove the attribute",
code = "",
applicability = "machine-applicable",
style = "tool-only"
)]
pub attr_span: Span,
}

#[derive(Diagnostic)]
#[diag(
"{$is_used_as_inner ->
[false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]`
*[other] the `#![{$name}]` attribute can only be used at the crate root
}"
)]
pub(crate) struct InvalidAttrStyle<'a> {
pub name: &'a str,
pub is_used_as_inner: bool,
#[note("this attribute does not have an `!`, which means it is applied to this {$target}")]
pub target_span: Option<Span>,
pub target: &'static str,
}

#[derive(Diagnostic)]
#[diag("doc alias is duplicated")]
pub(crate) struct DocAliasDuplicated {
#[label("first defined here")]
pub first_definition: Span,
}

#[derive(Diagnostic)]
#[diag("only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]`")]
pub(crate) struct DocAutoCfgExpectsHideOrShow;
48 changes: 31 additions & 17 deletions compiler/rustc_attr_parsing/src/target_checking.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
use std::borrow::Cow;

use rustc_ast::AttrStyle;
use rustc_errors::{DiagArgValue, MultiSpan, StashKey};
use rustc_errors::{DiagArgValue, Diagnostic, MultiSpan, StashKey};
use rustc_feature::Features;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::AttributeLintKind;
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,
InvalidAttrAtCrateLevel, InvalidTargetLint, ItemFollowingInnerAttr,
UnsupportedAttributesInWhere,
};
use crate::session_diagnostics::InvalidTarget;
use crate::target_checking::Policy::Allow;
Expand Down Expand Up @@ -142,14 +142,19 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
};

let attr_span = cx.attr_span;
cx.emit_lint(
cx.emit_dyn_lint(
lint,
AttributeLintKind::InvalidTarget {
name: name.to_string(),
target: target.plural_name(),
only: if only { "only " } else { "" },
applied,
attr_span,
move |dcx, level| {
InvalidTargetLint {
name: name.to_string(),
target: target.plural_name(),
only: if only { "only " } else { "" },
applied: DiagArgValue::StrListSepByAnd(
applied.iter().map(|i| Cow::Owned(i.to_string())).collect(),
),
attr_span,
}
.into_diag(dcx, level)
},
attr_span,
);
Expand All @@ -176,15 +181,24 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
return;
}

let kind = AttributeLintKind::InvalidStyle {
name: cx.attr_path.to_string(),
is_used_as_inner: cx.attr_style == AttrStyle::Inner,
target: target.name(),
target_span: cx.target_span,
};
let name = cx.attr_path.to_string();
let is_used_as_inner = cx.attr_style == AttrStyle::Inner;
let target_span = cx.target_span;
let attr_span = cx.attr_span;

cx.emit_lint(rustc_session::lint::builtin::UNUSED_ATTRIBUTES, kind, attr_span);
cx.emit_dyn_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
move |dcx, level| {
crate::errors::InvalidAttrStyle {
name: &name,
is_used_as_inner,
target_span: (!is_used_as_inner).then_some(target_span),
target: target.name(),
}
.into_diag(dcx, level)
},
attr_span,
);
}

// FIXME: Fix "Cannot determine resolution" error and remove built-in macros
Expand Down
13 changes: 6 additions & 7 deletions compiler/rustc_attr_parsing/src/validate_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::{
self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, Safety,
};
use rustc_errors::{Applicability, PResult};
use rustc_errors::{Applicability, Diagnostic, PResult};
use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, template};
use rustc_hir::AttrPath;
use rustc_hir::lints::AttributeLintKind;
use rustc_parse::parse_in;
use rustc_session::errors::report_lit_error;
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
Expand Down Expand Up @@ -210,14 +209,14 @@ pub fn emit_malformed_attribute(
suggestions.clear();
}
if should_warn(name) {
psess.buffer_lint(
let suggestions = suggestions.clone();
psess.dyn_buffer_lint(
ILL_FORMED_ATTRIBUTE_INPUT,
span,
ast::CRATE_NODE_ID,
AttributeLintKind::IllFormedAttributeInput {
suggestions: suggestions.clone(),
docs: template.docs,
help: None,
move |dcx, level| {
crate::errors::IllFormedAttributeInput::new(&suggestions, template.docs, None)
.into_diag(dcx, level)
},
);
} else {
Expand Down
Loading
Loading