Skip to content

Multiple code action for (at least) explicit record fields #3574

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

Closed
joyfulmantis opened this issue May 3, 2023 · 2 comments · Fixed by #3750 or #3775
Closed

Multiple code action for (at least) explicit record fields #3574

joyfulmantis opened this issue May 3, 2023 · 2 comments · Fixed by #3750 or #3775
Assignees
Labels
type: bug Something isn't right: doesn't work as intended, documentation is missing/outdated, etc..

Comments

@joyfulmantis
Copy link
Collaborator

Any time that expansion is used, for example, because of a dot record, the renamed ast includes an HsExpanded expression that contains both the original and generated expressions. This is problematic because an ordinary syb traversal of the ast usually traverses both sets of expressions, causing duplicate returns. In explicit record fields, this causes duplicate codeActions, for example.

Steps to reproduce

This small modification of one of explicit record fields tests was able to trigger the issue

{-# LANGUAGE Haskell2010 #-}
{-# LANGUAGE RecordWildCards #-}
{-# Language OverloadedRecordDot #-}
module Construction where

data MyRec = MyRec
  { foo :: Int
  , bar :: Int
  , baz :: Char
  }

convertMe :: () -> Int
convertMe _ =
  let foo = 3
      bar = 5
      baz = 'a'
  in MyRec {..}.foo

Expected behavior

In this case, instead of the expected one codeAction offering to rewrite the wildcard when clicking upon MyRec, there were two completely identical ones.

If using everything, this can be fixed with using everythingBut, manually starting traversal on one of the branches when HsExpanded is found, and at the same time returning True, which keeps the first everything from traversing any more on that branch.
For example, this is how I did it on the overloaded-record-dot-plugin

-- It's important that we use everthingBut here, because if we used everything we would
-- get duplicates for every case that occurs inside a HsExpanded expression.
collectRecordSelectors :: GenericQ [RecordSelectorExpr]
collectRecordSelectors = everythingBut (<>) (([], False) `mkQ` getRecSels)
-- | We want to return a list here, because on the occasion that we encounter a HsExpanded expression,
-- | we want to return all the results from recursing on one branch, which could be multiple matches
getRecSels :: LHsExpr (GhcPass 'Renamed) -> ([RecordSelectorExpr], Bool)
-- When we stumble upon an occurrence of HsExpanded, we only want to follow one branch
-- we do this here, by explicitly returning occurrences from traversing the original branch,
-- and returning True, which keeps syb from implicitly continuing to traverse.
getRecSels (unLoc -> XExpr (HsExpanded a _)) = (collectRecordSelectors a, True)

I, unfortunately, opened a pull request on the main branch of my fork of hls, so until that closes, I won't be able to submit a fix for explicit record fields. This bug may also affect other plugins, but probably only if they used the Renamed or TypeChecked ast.

@joyfulmantis joyfulmantis added status: needs triage type: bug Something isn't right: doesn't work as intended, documentation is missing/outdated, etc.. labels May 3, 2023
@ozkutuk
Copy link
Collaborator

ozkutuk commented May 6, 2023

Good catch! I implemented the proposed solution (and even copied the comments verbatim 🤭) in #3579. Can you take a look if everything makes sense? @joyfulmantis

@joyfulmantis
Copy link
Collaborator Author

Sure! Thanks for getting a fix out for this so quickly!

joyfulmantis added a commit to joyfulmantis/haskell-language-server that referenced this issue Aug 8, 2023
@mergify mergify bot closed this as completed in #3750 Aug 24, 2023
mergify bot added a commit that referenced this issue Aug 24, 2023
* Fix #3574 and support resolve in explicit records

* render shouldn't fail, added tests

* Improved comments

* Remove unused language extensions

* 8.10 and 9.0 fixes and separate collect names into it's own rule

* fix flags and add Resolve module haddock

* better tests

* works for all ghc versions

* Fix flags

* ignore incomplete record updates in explicit record fields

---------

Co-authored-by: Michael Peyton Jones <me@michaelpj.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
@fendor fendor mentioned this issue Aug 25, 2023
19 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Something isn't right: doesn't work as intended, documentation is missing/outdated, etc..
Projects
None yet
3 participants