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

Suggest changing type to const parameters if we encounter a type in the trait bound position #120570

Merged
merged 2 commits into from
Feb 4, 2024
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
3 changes: 3 additions & 0 deletions compiler/rustc_resolve/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ resolve_underscore_lifetime_name_cannot_be_used_here =
`'_` cannot be used here
.note = `'_` is a reserved lifetime name

resolve_unexpected_res_change_ty_to_const_param_sugg =
you might have meant to write a const parameter here

resolve_unreachable_label =
use of unreachable label `{$name}`
.label = unreachable label `{$name}`
Expand Down
15 changes: 14 additions & 1 deletion compiler/rustc_resolve/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_errors::codes::*;
use rustc_errors::{codes::*, Applicability};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{
symbol::{Ident, Symbol},
Expand Down Expand Up @@ -787,3 +787,16 @@ pub(crate) struct IsNotDirectlyImportable {
pub(crate) span: Span,
pub(crate) target: Ident,
}

#[derive(Subdiagnostic)]
#[suggestion(
resolve_unexpected_res_change_ty_to_const_param_sugg,
code = "const ",
style = "verbose"
)]
pub(crate) struct UnexpectedResChangeTyToConstParamSugg {
#[primary_span]
pub span: Span,
#[applicability]
pub applicability: Applicability,
}
12 changes: 6 additions & 6 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -594,9 +594,9 @@ struct DiagnosticMetadata<'ast> {
/// The current trait (used to suggest).
current_item: Option<&'ast Item>,

/// When processing generics and encountering a type not found, suggest introducing a type
/// param.
currently_processing_generics: bool,
/// When processing generic arguments and encountering an unresolved ident not found,
/// suggest introducing a type or const param depending on the context.
currently_processing_generic_args: bool,

/// The current enclosing (non-closure) function (used for better errors).
current_function: Option<(FnKind<'ast>, Span)>,
Expand Down Expand Up @@ -1069,7 +1069,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,

fn visit_generic_arg(&mut self, arg: &'ast GenericArg) {
debug!("visit_generic_arg({:?})", arg);
let prev = replace(&mut self.diagnostic_metadata.currently_processing_generics, true);
let prev = replace(&mut self.diagnostic_metadata.currently_processing_generic_args, true);
match arg {
GenericArg::Type(ref ty) => {
// We parse const arguments as path types as we cannot distinguish them during
Expand Down Expand Up @@ -1100,7 +1100,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
},
);

self.diagnostic_metadata.currently_processing_generics = prev;
self.diagnostic_metadata.currently_processing_generic_args = prev;
return;
}
}
Expand All @@ -1113,7 +1113,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
self.resolve_anon_const(ct, AnonConstKind::ConstArg(IsRepeatExpr::No))
}
}
self.diagnostic_metadata.currently_processing_generics = prev;
self.diagnostic_metadata.currently_processing_generic_args = prev;
}

fn visit_assoc_constraint(&mut self, constraint: &'ast AssocConstraint) {
Expand Down
66 changes: 59 additions & 7 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}

self.suggest_bare_struct_literal(&mut err);
self.suggest_changing_type_to_const_param(&mut err, res, source, span);

if self.suggest_pattern_match_with_let(&mut err, source, span) {
// Fallback label.
Expand All @@ -452,7 +453,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}

self.suggest_self_or_self_ref(&mut err, path, span);
self.detect_assoct_type_constraint_meant_as_path(&mut err, &base_error);
self.detect_assoc_type_constraint_meant_as_path(&mut err, &base_error);
if self.suggest_self_ty(&mut err, source, path, span)
|| self.suggest_self_value(&mut err, source, path, span)
{
Expand Down Expand Up @@ -491,7 +492,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
(err, candidates)
}

