-
-
Notifications
You must be signed in to change notification settings - Fork 389
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
{-# LANGUAGE LambdaCase #-} | ||
{-# LANGUAGE OverloadedStrings #-} | ||
{-# LANGUAGE PatternSynonyms #-} | ||
{-# LANGUAGE TupleSections #-} | ||
{-# LANGUAGE TypeFamilies #-} | ||
{-# LANGUAGE TypeOperators #-} | ||
{-# LANGUAGE ViewPatterns #-} | ||
|
@@ -18,12 +19,13 @@ module Ide.Plugin.ExplicitFields | |
import Control.Lens ((^.)) | ||
import Control.Monad.IO.Class (MonadIO, liftIO) | ||
import Control.Monad.Trans.Except (ExceptT) | ||
import Data.Bifunctor (first) | ||
import Data.Functor ((<&>)) | ||
import Data.Generics (GenericQ, everything, extQ, | ||
mkQ) | ||
import Data.Generics (GenericQ, everything, | ||
everythingBut, extQ, mkQ) | ||
import qualified Data.HashMap.Strict as HashMap | ||
import Data.Maybe (isJust, listToMaybe, | ||
maybeToList, fromMaybe) | ||
import Data.Maybe (fromMaybe, isJust, | ||
listToMaybe, maybeToList) | ||
import Data.Text (Text) | ||
import Development.IDE (IdeState, NormalizedFilePath, | ||
Pretty (..), Recorder (..), | ||
|
@@ -36,11 +38,11 @@ import Development.IDE.Core.Shake (define, use) | |
import qualified Development.IDE.Core.Shake as Shake | ||
import Development.IDE.GHC.Compat (HsConDetails (RecCon), | ||
HsRecFields (..), LPat, | ||
Outputable, getLoc, unLoc, | ||
recDotDot) | ||
Outputable, getLoc, recDotDot, | ||
unLoc) | ||
import Development.IDE.GHC.Compat.Core (Extension (NamedFieldPuns), | ||
GhcPass, | ||
HsExpr (RecordCon, rcon_flds), | ||
GhcPass, HsExpansion (..), | ||
HsExpr (RecordCon, XExpr, rcon_flds), | ||
HsRecField, LHsExpr, LocatedA, | ||
Name, Pass (..), Pat (..), | ||
RealSrcSpan, UniqFM, | ||
|
@@ -329,8 +331,13 @@ showRecordCon expr@(RecordCon _ _ flds) = | |
expr { rcon_flds = preprocessRecordCon flds } | ||
showRecordCon _ = Nothing | ||
|
||
-- 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. | ||
collectRecords :: GenericQ [RecordInfo] | ||
collectRecords = everything (<>) (maybeToList . (Nothing `mkQ` getRecPatterns `extQ` getRecCons)) | ||
collectRecords = | ||
everythingBut (<>) (first maybeToList . ((Nothing, False) `mkQ` getRecPatterns' `extQ` getRecCons)) | ||
where | ||
getRecPatterns' = (,False) . getRecPatterns | ||
|
||
-- | Collect 'Name's into a map, indexed by the names' unique identifiers. | ||
-- The 'Eq' instance of 'Name's makes use of their unique identifiers, hence | ||
|
@@ -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) | ||
-- When we stumble upon an occurrence of HsExpanded, we only want to follow a | ||
-- single 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. | ||
getRecCons (unLoc -> XExpr (HsExpanded _ expanded)) = (listToMaybe (collectRecords expanded), True) | ||
getRecCons e@(unLoc -> RecordCon _ _ flds) | ||
| isJust (rec_dotdot flds) = mkRecInfo e | ||
| isJust (rec_dotdot flds) = (mkRecInfo e, False) | ||
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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{-# LANGUAGE Haskell2010 #-} | ||
{-# LANGUAGE RecordWildCards #-} | ||
{-# Language OverloadedRecordDot #-} | ||
{-# LANGUAGE NamedFieldPuns #-} | ||
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, bar, baz}.foo |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{-# 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 |
There was a problem hiding this comment.
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.
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.