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
100 changes: 83 additions & 17 deletions compiler/rustc_attr_parsing/src/attributes/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,42 @@ fn check_attr_crate_level<S: Stage>(cx: &mut AcceptContext<'_, '_, S>, span: Spa
true
}

// FIXME: To be removed once merged and replace with `cx.expected_name_value(span, _name)`.
fn expected_name_value<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
span: Span,
_name: Option<Symbol>,
) {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::ExpectedNameValue,
span,
);
}

// FIXME: remove this method once merged and use `cx.expected_no_args(span)` instead.
fn expected_no_args<S: Stage>(cx: &mut AcceptContext<'_, '_, S>, span: Span) {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::ExpectedNoArgs,
span,
);
}

// FIXME: remove this method once merged and use `cx.expected_no_args(span)` instead.
// cx.expected_string_literal(span, _actual_literal);
fn expected_string_literal<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
span: Span,
_actual_literal: Option<&MetaItemLit>,
) {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::MalformedDoc,
span,
);
}

fn parse_keyword_and_attribute<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
path: &OwnedPathParser,
Expand All @@ -78,12 +114,12 @@ fn parse_keyword_and_attribute<S: Stage>(
attr_name: Symbol,
) {
let Some(nv) = args.name_value() else {
cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym());
expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym());
return;
};

let Some(value) = nv.value_as_str() else {
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit()));
return;
};

Expand Down Expand Up @@ -127,12 +163,21 @@ impl DocParser {
match path.word_sym() {
Some(sym::no_crate_inject) => {
if let Err(span) = args.no_args() {
cx.expected_no_args(span);
expected_no_args(cx, span);
return;
}

if self.attribute.no_crate_inject.is_some() {
cx.duplicate_key(path.span(), sym::no_crate_inject);
if let Some(used_span) = self.attribute.no_crate_inject {
let unused_span = path.span();
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::UnusedDuplicate {
this: unused_span,
other: used_span,
warning: true,
},
unused_span,
);
return;
}

Expand All @@ -144,7 +189,14 @@ impl DocParser {
}
Some(sym::attr) => {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span, args);
// FIXME: remove this method once merged and uncomment the line below instead.
// cx.expected_list(cx.attr_span, args);
let span = cx.attr_span;
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::MalformedDoc,
span,
);
return;
};

Expand Down Expand Up @@ -246,7 +298,7 @@ impl DocParser {
inline: DocInline,
) {
if let Err(span) = args.no_args() {
cx.expected_no_args(span);
expected_no_args(cx, span);
return;
}

Expand Down Expand Up @@ -328,7 +380,14 @@ impl DocParser {
match sub_item.args() {
a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => {
let Some(name) = sub_item.path().word_sym() else {
cx.expected_identifier(sub_item.path().span());
// FIXME: remove this method once merged and uncomment the line
// below instead.
// cx.expected_identifier(sub_item.path().span());
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::MalformedDoc,
sub_item.path().span(),
);
continue;
};
if let Ok(CfgEntry::NameValue { name, value, .. }) =
Expand Down Expand Up @@ -391,7 +450,7 @@ impl DocParser {
macro_rules! no_args {
($ident: ident) => {{
if let Err(span) = args.no_args() {
cx.expected_no_args(span);
expected_no_args(cx, span);
return;
}

Expand All @@ -410,7 +469,7 @@ impl DocParser {
macro_rules! no_args_and_not_crate_level {
($ident: ident) => {{
if let Err(span) = args.no_args() {
cx.expected_no_args(span);
expected_no_args(cx, span);
return;
}
let span = path.span();
Expand All @@ -423,7 +482,7 @@ impl DocParser {
macro_rules! no_args_and_crate_level {
($ident: ident) => {{
if let Err(span) = args.no_args() {
cx.expected_no_args(span);
expected_no_args(cx, span);
return;
}
let span = path.span();
Expand All @@ -436,12 +495,12 @@ impl DocParser {
macro_rules! string_arg_and_crate_level {
($ident: ident) => {{
let Some(nv) = args.name_value() else {
cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym());
expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym());
return;
};

let Some(s) = nv.value_as_str() else {
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit()));
return;
};

Expand Down Expand Up @@ -512,7 +571,14 @@ impl DocParser {
self.parse_single_test_doc_attr_item(cx, mip);
}
MetaItemOrLitParser::Lit(lit) => {
cx.unexpected_literal(lit.span);
// FIXME: remove this method once merged and uncomment the line
// below instead.
// cx.unexpected_literal(lit.span);
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::MalformedDoc,
lit.span,
);
}
}
}
Expand Down Expand Up @@ -582,7 +648,7 @@ impl DocParser {
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(
rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT,
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::IllFormedAttributeInput { suggestions, docs: None },
span,
);
Expand All @@ -595,14 +661,14 @@ impl DocParser {
self.parse_single_doc_attr_item(cx, mip);
}
MetaItemOrLitParser::Lit(lit) => {
cx.expected_name_value(lit.span, None);
expected_name_value(cx, lit.span, None);
}
}
}
}
ArgParser::NameValue(nv) => {
if nv.value_as_str().is_none() {
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit()));
} else {
unreachable!(
"Should have been handled at the same time as sugar-syntaxed doc comments"
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,14 @@ lint_expectation = this lint expectation is unfulfilled
.note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message
.rationale = {$rationale}

lint_expected_name_value =
expected this to be of the form `... = "..."`
.warn = {-lint_previously_accepted}

lint_expected_no_args =
didn't expect any arguments here
.warn = {-lint_previously_accepted}

lint_for_loops_over_fallibles =
for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement
.suggestion = consider using `if let` to clear intent
Expand Down Expand Up @@ -558,6 +566,10 @@ lint_macro_expr_fragment_specifier_2024_migration =

lint_malformed_attribute = malformed lint attribute input

lint_malformed_doc =
malformed `doc` attribute input
.warn = {-lint_previously_accepted}

lint_map_unit_fn = `Iterator::map` call that discard the iterator's values
.note = `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated
.function_label = this function returns `()`, which is likely not what you wanted
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_lint/src/early/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,5 +428,11 @@ pub fn decorate_attribute_lint(
sugg: suggested.map(|s| lints::UnknownCrateTypesSuggestion { span, snippet: s }),
}
.decorate_lint(diag),

&AttributeLintKind::MalformedDoc => lints::MalformedDoc.decorate_lint(diag),

&AttributeLintKind::ExpectedNoArgs => lints::ExpectedNoArgs.decorate_lint(diag),

&AttributeLintKind::ExpectedNameValue => lints::ExpectedNameValue.decorate_lint(diag),
}
}
15 changes: 15 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3185,6 +3185,21 @@ pub(crate) struct UnusedDuplicate {
pub warning: bool,
}

#[derive(LintDiagnostic)]
#[diag(lint_malformed_doc)]
#[warning]
pub(crate) struct MalformedDoc;

#[derive(LintDiagnostic)]
#[diag(lint_expected_no_args)]
#[warning]
pub(crate) struct ExpectedNoArgs;

#[derive(LintDiagnostic)]
#[diag(lint_expected_name_value)]
#[warning]
pub(crate) struct ExpectedNameValue;

#[derive(LintDiagnostic)]
#[diag(lint_unsafe_attr_outside_unsafe)]
pub(crate) struct UnsafeAttrOutsideUnsafeLint {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3458,7 +3458,7 @@ declare_lint! {
/// but this lint was introduced to avoid breaking any existing
/// crates which included them.
pub INVALID_DOC_ATTRIBUTES,
Deny,
Warn,
"detects invalid `#[doc(...)]` attributes",
}

Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_lint_defs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,9 @@ pub enum AttributeLintKind {
span: Span,
suggested: Option<Symbol>,
},
MalformedDoc,
ExpectedNoArgs,
ExpectedNameValue,
}

pub type RegisteredTools = FxIndexSet<Ident>;
Expand Down
37 changes: 19 additions & 18 deletions tests/rustdoc-ui/bad-render-options.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
// regression test for https://github.com/rust-lang/rust/issues/149187
#![deny(invalid_doc_attributes)]

#![doc(html_favicon_url)]
//~^ ERROR: malformed `doc` attribute
//~| NOTE expected this to be of the form `html_favicon_url = "..."`
//~^ ERROR
//~| WARN
#![doc(html_logo_url)]
//~^ ERROR: malformed `doc` attribute
//~| NOTE expected this to be of the form `html_logo_url = "..."`
//~^ ERROR
//~| WARN
#![doc(html_playground_url)]
//~^ ERROR: malformed `doc` attribute
//~| NOTE expected this to be of the form `html_playground_url = "..."`
//~^ ERROR
//~| WARN
#![doc(issue_tracker_base_url)]
//~^ ERROR: malformed `doc` attribute
//~| NOTE expected this to be of the form `issue_tracker_base_url = "..."`
//~^ ERROR
//~| WARN
#![doc(html_favicon_url = 1)]
//~^ ERROR malformed `doc` attribute
//~| NOTE expected a string literal
//~^ ERROR
//~| WARN
#![doc(html_logo_url = 2)]
//~^ ERROR malformed `doc` attribute
//~| NOTE expected a string literal
//~^ ERROR
//~| WARN
#![doc(html_playground_url = 3)]
//~^ ERROR malformed `doc` attribute
//~| NOTE expected a string literal
//~^ ERROR
//~| WARN
#![doc(issue_tracker_base_url = 4)]
//~^ ERROR malformed `doc` attribute
//~| NOTE expected a string literal
//~^ ERROR
//~| WARN
#![doc(html_no_source = "asdf")]
//~^ ERROR malformed `doc` attribute
//~| NOTE didn't expect any arguments here
//~^ ERROR
//~| WARN
Loading
Loading