Skip to content

Conversation

Kivooeo
Copy link
Member

@Kivooeo Kivooeo commented Apr 20, 2025

I consider this solution a starting point and not final. I'm open to your ideas and suggestions for further improvements to the heuristic or implementation.

Summary

This PR improves the heuristic used by rustc to suggest type parameters for missing types, addressing issue #139999. The issue occurs when rustc mistakenly suggests adding a type parameter for a missing type (e.g., ForgotToImport) that is likely a concrete type rather than a generic. This can lead to confusing error messages, as shown in the example:

pub fn do_stuff() -> Option<ForgotToImport> {
    None
}

Current output suggests adding a type parameter:

help: you might be missing a type parameter
  |
1 | pub fn do_stuff<ForgotToImport>() -> Option<ForgotToImport> {
  |                ++++++++++++++++

This PR refines the heuristic to avoid such suggestions for names that are likely concrete types.

Changes

The fix modifies the logic in the type suggestion code to skip type parameter suggestions when the missing type name:

  1. Is not a common generic name (e.g., Item, Output, Error).
  2. Uses UpperCamelCase, which is typical for concrete types.
  3. Is longer than 3 characters, as type parameters are often short (e.g., T, U).

Example

With this change, the example code above will no longer suggest a type parameter for ForgotToImport, resulting in a cleaner error message:

error[E0412]: cannot find type `ForgotToImport` in this scope
 --> src/lib.rs:1:29
  |
1 | pub fn do_stuff() -> Option<ForgotToImport> {
  |                             ^^^^^^^^^^^^^^ not found in this scope

Rationale

This approach balances precision and simplicity:

  • UpperCamelCase is a strong indicator of concrete types in Rust, as type parameters are often single letters or specific generic names.
  • The length check (>3) reduces false positives for longer names, which are less common for generics.
  • Preserving suggestions for well-known generic names ensures backward compatibility and avoids breaking valid cases.

Related Issue

fixes #139999

cc @Wilfred (issue reporter)

@rustbot
Copy link
Collaborator

rustbot commented Apr 20, 2025

r? @oli-obk

rustbot has assigned @oli-obk.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 20, 2025
@Kivooeo Kivooeo force-pushed the fix-139999-type-suggestion branch from 95b39e7 to 037c08e Compare April 20, 2025 11:19
@rust-log-analyzer

This comment has been minimized.

@Kivooeo Kivooeo force-pushed the fix-139999-type-suggestion branch from 660a5f4 to 9a92054 Compare April 20, 2025 11:35
Copy link
Contributor

Choose a reason for hiding this comment

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

Also test that a common name like Item still suggest making it generic

&& sugg.chars().skip(1).any(|c| c.is_lowercase());

// If it's not a known generic name and looks like a concrete type, skip the suggestion.
if !is_common_generic && looks_like_camel_case && sugg.len() > 3 {
Copy link
Contributor

Choose a reason for hiding this comment

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

the len 3 check is always triggered on const parameter suggestions. check the length of ident instead. Also add a test for const generics, too

Copy link
Member Author

@Kivooeo Kivooeo Apr 22, 2025

Choose a reason for hiding this comment

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

can you please show me example of test for const, because i dont see how can i do it

pub fn do_stuff<const T: usize>() -> Option<T> {
    None
}

this surely wrong because trigger another errror
and

pub fn do_stuff<const T: usize>() -> Option<ForgetType> {
    None
}

this works fine

and i cant do something like this because it's basically wrong

pub fn do_stuff() -> Option<const T: usize> {
    None
}

so i need an example of how test it correctly

or do you meant something like this?

pub fn do_stuff() -> Option<MAX> {
    None
}

MAX here looks like constant so should be a generic right?

Copy link
Member

Choose a reason for hiding this comment

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

fn f() -> [(); N] {}

Copy link
Member Author

@Kivooeo Kivooeo Apr 22, 2025

Choose a reason for hiding this comment

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

this one works just fine i guess

error[E0425]: cannot find value `N` in this scope
 --> C:\Users\Love\projects\asfgas\src/main.rs:1:16
  |
1 | fn f() -> [(); N] {}
  |                ^ not found in this scope
  |
help: you might be missing a const parameter
  |
1 | fn f<const N: /* Type */>() -> [(); N] {}
  |     +++++++++++++++++++++

Copy link
Member

Choose a reason for hiding this comment

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

Oli is saying that for const parameters you currently perform checks like common_generic_names.contains("const N: usize") which will never fail! You should compare against the original ident (in this case N)

Copy link
Member

Choose a reason for hiding this comment

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

Note however that for const parameters, the list common_generic_names doesn't make sense, it's specifically for type parameters, so you should be comparing against a different list or none at all

Copy link
Member Author

Choose a reason for hiding this comment

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

yes i got it, but i was wondering how do i write tests for this const case more correctly, and i also want to ask if name is full uppercase, should we consider it as generic?

Copy link
Member

Choose a reason for hiding this comment

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

We should either use a different list for const params (of common const parameter names of length>1 (idk which ones could be considered common (LEN? LENGTH?, SIZE? perhaps I dunno)) or don't do such a check for them at all

Copy link
Member

@fmease fmease Apr 22, 2025

Choose a reason for hiding this comment

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

and i also want to ask if name is full uppercase, should we consider it as generic?

I guess not necessarily, similar to (generic) type parameters. Since ELEMS can just as well refer to a const item. E.g.:

const ELEMS: u32 = 3;
fn f() -> [i32; ELEMZ] { todo!() }

Comment on lines 2793 to 2794
.map(|c| c.is_uppercase())
.unwrap_or(false)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
.map(|c| c.is_uppercase())
.unwrap_or(false)
.is_some_and(|c| c.is_uppercase())

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 22, 2025
@oli-obk
Copy link
Contributor

oli-obk commented Apr 22, 2025

To close the issue when this PR is merged, change your main comment to contain fixes #123456 (also please remove that from the title

@Kivooeo Kivooeo changed the title Improve type parameter suggestion heuristic for missing types (fixes #139999) Improve type parameter suggestion heuristic for missing types Apr 22, 2025
//
// This approach may produce false positives or negatives, but works well in most cases.

let common_generic_names: &[&str] = &[
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let common_generic_names: &[&str] = &[
let common_type_param_names: &[&str] = &[

since it doesn't make sense for 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.

Also, I'd like to draw attention to the

    && segment.ident.name != kw::SelfUpper
    && segment.ident.name != kw::Dyn =>

at the very start of the function. We're already filtering out certain identifiers (in this case Self and dyn). I'd like us to unify this check and the common_generic_names one is some way or another

Copy link
Member Author

@Kivooeo Kivooeo Apr 22, 2025

Choose a reason for hiding this comment

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

this is good point, will it be better to move my logic right after this let (ident, span) = ... or integrate them inside the match?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think what @fmease might expect is a change like...

        let [segment] = segments else {
               return None;
         };

          // comment here explaining why we skip these
         let skip_names = [kw::SelfUpper, kw::Dyn, sym::This, sym::Error, /* etc */];

         if !segment.has_generic_args && !skip_names.contains(segment.ident.name) {
                 return None;
         }
         let ident = segment.ident.to_string();
         let span =  segment.ident.span;

I personally much prefer let_else over a match in this case. Feel free to write it with a match if you prefer.

Also, not all these names are available as symbols in sym. Feel free to add them there as needed,

Copy link
Member

Choose a reason for hiding this comment

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

We already have a bunch of test files for this diagnostic:

  • tests/ui/missing/missing-items/missing-const-parameter.rs
  • tests/ui/missing/missing-items/missing-type-parameter2.rs
  • tests/ui/missing/missing-items/missing-type-parameter.rs

Could you merge your new test cases into the appropriate preexisting test files? That would great, thanks!

Copy link
Member

Choose a reason for hiding this comment

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

We already have a bunch of test files for this diagnostic:

  • tests/ui/missing/missing-items/missing-const-parameter.rs
  • tests/ui/missing/missing-items/missing-type-parameter2.rs
  • tests/ui/missing/missing-items/missing-type-parameter.rs

Could you merge your new test cases into the appropriate preexisting test files? That would great, thanks!

@Kivooeo
Copy link
Member Author

Kivooeo commented Apr 22, 2025

@oli-obk @fmease check this changes, replaced sugg with ident and added more test cases.
i played around this check at function start and i didnt liked it much because split condition leads to not all cases are covered, and moving entire logic on top is worse because the place it current located filtering out main which is good for more precise error messaging.

and i also didnt liked idea about renaming array of common names, because it's just common generic names that looks like UpperCamelCase and it's len > 3, so this array and check is needed to remove false positive results

@rust-log-analyzer

This comment has been minimized.

@Kivooeo Kivooeo force-pushed the fix-139999-type-suggestion branch from 25300e3 to e94d192 Compare April 22, 2025 14:53
@fmease fmease self-assigned this Aug 4, 2025
@alex-semenyuk
Copy link
Member

@Kivooeo
Thanks for your contribution.
Form wg-triage. Any updates on this PR?

@Kivooeo
Copy link
Member Author

Kivooeo commented Aug 9, 2025

It's basically waiting for review, in my last message here, I questened about how to better do this, and it's basically stalled

@alex-semenyuk
Copy link
Member

@rustbot label: +S-waiting-on-review, -S-waiting-on-author

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Aug 9, 2025
@Kivooeo
Copy link
Member Author

Kivooeo commented Aug 9, 2025

@fmease given to gap of time, we need to revise rules, which we we'll use to use heuristics and filter generics out of forgetted things,

I also wonder if we even need such mechanism but let's say for start thay yes

Then here me proposing a start version of rules and then work on it before implementation itself (which wont be a problem)

  1. less than eight length
  2. check camelcase
  3. we might want somehow check scope context and signature if it have any generics for sure
  4. id keep a list with common names
  5. also we can module some kind of score system

@wesleywiser
Copy link
Member

Hi @oli-obk and @fmease, it looks like the author has some questions here that would be helpful in getting this ready for another review.

@bors
Copy link
Collaborator

bors commented Sep 13, 2025

☔ The latest upstream changes (presumably #146499) made this pull request unmergeable. Please resolve the merge conflicts.

Copy link
Member

@fmease fmease left a comment

Choose a reason for hiding this comment

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

I'm sorry for the delay.

The fix modifies the logic in the type suggestion code to skip type parameter suggestions when the missing type name:

  1. Is not a common generic name (e.g., Item, Output, Error).
  2. Uses UpperCamelCase, which is typical for concrete types.
  3. Is longer than 3 characters, as type parameters are often short (e.g., T, U).

I guess in practice it's true that these are unlikely meant to be type parameters. It does kind of punish users who make an effort in descriptively naming their type/const params.

I disagree with some of these "arbitrarily chosen" exceptions like Error (I have named ty params that before over just E). Strictly speaking, upper camel case is the naming convention of all non-primitive types which includes type params.

In order to unblock this, could you please remove the list of exceptions together with the upper camel case heuristic and only keep the codepoint count check.

I was about to say let's raise the limit from 3 to 5 but actually let's make this heuristic even simpler and require the ident to be a single upper-case char (which we've already computed). On master, that ident only needs to be one if we're outside of a generic argument list.

It goes directly against my previous statement ("punishes" people with descriptive generic param names) but I find it more important that the heuristic is a simple as possible (without greatly sacrificing the diagnostic quality). If you believe count==1 regresses to many tests/ui/s, then go with my suggest count==5 (still no skip list or casing detection please).

View changes since this review

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

rustc confuses missing types with missing type parameters
9 participants