Skip to content

Consider HsExpanded expressions during SYB traversal #3579

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
wants to merge 1 commit into from

Conversation

ozkutuk
Copy link
Collaborator

@ozkutuk ozkutuk commented May 6, 2023

Fixes #3574. Implements the proposed solution and adds the given reproduction steps as a test case.

Copy link
Collaborator

@joyfulmantis joyfulmantis left a comment

Choose a reason for hiding this comment

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

Thanks for getting the patch out so quickly!

@@ -347,14 +354,19 @@ collectRecords = everything (<>) (maybeToList . (Nothing `mkQ` getRecPatterns `e
collectNames :: GenericQ (UniqFM Name [Name])
collectNames = everything (plusUFM_C (<>)) (emptyUFM `mkQ` (\x -> unitUFM x [x]))

getRecCons :: LHsExpr (GhcPass 'Renamed) -> Maybe RecordInfo
getRecCons :: LHsExpr (GhcPass 'Renamed) -> (Maybe RecordInfo, Bool)
Copy link
Collaborator

Choose a reason for hiding this comment

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

The problem with returning a Maybe instead of a List here is that you can only return one match from within the HsExpanded expression, even though there could be multiple matches. This example showcases this.

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

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

data YourRec = YourRec
  { foo2 :: MyRec
  , bar2 :: Int
  , baz2 :: Char
  }

myRecExample = MyRec {..}
  where
    foo = 5
    bar = 6
    baz = 'a'

yourRecExample = YourRec {..}
    where
        foo2 = myRecExample
        bar2 = 5
        baz2 = 'a'

convertMe :: () -> Int
convertMe _ =
  (let MyRec{..} = myRecExample
       YourRec{..} = yourRecExample
    in foo2).foo

In this case, your code, would only offer to rewrite the first matched wildcard instead of both.
This example is very constructed, however, HsExpansion could possibly be used for more cases in the future, which would cause these sorts of edge cases to also appear more often.

where
mkRecInfo :: LHsExpr (GhcPass 'Renamed) -> Maybe RecordInfo
mkRecInfo expr = listToMaybe
[ RecordInfoCon realSpan' (unLoc expr) | RealSrcSpan realSpan' _ <- [ getLoc expr ]]
getRecCons _ = Nothing
getRecCons _ = (Nothing, False)

getRecPatterns :: LPat (GhcPass 'Renamed) -> Maybe RecordInfo
getRecPatterns conPat@(conPatDetails . unLoc -> Just (RecCon flds))
Copy link
Collaborator

Choose a reason for hiding this comment

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

ConPat can also occur inside an HsExpanded expression. For example, the constructed example above uses ConPat (because the wildcards occur in the let binding)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually thinking about it though, you shouldn't need to change this, because getRecCon should already, by calling collectRecords, return both RecordCon and ConPat matches. May want to point that out in the comments though.

@joyfulmantis
Copy link
Collaborator

Closed. Completed in #3750 .

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.

Multiple code action for (at least) explicit record fields
2 participants