Skip to content
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

Detect empty leading where clauses on type aliases #121326

Merged
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
32 changes: 22 additions & 10 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,9 +403,10 @@ impl Default for Generics {
/// A where-clause in a definition.
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct WhereClause {
/// `true` if we ate a `where` token: this can happen
/// if we parsed no predicates (e.g. `struct Foo where {}`).
/// This allows us to pretty-print accurately.
/// `true` if we ate a `where` token.
///
/// This can happen if we parsed no predicates, e.g., `struct Foo where {}`.
/// This allows us to pretty-print accurately and provide correct suggestion diagnostics.
pub has_where_token: bool,
pub predicates: ThinVec<WherePredicate>,
pub span: Span,
Expand Down Expand Up @@ -3007,18 +3008,29 @@ pub struct Trait {
///
/// If there is no where clause, then this is `false` with `DUMMY_SP`.
#[derive(Copy, Clone, Encodable, Decodable, Debug, Default)]
pub struct TyAliasWhereClause(pub bool, pub Span);
pub struct TyAliasWhereClause {
pub has_where_token: bool,
pub span: Span,
}

/// The span information for the two where clauses on a `TyAlias`.
#[derive(Copy, Clone, Encodable, Decodable, Debug, Default)]
pub struct TyAliasWhereClauses {
/// Before the equals sign.
pub before: TyAliasWhereClause,
/// After the equals sign.
pub after: TyAliasWhereClause,
/// The index in `TyAlias.generics.where_clause.predicates` that would split
/// into predicates from the where clause before the equals sign and the ones
/// from the where clause after the equals sign.
pub split: usize,
}

#[derive(Clone, Encodable, Decodable, Debug)]
pub struct TyAlias {
pub defaultness: Defaultness,
pub generics: Generics,
/// The span information for the two where clauses (before equals, after equals)
pub where_clauses: (TyAliasWhereClause, TyAliasWhereClause),
/// The index in `generics.where_clause.predicates` that would split into
/// predicates from the where clause before the equals and the predicates
/// from the where clause after the equals
pub where_predicates_split: usize,
pub where_clauses: TyAliasWhereClauses,
pub bounds: GenericBounds,
pub ty: Option<P<Ty>>,
}
Expand Down
12 changes: 6 additions & 6 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1079,8 +1079,8 @@ pub fn noop_visit_item_kind<T: MutVisitor>(kind: &mut ItemKind, vis: &mut T) {
}) => {
visit_defaultness(defaultness, vis);
vis.visit_generics(generics);
vis.visit_span(&mut where_clauses.0.1);
vis.visit_span(&mut where_clauses.1.1);
vis.visit_span(&mut where_clauses.before.span);
vis.visit_span(&mut where_clauses.after.span);
visit_bounds(bounds, vis);
visit_opt(ty, |ty| vis.visit_ty(ty));
}
Expand Down Expand Up @@ -1163,8 +1163,8 @@ pub fn noop_flat_map_assoc_item<T: MutVisitor>(
}) => {
visit_defaultness(defaultness, visitor);
visitor.visit_generics(generics);
visitor.visit_span(&mut where_clauses.0.1);
visitor.visit_span(&mut where_clauses.1.1);
visitor.visit_span(&mut where_clauses.before.span);
visitor.visit_span(&mut where_clauses.after.span);
visit_bounds(bounds, visitor);
visit_opt(ty, |ty| visitor.visit_ty(ty));
}
Expand Down Expand Up @@ -1257,8 +1257,8 @@ pub fn noop_flat_map_foreign_item<T: MutVisitor>(
}) => {
visit_defaultness(defaultness, visitor);
visitor.visit_generics(generics);
visitor.visit_span(&mut where_clauses.0.1);
visitor.visit_span(&mut where_clauses.1.1);
visitor.visit_span(&mut where_clauses.before.span);
visitor.visit_span(&mut where_clauses.after.span);
visit_bounds(bounds, visitor);
visit_opt(ty, |ty| visitor.visit_ty(ty));
}
Expand Down
19 changes: 10 additions & 9 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,20 @@ pub(super) struct ItemLowerer<'a, 'hir> {
/// clause if it exists.
fn add_ty_alias_where_clause(
generics: &mut ast::Generics,
mut where_clauses: (TyAliasWhereClause, TyAliasWhereClause),
mut where_clauses: TyAliasWhereClauses,
prefer_first: bool,
) {
if !prefer_first {
where_clauses = (where_clauses.1, where_clauses.0);
}
if where_clauses.0.0 || !where_clauses.1.0 {
generics.where_clause.has_where_token = where_clauses.0.0;
generics.where_clause.span = where_clauses.0.1;
} else {
generics.where_clause.has_where_token = where_clauses.1.0;
generics.where_clause.span = where_clauses.1.1;
(where_clauses.before, where_clauses.after) = (where_clauses.after, where_clauses.before);
}
let where_clause =
if where_clauses.before.has_where_token || !where_clauses.after.has_where_token {
where_clauses.before
} else {
where_clauses.after
};
generics.where_clause.has_where_token = where_clause.has_where_token;
generics.where_clause.span = where_clause.span;
}

impl<'a, 'hir> ItemLowerer<'a, 'hir> {
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,5 @@ ast_passes_where_clause_after_type_alias = where clauses are not allowed after t
ast_passes_where_clause_before_type_alias = where clauses are not allowed before the type for type aliases
.note = see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information
.suggestion = move it to the end of the type declaration
.remove_suggestion = remove this `where`
.move_suggestion = move it to the end of the type declaration
84 changes: 45 additions & 39 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,38 +138,42 @@ impl<'a> AstValidator<'a> {
&mut self,
ty_alias: &TyAlias,
) -> Result<(), errors::WhereClauseBeforeTypeAlias> {
let before_predicates =
ty_alias.generics.where_clause.predicates.split_at(ty_alias.where_predicates_split).0;

if ty_alias.ty.is_none() || before_predicates.is_empty() {
if ty_alias.ty.is_none() || !ty_alias.where_clauses.before.has_where_token {
return Ok(());
}

let mut state = State::new();
if !ty_alias.where_clauses.1.0 {
state.space();
state.word_space("where");
} else {
state.word_space(",");
}
let mut first = true;
for p in before_predicates {
if !first {
state.word_space(",");
let (before_predicates, after_predicates) =
ty_alias.generics.where_clause.predicates.split_at(ty_alias.where_clauses.split);
let span = ty_alias.where_clauses.before.span;

let sugg = if !before_predicates.is_empty() || !ty_alias.where_clauses.after.has_where_token
{
let mut state = State::new();

if !ty_alias.where_clauses.after.has_where_token {
state.space();
state.word_space("where");
}
first = false;
state.print_where_predicate(p);
}

let span = ty_alias.where_clauses.0.1;
Err(errors::WhereClauseBeforeTypeAlias {
span,
sugg: errors::WhereClauseBeforeTypeAliasSugg {
let mut first = after_predicates.is_empty();
for p in before_predicates {
if !first {
state.word_space(",");
}
first = false;
state.print_where_predicate(p);
}

errors::WhereClauseBeforeTypeAliasSugg::Move {
left: span,
snippet: state.s.eof(),
right: ty_alias.where_clauses.1.1.shrink_to_hi(),
},
})
right: ty_alias.where_clauses.after.span.shrink_to_hi(),
}
} else {
errors::WhereClauseBeforeTypeAliasSugg::Remove { span }
};

Err(errors::WhereClauseBeforeTypeAlias { span, sugg })
}

fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
Expand Down Expand Up @@ -457,8 +461,7 @@ impl<'a> AstValidator<'a> {
fn check_foreign_ty_genericless(
&self,
generics: &Generics,
before_where_clause: &TyAliasWhereClause,
after_where_clause: &TyAliasWhereClause,
where_clauses: &TyAliasWhereClauses,
) {
let cannot_have = |span, descr, remove_descr| {
self.dcx().emit_err(errors::ExternTypesCannotHave {
Expand All @@ -473,14 +476,14 @@ impl<'a> AstValidator<'a> {
cannot_have(generics.span, "generic parameters", "generic parameters");
}

let check_where_clause = |where_clause: &TyAliasWhereClause| {
if let TyAliasWhereClause(true, where_clause_span) = where_clause {
cannot_have(*where_clause_span, "`where` clauses", "`where` clause");
let check_where_clause = |where_clause: TyAliasWhereClause| {
if where_clause.has_where_token {
cannot_have(where_clause.span, "`where` clauses", "`where` clause");
}
};

check_where_clause(before_where_clause);
check_where_clause(after_where_clause);
check_where_clause(where_clauses.before);
check_where_clause(where_clauses.after);
}

fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body: Option<Span>) {
Expand Down Expand Up @@ -1122,9 +1125,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
if let Err(err) = self.check_type_alias_where_clause_location(ty_alias) {
self.dcx().emit_err(err);
}
} else if where_clauses.1.0 {
} else if where_clauses.after.has_where_token {
self.dcx().emit_err(errors::WhereClauseAfterTypeAlias {
span: where_clauses.1.1,
span: where_clauses.after.span,
help: self.session.is_nightly_build().then_some(()),
});
}
Expand Down Expand Up @@ -1154,7 +1157,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_defaultness(fi.span, *defaultness);
self.check_foreign_kind_bodyless(fi.ident, "type", ty.as_ref().map(|b| b.span));
self.check_type_no_bounds(bounds, "`extern` blocks");
self.check_foreign_ty_genericless(generics, &where_clauses.0, &where_clauses.1);
self.check_foreign_ty_genericless(generics, where_clauses);
self.check_foreign_item_ascii_only(fi.ident);
}
ForeignItemKind::Static(_, _, body) => {
Expand Down Expand Up @@ -1477,15 +1480,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
if let AssocItemKind::Type(ty_alias) = &item.kind
&& let Err(err) = self.check_type_alias_where_clause_location(ty_alias)
{
let sugg = match err.sugg {
errors::WhereClauseBeforeTypeAliasSugg::Remove { .. } => None,
errors::WhereClauseBeforeTypeAliasSugg::Move { snippet, right, .. } => {
Some((right, snippet))
}
};
self.lint_buffer.buffer_lint_with_diagnostic(
DEPRECATED_WHERE_CLAUSE_LOCATION,
item.id,
err.span,
fluent::ast_passes_deprecated_where_clause_location,
BuiltinLintDiagnostics::DeprecatedWhereclauseLocation(
err.sugg.right,
err.sugg.snippet,
),
BuiltinLintDiagnostics::DeprecatedWhereclauseLocation(sugg),
);
}

Expand Down
30 changes: 19 additions & 11 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,17 +515,25 @@ pub struct WhereClauseBeforeTypeAlias {
}

#[derive(Subdiagnostic)]
#[multipart_suggestion(
ast_passes_suggestion,
applicability = "machine-applicable",
style = "verbose"
)]
pub struct WhereClauseBeforeTypeAliasSugg {
#[suggestion_part(code = "")]
pub left: Span,
pub snippet: String,
#[suggestion_part(code = "{snippet}")]
pub right: Span,

pub enum WhereClauseBeforeTypeAliasSugg {
Copy link
Member Author

@fmease fmease Feb 20, 2024

Choose a reason for hiding this comment

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

The split into Remove & Move was not only done to provide a more accurate diagnostic message for the edge case “two where clauses, a leading and a trailing one, where the leading one is empty” but also to prevent the debug assertion empty span and no suggestions from firing.

#[suggestion(ast_passes_remove_suggestion, applicability = "machine-applicable", code = "")]
Remove {
#[primary_span]
span: Span,
},
#[multipart_suggestion(
ast_passes_move_suggestion,
applicability = "machine-applicable",
style = "verbose"
)]
Move {
#[suggestion_part(code = "")]
left: Span,
snippet: String,
#[suggestion_part(code = "{snippet}")]
right: Span,
},
}

#[derive(Diagnostic)]
Expand Down
15 changes: 4 additions & 11 deletions compiler/rustc_ast_pretty/src/pprust/state/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,13 @@ impl<'a> State<'a> {
defaultness,
generics,
where_clauses,
where_predicates_split,
bounds,
ty,
}) => {
self.print_associated_type(
ident,
generics,
*where_clauses,
*where_predicates_split,
bounds,
ty.as_deref(),
vis,
Expand Down Expand Up @@ -108,15 +106,14 @@ impl<'a> State<'a> {
&mut self,
ident: Ident,
generics: &ast::Generics,
where_clauses: (ast::TyAliasWhereClause, ast::TyAliasWhereClause),
where_predicates_split: usize,
where_clauses: ast::TyAliasWhereClauses,
bounds: &ast::GenericBounds,
ty: Option<&ast::Ty>,
vis: &ast::Visibility,
defaultness: ast::Defaultness,
) {
let (before_predicates, after_predicates) =
generics.where_clause.predicates.split_at(where_predicates_split);
generics.where_clause.predicates.split_at(where_clauses.split);
self.head("");
self.print_visibility(vis);
self.print_defaultness(defaultness);
Expand All @@ -127,13 +124,13 @@ impl<'a> State<'a> {
self.word_nbsp(":");
self.print_type_bounds(bounds);
}
self.print_where_clause_parts(where_clauses.0.0, before_predicates);
self.print_where_clause_parts(where_clauses.before.has_where_token, before_predicates);
if let Some(ty) = ty {
self.space();
self.word_space("=");
self.print_type(ty);
}
self.print_where_clause_parts(where_clauses.1.0, after_predicates);
self.print_where_clause_parts(where_clauses.after.has_where_token, after_predicates);
self.word(";");
self.end(); // end inner head-block
self.end(); // end outer head-block
Expand Down Expand Up @@ -249,15 +246,13 @@ impl<'a> State<'a> {
defaultness,
generics,
where_clauses,
where_predicates_split,
bounds,
ty,
}) => {
self.print_associated_type(
item.ident,
generics,
*where_clauses,
*where_predicates_split,
bounds,
ty.as_deref(),
&item.vis,
Expand Down Expand Up @@ -536,15 +531,13 @@ impl<'a> State<'a> {
defaultness,
generics,
where_clauses,
where_predicates_split,
bounds,
ty,
}) => {
self.print_associated_type(
ident,
generics,
*where_clauses,
*where_predicates_split,
bounds,
ty.as_deref(),
vis,
Expand Down
6 changes: 1 addition & 5 deletions compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -601,11 +601,7 @@ impl<'a> TraitDef<'a> {
kind: ast::AssocItemKind::Type(Box::new(ast::TyAlias {
defaultness: ast::Defaultness::Final,
generics: Generics::default(),
where_clauses: (
ast::TyAliasWhereClause::default(),
ast::TyAliasWhereClause::default(),
),
where_predicates_split: 0,
where_clauses: ast::TyAliasWhereClauses::default(),
bounds: Vec::new(),
ty: Some(type_def.to_ty(cx, self.span, type_ident, generics)),
})),
Expand Down
Loading
Loading