Skip to content

Add a quickfix for accessing a private field of a struct #19869

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

Merged
merged 1 commit into from
Jun 3, 2025

Conversation

MatrixFrog
Copy link
Contributor

@MatrixFrog MatrixFrog commented May 26, 2025

If you have a struct with a private field

struct S {
  bar: u32,
}

and you try to use it elsewhere

let s = S {
  bar: 100,
};

rust-analyzer will now offer a fix to make the field pub, or pub(crate), so that it is visible from the place you're trying to use it from.

@MatrixFrog MatrixFrog force-pushed the publicize_field branch 3 times, most recently from ba133a9 to 7789e92 Compare May 26, 2025 20:36
@davidbarsky
Copy link
Contributor

Oh, I personally wanted this feature for a bit! Do you mind fixing the clippy lints, by any chance?

@MatrixFrog
Copy link
Contributor Author

I'm hoping to finish this up tomorrow. It's my first PR to rest analyzer so it's extremely possible that I have done something quite wrong, but hopefully someone can help sort everything out.

Copy link
Contributor

@ChayimFriedman2 ChayimFriedman2 left a comment

Choose a reason for hiding this comment

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

Sorry to dismiss (part of) the work you did, but I'd be more comfortable if when we generate the diagnostics we will save the field, instead of trying to lookup it in the type's fields list (while we're at it, we could also use the existing variant instead of re-resolving it, but you don't need to do this).

Generally, what needs to be done is this: here:

You will need to change private from bool into Option<LocalFieldId>, and save it when emitting the diagnostic (it is readily available). Similarly here:

pub private: bool,

You need to change private to contain Option<hir::Field> (you will need to do some conversions from hir-ty to hir types but it's trivial).

Then in the quickfix code, you call source() on the field to get the AST.

In addition to that, I noticed that you handle macro files wrongly: when the type is inside a macro, you should use the original file not just for where to put the edit, but also to find the node, otherwise the ranges will be messed up. Use original_file_X() methods on the InFile for that (they're defined in hir-expand/src/files.rs). With my suggestion, I suggest you lookup the first token in the field declaration, call original_file_range_opt() on that, and insert pub before the token.

Feel free to ask should you need more help.

Copy link
Member

@Veykril Veykril left a comment

Choose a reason for hiding this comment

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

Nice change! That means we can remove this part from the corresponding assist I think

fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {

I feel like NoSuchField's private bool field should be changed to Option<FieldId> (which is Some for when it exists but is not visible). THen we have access here to the semantic field and can source that directly instead of having to iterate through the AST here.

@Veykril
Copy link
Member

Veykril commented May 28, 2025

Oh, looks like Chayim proposed the exact same thing (with more details), didn't see that 😅

@MatrixFrog
Copy link
Contributor Author

MatrixFrog commented May 28, 2025

No, not dismissive at all, this is very helpful! Got started on that here MatrixFrog@5a259c0 but there's one spot where I'm not quite seeing how to make the conversion: MatrixFrog@5a259c0#diff-07791b9f83c04bf09ca939688d564a7e35d082e2b916b33c53632e7d28e2e96dR651

I'm still getting used to the way a lot of rust-analyzer code passes around Idx's instead of the actual structs those indexes refer to.

@ChayimFriedman2
Copy link
Contributor

A hir::Field is just a VariantDef and a LocalFieldId. The LocalFieldId you have, and the hir-ty diagnostic also contains a VariantId which is an enum of StructId, UnionId and EnumVariantId. VariantDef is an enum of Struct, Union and Variant. Struct is a wrapper around StructId, ditto for Union and Variant. So you just need to match the VariantId and call into() on each variant and wrap it in VariantDef. You can even create a impl From<VariantId> for VariantDef in hir/src/lib.rs for that.

@MatrixFrog
Copy link
Contributor Author

Thank you so much, I think I'm starting to get a feel for how things work. This is still a work in progress, but I think it's getting closer!

@MatrixFrog MatrixFrog marked this pull request as ready for review May 31, 2025 08:06
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 31, 2025
Copy link
Member

@Veykril Veykril left a comment

Choose a reason for hiding this comment

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

Looks good aside from the comment changes!

Copy link
Contributor

@ChayimFriedman2 ChayimFriedman2 left a comment

Choose a reason for hiding this comment

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

Looks good! Maybe it's good to squash the commits.

@MatrixFrog
Copy link
Contributor Author

thank you this was fun!

@Veykril Veykril added this pull request to the merge queue Jun 3, 2025
Merged via the queue into rust-lang:master with commit cf969d2 Jun 3, 2025
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants