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

Rename implementation #1878

Closed
wants to merge 55 commits into from
Closed

Conversation

OliverMadine
Copy link
Collaborator

@OliverMadine OliverMadine commented May 30, 2021

A plugin to implement workspace-wide renaming of symbols as my GSoC 2021 project!

Todo List

  • Initial package structure
  • Test cases
  • Find basic reference locations
  • Find correct references of type constructors
  • Find correct references of data constructors
  • Write any additional test cases (type families, record wildcards)
  • Validate names?
  • Index all files (hie-bios)
  • Use retrie to rewrites right-hand sides
  • Limit retrie rewrite locations to references locations
  • Parse AST to rename left-hand sides
  • Type rewrites
  • Declaration rewrite
  • Qualified name rewrites
  • Update import lists
  • Disable plugin for ghc < 8.8

Example:
example

@OliverMadine OliverMadine marked this pull request as draft May 30, 2021 20:23
@wz1000
Copy link
Collaborator

wz1000 commented May 31, 2021

Have you thought about augmenting something like retrie with the reference data to get semantics preserving transformations using exactprint?

Here is another case for you to consider(the language is full of these, you have to use exactprint to have any hope of being correct):

foo = do bar
         baz

naively substituting quxx for foo breaks the parse:

quux = do bar
         baz

@pepeiborra
Copy link
Collaborator

pepeiborra commented May 31, 2021

Have you thought about augmenting something like retrie with the reference data to get semantics preserving transformations using exactprint?

Here is another case for you to consider(the language is full of these, you have to use exactprint to have any hope of being correct):

foo = do bar
         baz

naively substituting quxx for foo breaks the parse:

quux = do bar
         baz

Yes, I agree, please consider hooking up retrie with hiedb. @xich has given me powers to maintain and release retrie, so I can help review and land the changes

@OliverMadine
Copy link
Collaborator Author

Have you thought about augmenting something like retrie with the reference data to get semantics preserving transformations using exactprint?

Here is another case for you to consider(the language is full of these, you have to use exactprint to have any hope of being correct):

foo = do bar
         baz

naively substituting quxx for foo breaks the parse:

quux = do bar
         baz

Yes, I agree, please consider hooking up retrie with hiedb. @xich has given powers to maintain and release retrie, so I can help review and land the changes

ok, I can see why it’s important that I work with retrie. I’ll look into doing this. Thank you both for the help getting started!

@expipiplus1
Copy link
Contributor

An alternative if the user is using a formatting program, would be to modify the AST and have the formatter reprint it. I assume that retrie is doing something similar under the hood.

@pepeiborra
Copy link
Collaborator

@OliverMadine do how is this going? Do you mind sharing what you are working on?

@OliverMadine
Copy link
Collaborator Author

OliverMadine commented Jul 9, 2021

@OliverMadine do how is this going? Do you mind sharing what you are working on?

Hello,

I've been working on using retrie to perform the rewrites. I've pushed a basic version that manages limiting the rewrites to references (using MatchResultTransformer) and allows rewrites of recursive calls (since this is not an issue with rename rewrites)

The main problem now is renaming declarations themselves.
For example:

when renaming foo to bar in

foo n = foo (n-1) + foo (n-2)

we get

foo n = bar (n-1) + bar (n-2)

but we expect

bar n = bar (n-1) + bar (n-2)

I'm looking at modifying the ast directly to do this.

As for hie-bios, I had a hacky approach that assumes the hie.yaml lists all components in a multi-cradle then getCompilerOptions can return all components to be loaded simultaneously, however, it looks like @fendor has been working on a much nicer solution.

Sorry for the late response. I'll also try to update you more frequently from now on.

@fendor
Copy link
Collaborator

fendor commented Jul 9, 2021

@OliverMadine Your hacky solution is still welcome for supporting older cabal and stack versions!

@pepeiborra
Copy link
Collaborator

@OliverMadine do how is this going? Do you mind sharing what you are working on?

Hello,

I've been working on using retrie to perform the rewrites. I've pushed a basic version that manages limiting the rewrites to references (using MatchResultTransformer) and allows rewrites of recursive calls (since this is not an issue with rename rewrites)

The main problem now is renaming declarations themselves.
For example:

when renaming foo to bar in

foo n = foo (n-1) + foo (n-2)

we get

foo n = bar (n-1) + bar (n-2)

but we expect

bar n = bar (n-1) + bar (n-2)

I'm looking at modifying the ast directly to do this.

As for hie-bios, I had a hacky approach that assumes the hie.yaml lists all components in a multi-cradle then getCompilerOptions can return all components to be loaded simultaneously, however, it looks like @fendor has been working on a much nicer solution.

Sorry for the late response. I'll also try to update you more frequently from now on.

Fantastic, this is good progress. I left a comment on how to deal with renaming the declaration.

I think that it would be a good idea to land this early version of the feature as soon as possible. If you don't want people to use it yet, change the flag in haskell-language-server.cabalto be disabled by default.

@pepeiborra
Copy link
Collaborator

The lack of updates here is getting worrying. @OliverMadine Can you share the latest status?

@OliverMadine
Copy link
Collaborator Author

OliverMadine commented Jul 19, 2021

The lack of updates here is getting worrying. @OliverMadine Can you share the latest status?

I’ve had covid for the past week so it hasn’t been my most productive… I pushed a few small changes but my main issue is still renaming declarations (I've explained why I don't think your previously suggested solution will work above).

@pepeiborra
Copy link
Collaborator

The lack of updates here is getting worrying. @OliverMadine Can you share the latest status?

I’ve had covid for the past week so it hasn’t been my most productive… I pushed a few small changes but my main issue is still renaming declarations (I've explained why I don't think your previously suggested solution will work above).

Hope you are all right, and thanks for the update! I'll reply inline.

While we look for a solution, it would be a good idea to rebase on top of master and fix conflicts so that we can get CI running and a green set of lights.

Copy link
Collaborator

@pepeiborra pepeiborra 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! I have left a couple of comments requesting minor changes.

In addition to those, I think you need to register the plugin test suite in the GitHub CI explicitly, see https://github.com/haskell/haskell-language-server/blob/master/.github/workflows/test.yml

plugins/hls-rename-plugin/hls-rename-plugin.cabal Outdated Show resolved Hide resolved
haskell-language-server.cabal Outdated Show resolved Hide resolved
plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs Outdated Show resolved Hide resolved
plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs Outdated Show resolved Hide resolved
stack-8.6.4.yaml Outdated Show resolved Hide resolved
stack-8.6.5.yaml Outdated Show resolved Hide resolved
stack-9.0.1.yaml Outdated Show resolved Hide resolved
Comment on lines 118 to 138
updateLhsDecls :: [Location] -> RdrName -> HsModule GhcPs -> HsModule GhcPs
updateLhsDecls refs newRdrName ps@HsModule{hsmodDecls} =
ps {hsmodDecls = map (fmap renameLhsDecl) hsmodDecls}
where
renameLhsDecl :: HsDecl GhcPs -> HsDecl GhcPs
renameLhsDecl (SigD xSig (TypeSig xTySig sigNames wc)) =
SigD xSig $ TypeSig xTySig (map renameRdrName' sigNames) wc
renameLhsDecl (ValD xVal funBind@FunBind{fun_id, fun_matches = fun_matches@MG{mg_alts}})
= ValD xVal $ funBind {
fun_id = renameRdrName' fun_id,
fun_matches = fun_matches {mg_alts = fmap (map (fmap $ renameLhsMatch newRdrName)) mg_alts}
}
renameLhsDecl (TyClD xTy dataDecl@DataDecl{tcdLName, tcdDataDefn = hsDataDefn@HsDataDefn{dd_cons}})
= TyClD xTy $ dataDecl {
tcdLName = renameRdrName' tcdLName
}
renameLhsDecl (TyClD xTy synDecl@SynDecl{tcdLName})
= TyClD xTy $ synDecl {
tcdLName = renameRdrName' tcdLName
}
renameLhsDecl decl = decl
Copy link
Collaborator

Choose a reason for hiding this comment

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

Did you consider SYB for doing this rewrite, just like wingman does?

That way, if newer GHC versions change the AST, this code will still work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think this would be a little difficult since there are a lot of names in the decls that we do not want to rename (either because retrie is already renaming the given reference or we don't support renaming for the given type of name).

However, I could take this approach (i.e. modifying the entire source directly and not using retrie). See my comment below.

@OliverMadine
Copy link
Collaborator Author

@pepeiborra

Any thoughts on modifying the entire parsed source directly (using SYB rather than using Retrie)?

Here’s a branch that takes this approach.
It is passing all of the tests locally.

The advantages seem to be:

  • Compatibility with earlier ghc versions
  • No issues with retrie compatibility on windows
  • We can rename non top-level names
  • Easier solution to maintain

The disadvantage would be that it is slower. Possibly significantly?
Maybe I’m missing something else?

@pepeiborra
Copy link
Collaborator

@pepeiborra

Any thoughts on modifying the entire parsed source directly (using SYB rather than using Retrie)?

Here’s a branch that takes this approach.

It is passing all of the tests locally.

The advantages seem to be:

  • Compatibility with earlier ghc versions

  • No issues with retrie compatibility on windows

  • We can rename non top-level names

  • Easier solution to maintain

The disadvantage would be that it is slower. Possibly significantly?

Maybe I’m missing something else?

I like it. Retrie uses SYB as well under the hood, so I don't expect this solution to be slower. A rename is the trivial rewrite where both the lhs and the rhs are a simple name, so we don't really need the full power of retrie.

@OliverMadine
Copy link
Collaborator Author

OliverMadine commented Aug 17, 2021

I'm opening a new PR due to the change of approach (and to clean the mess of commits I made while learning how to make the test cases consistent).

@samhh
Copy link

samhh commented Aug 18, 2021

Fixed link^: #2108

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.

8 participants