Skip to content

Add checks for attributes in types #144195

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
11 changes: 11 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,17 @@ parse_attr_without_generics = attribute without generic parameters
parse_attribute_on_param_type = attributes cannot be applied to a function parameter's type
.label = attributes are not allowed here

parse_attribute_on_type = attributes cannot be applied to types
.label = attributes are not allowed here
.suggestion = remove attribute from here

parse_attribute_on_generic_arg = attributes cannot be applied to generic arguments
.label = attributes are not allowed here
.suggestion = remove attribute from here

parse_attribute_on_empty_type = attributes cannot be applied here
.label = attributes are not allowed here

parse_bad_assoc_type_bounds = bounds on associated types do not belong here
.label = belongs in `where` clause

Expand Down
28 changes: 28 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1489,6 +1489,34 @@ pub(crate) struct AttributeOnParamType {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(parse_attribute_on_type)]
pub(crate) struct AttributeOnType {
#[primary_span]
#[label]
pub span: Span,
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub fix_span: Span,
}

#[derive(Diagnostic)]
#[diag(parse_attribute_on_generic_arg)]
pub(crate) struct AttributeOnGenericArg {
#[primary_span]
#[label]
pub span: Span,
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub fix_span: Span,
}

#[derive(Diagnostic)]
#[diag(parse_attribute_on_empty_type)]
pub(crate) struct AttributeOnEmptyType {
#[primary_span]
#[label]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(parse_pattern_method_param_without_body, code = E0642)]
pub(crate) struct PatternMethodParamWithoutBody {
Expand Down
31 changes: 28 additions & 3 deletions compiler/rustc_parse/src/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{Parser, Restrictions, TokenType};
use crate::ast::{PatKind, TyKind};
use crate::errors::{
self, FnPathFoundNamedParams, PathFoundAttributeInParams, PathFoundCVariadicParams,
PathSingleColon, PathTripleColon,
self, AttributeOnEmptyType, AttributeOnGenericArg, FnPathFoundNamedParams,
PathFoundAttributeInParams, PathFoundCVariadicParams, PathSingleColon, PathTripleColon,
};
use crate::exp;
use crate::parser::{CommaRecoveryMode, RecoverColon, RecoverComma};
use crate::parser::{CommaRecoveryMode, ExprKind, RecoverColon, RecoverComma};

/// Specifies how to parse a path.
#[derive(Copy, Clone, PartialEq)]
Expand Down Expand Up @@ -880,6 +880,12 @@ impl<'a> Parser<'a> {
&mut self,
ty_generics: Option<&Generics>,
) -> PResult<'a, Option<GenericArg>> {
let mut attr_span: Option<Span> = None;
if self.token == token::Pound && self.look_ahead(1, |t| *t == token::OpenBracket) {
let attrs_wrapper = self.parse_outer_attributes()?;
let raw_attrs = attrs_wrapper.take_for_recovery(self.psess);
attr_span = Some(raw_attrs[0].span.to(raw_attrs.last().unwrap().span));
}
let start = self.token.span;
let arg = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
// Parse lifetime argument.
Expand Down Expand Up @@ -934,6 +940,9 @@ impl<'a> Parser<'a> {
}
} else if self.token.is_keyword(kw::Const) {
return self.recover_const_param_declaration(ty_generics);
} else if let Some(attr_span) = attr_span {
let diag = self.dcx().create_err(AttributeOnEmptyType { span: attr_span });
return Err(diag);
} else {
// Fall back by trying to parse a const-expr expression. If we successfully do so,
// then we should report an error that it needs to be wrapped in braces.
Copy link
Member

@fmease fmease Aug 4, 2025

Choose a reason for hiding this comment

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

Two lines below, there's a preexisting let attrs = self.parse_outer_attributes()?;. We should be able to drop it and pass the already parsed attrs to the parse_expr_res.

It's not a blocking concern tho ig and it would stop us from recovering from /// and /** */ on gen args. Can be done in a follow-up.

Expand All @@ -953,6 +962,22 @@ impl<'a> Parser<'a> {
}
}
};

if let Some(attr_span) = attr_span {
let guar = self.dcx().emit_err(AttributeOnGenericArg {
span: attr_span,
fix_span: attr_span.until(arg.span()),
});
return Ok(Some(match arg {
GenericArg::Type(_) => GenericArg::Type(self.mk_ty(attr_span, TyKind::Err(guar))),
GenericArg::Const(_) => {
let error_expr = self.mk_expr(attr_span, ExprKind::Err(guar));
GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value: error_expr })
}
GenericArg::Lifetime(lt) => GenericArg::Lifetime(lt),
}));
}

Ok(Some(arg))
}

Expand Down
28 changes: 24 additions & 4 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ use thin_vec::{ThinVec, thin_vec};

use super::{Parser, PathStyle, SeqSep, TokenType, Trailing};
use crate::errors::{
self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType,
FnPtrWithGenerics, FnPtrWithGenericsSugg, HelpUseLatestEdition, InvalidDynKeyword,
LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType,
ReturnTypesUseThinArrow,
self, AttributeOnEmptyType, AttributeOnType, DynAfterMut, ExpectedFnPathFoundFnKeyword,
ExpectedMutOrConstInRawPointerType, FnPtrWithGenerics, FnPtrWithGenericsSugg,
HelpUseLatestEdition, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime,
NestedCVariadicType, ReturnTypesUseThinArrow,
};
use crate::parser::item::FrontMatterParsingMode;
use crate::{exp, maybe_recover_from_interpolated_ty_qpath};
Expand Down Expand Up @@ -253,7 +253,27 @@ impl<'a> Parser<'a> {
) -> PResult<'a, P<Ty>> {
let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes;
maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery);
if self.token == token::Pound && self.look_ahead(1, |t| *t == token::OpenBracket) {
let attrs_wrapper = self.parse_outer_attributes()?;
let raw_attrs = attrs_wrapper.take_for_recovery(self.psess);
let attr_span = raw_attrs[0].span.to(raw_attrs.last().unwrap().span);
let (full_span, guar) = match self.parse_ty() {
Ok(ty) => {
let full_span = attr_span.until(ty.span);
let guar = self
.dcx()
.emit_err(AttributeOnType { span: attr_span, fix_span: full_span });
(attr_span, guar)
}
Err(err) => {
err.cancel();
let guar = self.dcx().emit_err(AttributeOnEmptyType { span: attr_span });
(attr_span, guar)
}
};

return Ok(self.mk_ty(full_span, TyKind::Err(guar)));
}
if let Some(ty) = self.eat_metavar_seq_with_matcher(
|mv_kind| matches!(mv_kind, MetaVarKind::Ty { .. }),
|this| this.parse_ty_no_question_mark_recover(),
Expand Down
29 changes: 29 additions & 0 deletions tests/ui/parser/attribute-on-empty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//! Regression test for: <https://github.com/rust-lang/rust/issues/144132>
//! <https://github.com/rust-lang/rust/issues/135017>

struct Baz<const N: usize>(i32);

fn main() {
let _: Baz<#[cfg(any())]> = todo!();
//~^ ERROR attributes cannot be applied here
}

fn f(_param: #[attr]) {}
//~^ ERROR attributes cannot be applied to a function parameter's type
//~| ERROR expected type, found `)`

fn g() -> #[attr] { 0 }
//~^ ERROR attributes cannot be applied here

struct S {
field: #[attr],
//~^ ERROR attributes cannot be applied here
field1: (#[attr], i32),
//~^ ERROR attributes cannot be applied here
}

type Tuple = (#[attr], String);
//~^ ERROR attributes cannot be applied here

impl #[attr] {}
//~^ ERROR attributes cannot be applied here
52 changes: 52 additions & 0 deletions tests/ui/parser/attribute-on-empty.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
error: attributes cannot be applied here
--> $DIR/attribute-on-empty.rs:7:16
|
LL | let _: Baz<#[cfg(any())]> = todo!();
| - ^^^^^^^^^^^^^ attributes are not allowed here
| |
| while parsing the type for `_`

error: attributes cannot be applied to a function parameter's type
--> $DIR/attribute-on-empty.rs:11:14
|
LL | fn f(_param: #[attr]) {}
| ^^^^^^^ attributes are not allowed here

error: expected type, found `)`
--> $DIR/attribute-on-empty.rs:11:21
|
LL | fn f(_param: #[attr]) {}
| ^ expected type

error: attributes cannot be applied here
--> $DIR/attribute-on-empty.rs:15:11
|
LL | fn g() -> #[attr] { 0 }
| ^^^^^^^ attributes are not allowed here

error: attributes cannot be applied here
--> $DIR/attribute-on-empty.rs:19:12
|
LL | field: #[attr],
| ^^^^^^^ attributes are not allowed here

error: attributes cannot be applied here
--> $DIR/attribute-on-empty.rs:21:14
|
LL | field1: (#[attr], i32),
| ^^^^^^^ attributes are not allowed here

error: attributes cannot be applied here
--> $DIR/attribute-on-empty.rs:25:15
|
LL | type Tuple = (#[attr], String);
| ^^^^^^^ attributes are not allowed here

error: attributes cannot be applied here
--> $DIR/attribute-on-empty.rs:28:6
|
LL | impl #[attr] {}
| ^^^^^^^ attributes are not allowed here

error: aborting due to 8 previous errors

58 changes: 58 additions & 0 deletions tests/ui/parser/attribute-on-type.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//! Regression test for: <https://github.com/rust-lang/rust/issues/144132>
//! <https://github.com/rust-lang/rust/issues/135017>

//@ run-rustfix

#![allow(dead_code, unused_variables)]

struct Foo<T>(T);
struct Bar<'a>(&'a i32);
struct Baz<const N: usize>(i32);

fn main() {
let foo: Foo<i32> = Foo(2i32);
//~^ ERROR attributes cannot be applied to generic arguments

let _: &'static str = "123";
//~^ ERROR attributes cannot be applied to types

let _: Bar<'static> = Bar(&123);
//~^ ERROR attributes cannot be applied to generic arguments

let _: Baz<42> = Baz(42);
//~^ ERROR attributes cannot be applied to generic arguments

let _: Foo<String> = Foo(String::new());
//~^ ERROR attributes cannot be applied to generic arguments

let _: Bar<'static> = Bar(&456);
//~^ ERROR attributes cannot be applied to generic arguments

let _generic: Box<i32> = Box::new(1);
//~^ ERROR attributes cannot be applied to generic arguments

let _assignment: i32 = *Box::new(1);
//~^ ERROR attributes cannot be applied to types

let _complex: Vec<String> = vec![];
//~^ ERROR attributes cannot be applied to generic arguments

let _nested: Box<Vec<u64>> = Box::new(vec![]);
//~^ ERROR attributes cannot be applied to generic arguments
}

fn g() -> i32 { 0 }
//~^ ERROR attributes cannot be applied to types

struct S {
field: i32,
//~^ ERROR attributes cannot be applied to types
field1: (i32, i32),
//~^ ERROR attributes cannot be applied to types
}

type Tuple = (i32, String);
//~^ ERROR attributes cannot be applied to types

impl S {}
//~^ ERROR attributes cannot be applied to types
58 changes: 58 additions & 0 deletions tests/ui/parser/attribute-on-type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//! Regression test for: <https://github.com/rust-lang/rust/issues/144132>
//! <https://github.com/rust-lang/rust/issues/135017>

//@ run-rustfix

#![allow(dead_code, unused_variables)]

struct Foo<T>(T);
struct Bar<'a>(&'a i32);
struct Baz<const N: usize>(i32);

fn main() {
let foo: Foo<#[cfg(not(wrong))] i32> = Foo(2i32);
//~^ ERROR attributes cannot be applied to generic arguments

let _: #[attr] &'static str = "123";
//~^ ERROR attributes cannot be applied to types

let _: Bar<#[cfg(any())] 'static> = Bar(&123);
//~^ ERROR attributes cannot be applied to generic arguments

let _: Baz<#[cfg(any())] 42> = Baz(42);
//~^ ERROR attributes cannot be applied to generic arguments

let _: Foo<#[cfg(not(wrong))]String> = Foo(String::new());
//~^ ERROR attributes cannot be applied to generic arguments

let _: Bar<#[cfg(any())] 'static> = Bar(&456);
//~^ ERROR attributes cannot be applied to generic arguments

let _generic: Box<#[attr] i32> = Box::new(1);
//~^ ERROR attributes cannot be applied to generic arguments

let _assignment: #[attr] i32 = *Box::new(1);
//~^ ERROR attributes cannot be applied to types

let _complex: Vec<#[derive(Debug)] String> = vec![];
//~^ ERROR attributes cannot be applied to generic arguments

let _nested: Box<Vec<#[cfg(feature = "test")] u64>> = Box::new(vec![]);
//~^ ERROR attributes cannot be applied to generic arguments
}

fn g() -> #[attr] i32 { 0 }
//~^ ERROR attributes cannot be applied to types

struct S {
field: #[attr] i32,
//~^ ERROR attributes cannot be applied to types
field1: (#[attr] i32, i32),
//~^ ERROR attributes cannot be applied to types
}

type Tuple = (#[attr] i32, String);
//~^ ERROR attributes cannot be applied to types

impl #[attr] S {}
//~^ ERROR attributes cannot be applied to types
Loading
Loading