fn detect_assoct_type_constraint_meant_as_path(
fn detect_assoc_type_constraint_meant_as_path(
&self,
err: &mut Diagnostic,
base_error: &BaseError,
Expand Down Expand Up @@ -799,17 +800,19 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
false,
) = (source, res, is_macro)
{
if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object {
if let Some(bounds @ [first_bound, .., last_bound]) =
self.diagnostic_metadata.current_trait_object
{
fallback = true;
let spans: Vec<Span> = bounds
.iter()
.map(|bound| bound.span())
.filter(|&sp| sp != base_error.span)
.collect();

let start_span = bounds[0].span();
let start_span = first_bound.span();
// `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><)
let end_span = bounds.last().unwrap().span();
let end_span = last_bound.span();
// `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar)
let last_bound_span = spans.last().cloned().unwrap();
let mut multi_span: MultiSpan = spans.clone().into();
Expand Down Expand Up @@ -1136,6 +1139,55 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}
}

fn suggest_changing_type_to_const_param(
&mut self,
err: &mut Diagnostic,
res: Option<Res>,
source: PathSource<'_>,
span: Span,
) {
let PathSource::Trait(_) = source else { return };

// We don't include `DefKind::Str` and `DefKind::AssocTy` as they can't be reached here anyway.
let applicability = match res {
Some(Res::PrimTy(PrimTy::Int(_) | PrimTy::Uint(_) | PrimTy::Bool | PrimTy::Char)) => {
Applicability::MachineApplicable
}
// FIXME(const_generics): Add `DefKind::TyParam` and `SelfTyParam` once we support generic
// const generics. Of course, `Struct` and `Enum` may contain ty params, too, but the
// benefits of including them here outweighs the small number of false positives.
Some(Res::Def(DefKind::Struct | DefKind::Enum, _))
if self.r.tcx.features().adt_const_params =>
Copy link
Member

Choose a reason for hiding this comment

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

❤️

{
Applicability::MaybeIncorrect
}
_ => return,
};

let Some(item) = self.diagnostic_metadata.current_item else { return };
let Some(generics) = item.kind.generics() else { return };

let param = generics.params.iter().find_map(|param| {
// Only consider type params with exactly one trait bound.
if let [bound] = &*param.bounds
&& let ast::GenericBound::Trait(tref, ast::TraitBoundModifiers::NONE) = bound
&& tref.span == span
&& param.ident.span.eq_ctxt(span)
{
Some(param.ident.span)
} else {
None
}
});

if let Some(param) = param {
err.subdiagnostic(errors::UnexpectedResChangeTyToConstParamSugg {
span: param.shrink_to_lo(),
applicability,
});
}
}

fn suggest_pattern_match_with_let(
&mut self,
err: &mut Diagnostic,
Expand Down Expand Up @@ -2419,10 +2471,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
let mut iter = ident.chars().map(|c| c.is_uppercase());
let single_uppercase_char =
matches!(iter.next(), Some(true)) && matches!(iter.next(), None);
if !self.diagnostic_metadata.currently_processing_generics && !single_uppercase_char {
if !self.diagnostic_metadata.currently_processing_generic_args && !single_uppercase_char {
return None;
}
match (self.diagnostic_metadata.current_item, single_uppercase_char, self.diagnostic_metadata.currently_processing_generics) {
match (self.diagnostic_metadata.current_item, single_uppercase_char, self.diagnostic_metadata.currently_processing_generic_args) {
(Some(Item { kind: ItemKind::Fn(..), ident, .. }), _, _) if ident.name == sym::main => {
// Ignore `fn main()` as we don't want to suggest `fn main<T>()`
}
Expand Down
10 changes: 10 additions & 0 deletions tests/ui/resolve/change-ty-to-const-param-sugg-0.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
fn make<N: u32>() {}
//~^ ERROR expected trait, found builtin type `u32`
//~| HELP you might have meant to write a const parameter here

struct Array<N: usize>([bool; N]);
//~^ ERROR expected trait, found builtin type `usize`
//~| HELP you might have meant to write a const parameter here
//~| ERROR expected value, found type parameter `N`

fn main() {}
34 changes: 34 additions & 0 deletions tests/ui/resolve/change-ty-to-const-param-sugg-0.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
error[E0404]: expected trait, found builtin type `u32`
--> $DIR/change-ty-to-const-param-sugg-0.rs:1:12
|
LL | fn make<N: u32>() {}
| ^^^ not a trait
|
help: you might have meant to write a const parameter here
|
LL | fn make<const N: u32>() {}
| +++++

error[E0404]: expected trait, found builtin type `usize`
--> $DIR/change-ty-to-const-param-sugg-0.rs:5:17
|
LL | struct Array<N: usize>([bool; N]);
| ^^^^^ not a trait
|
help: you might have meant to write a const parameter here
|
LL | struct Array<const N: usize>([bool; N]);
| +++++

error[E0423]: expected value, found type parameter `N`
--> $DIR/change-ty-to-const-param-sugg-0.rs:5:31
|
LL | struct Array<N: usize>([bool; N]);
| - ^ not a value
| |
| found this type parameter

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0404, E0423.
For more information about an error, try `rustc --explain E0404`.
24 changes: 24 additions & 0 deletions tests/ui/resolve/change-ty-to-const-param-sugg-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![feature(adt_const_params)]
#![allow(incomplete_features)]

use std::marker::ConstParamTy;

struct Tagged<T: Tag, O: Options>;
//~^ ERROR expected trait, found enum `Tag`
//~| HELP you might have meant to write a const parameter here
//~| ERROR expected trait, found struct `Options`
//~| HELP you might have meant to write a const parameter here

#[derive(PartialEq, Eq, ConstParamTy)]
enum Tag {
One,
Two,
}

#[derive(PartialEq, Eq, ConstParamTy)]
struct Options {
verbose: bool,
safe: bool,
}

fn main() {}
25 changes: 25 additions & 0 deletions tests/ui/resolve/change-ty-to-const-param-sugg-1.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
error[E0404]: expected trait, found enum `Tag`
--> $DIR/change-ty-to-const-param-sugg-1.rs:6:18
|
LL | struct Tagged<T: Tag, O: Options>;
| ^^^ not a trait
|
help: you might have meant to write a const parameter here
|
LL | struct Tagged<const T: Tag, O: Options>;
| +++++

error[E0404]: expected trait, found struct `Options`
--> $DIR/change-ty-to-const-param-sugg-1.rs:6:26
|
LL | struct Tagged<T: Tag, O: Options>;
| ^^^^^^^ not a trait
|
help: you might have meant to write a const parameter here
|
LL | struct Tagged<T: Tag, const O: Options>;
| +++++

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0404`.
Loading