Skip to content

Rename(refactor) support for record's named fields #53024

Open

Description

@hamsbrar
({int a}) record = (a: 123);
//    ^ hovering over 'a' and pressing f2 shows the message "The element can't be renamed."
> dart --version
Dart SDK version: 3.0.6 (stable) (Tue Jul 11 18:49:07 2023 +0000) on "linux_x64"

> code --list-extensions --show-versions | grep dart-code
[email protected]

Activity

  1. added
    type-enhancementA request for a change that isn't a bug
    devexp-refactorsIssues with analysis server refactors
    P3A lower priority bug or feature request
    on Jul 24, 2023
  2. bwilkerson commented on Aug 6, 2023

    @bwilkerson
    Member

    I'm not claiming that it was the correct decision, but the choice to not support refactoring for record fields was intentional.

    The rationale behind the decision is that, unlike classes, enums, and other nominal types, records are structurally typed. That means that the types of a and b in

    void f(({int first, int last}) a, ({int first, int last}) b) {}

    are the same type because of the shape of the record. There's actually nothing there, other than the shape to tell us that they're intended to be the same. In fact, if we change the name a to nameLengths and the name b to characterRange, then we'd immediately suspect that the length of the first name and the first character in the range were only coincidentally named the same.

    In other words, for a compiler that doesn't understand the semantics of the names, there's no reason to think that the two record types have any semantic relationship, or that both names should be changed if one of the names is selected.

    We could make an exception for an assignment and assume that the type on the left should always be associated with the type on the right, and that would likely always be true. But what we decided was that it would be confusing if 'rename' was sometimes available and sometimes not, so we decided it was better to never offer it.

    Interested to see what the community thinks.

  3. hamsbrar commented on Aug 6, 2023

    @hamsbrar
    Author

    I think rename could be applied on locations where a shape is used.

    For example, let's take shape x1 that has a field a1. If I manually rename the field a1 to a2(which will result in a new shape x2), I can immediately see places where shape x1 is expected but not provided (thanks to warnings/errors). These broken places are the only places where shape x1 was being used so these are the only places where I've rename a1 to a2. If any of those broken places involves another shape, say y1, it's definitely the case y1 is structurally the same as x1(as long as shapes are sealed) so I've got to rename a1 to a2 in y1 too.

  4. bwilkerson commented on Aug 6, 2023

    @bwilkerson
    Member

    I think that's an interesting idea.

    I'd want to think through some concrete examples before committing to it in order to evaluate whether there are potentially confusing cases where using the shape would cause us to either rename too much or not rename enough, but I think it might be worth exploring.

    @scheglov Do we have information about which field names needed to be the same to resolve everything within a function or initializer expression? If not, is it information that we could collect? (Just trying to evaluate the feasibility of the proposed approach.

  5. DanTup commented on Oct 3, 2024

    @DanTup
    Collaborator

    A slightly related issue came up at Dart-Code/Dart-Code#5294, which is that LSP's Document Highlights also doesn't know how to relate these things together, so you don't get the same highlighting you do for class fields.

    I suspect if something was implemented to support renaming, it could also be used to improve that.

  6. added
    area-devexpFor issues related to the analysis server, IDE support, linter, `dart fix`, and diagnostic messages.
    and removed on Feb 21, 2025
  7. FMorschel commented on Apr 24, 2025

    @FMorschel
    Contributor

    I'd love for this to happen so record typedefs work the same as function typedefs.

    typedef MyFunc = void Function(int, {int value});
    typedef MyRecord = (int, {int value});

    The same rules applied to renaming the first should probably work to rename the second as well.

  8. bwilkerson commented on Apr 24, 2025

    @bwilkerson
    Member

    I agree, feature parity would be good here.

  9. scheglov commented on Jul 21, 2025

    @scheglov
    Contributor

    What does work for function typedefs?

    typedef MyFunc = void Function(int, {int value});
    
    void f2(MyFunc myFunc) {
      myFunc(0, value: 1);
    }

    Renaming value in int value does not update value: 1.

  10. FMorschel commented on Jul 21, 2025

    @FMorschel
    Contributor

    Sorry, I should've been clearer. I'm not saying that it does work today. I was saying that it would be great if both did.

    The only thing that does work in one that doesn't in the other is comment references, but renaming between those two (comment reference and actual parameter in Function typedefs) is still broken (see #60580). I've tried working on that, but I'm not familiar enough with analyzer yet to understand where I should fix it. It seems that both elements are different.

    Maybe that is the same underlying problem with your example, but I've not debugged that case yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3A lower priority bug or feature requestarea-devexpFor issues related to the analysis server, IDE support, linter, `dart fix`, and diagnostic messages.devexp-refactorsIssues with analysis server refactorstype-enhancementA request for a change that isn't a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions