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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

MatrixFrog
Copy link
Contributor

No description provided.

@MatrixFrog MatrixFrog force-pushed the publicize_field branch 2 times, most recently from 20273d2 to ba133a9 Compare May 26, 2025 19:35
@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.


let source_change = SourceChange::from_text_edit(
def_file_id.file_id(sema.db),
TextEdit::insert(field_definition.syntax().text_range().start(), "pub ".into()),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Two options here.

  1. Use pub(crate) if the definition and usage are in the same crate. pub otherwise.
  2. Give the user the option of which to use.

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 I prefer the first option, but having a setting is also fine.

Copy link
Member

Choose a reason for hiding this comment

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

+1 for the first, we do have an assist to promote pub(crate) to pub so its an easy fix for the user, no need for a setting i think

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.

Comment on lines +82 to +84
let source_change = SourceChange::from_text_edit(
def_file_id.file_id(sema.db),
TextEdit::insert(field_definition.syntax().text_range().start(), "pub ".into()),
Copy link
Member

Choose a reason for hiding this comment

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

The text range here might belong to a macro file here, so we need to upmap that out of macros (and use the upmapped file id from there). Otherwise things will break

"make_field_public",
"Make field public",
source_change,
record_expr_field.syntax().text_range(),
Copy link
Member

Choose a reason for hiding this comment

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

Same here, this might be a range from a macro expansion, we need to upmap that

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants