diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index 2213c50e74..83147f70c1 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -10,7 +10,8 @@ module Ide.Plugin.Rename (descriptor, E.Log) where import Control.Lens ((^.)) import Control.Monad -import Control.Monad.Except (ExceptT, throwError) +import Control.Monad.Except (ExceptT, MonadError, + throwError) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Trans.Class (lift) import Data.Either (rights) @@ -40,14 +41,13 @@ import qualified Development.IDE.GHC.ExactPrint as E import Development.IDE.Plugin.CodeAction import Development.IDE.Spans.AtPoint import Development.IDE.Types.Location +import GHC (isGoodSrcSpan) import GHC.Iface.Ext.Types (HieAST (..), HieASTs (..), NodeOrigin (..), SourcedNodeInfo (..)) import GHC.Iface.Ext.Utils (generateReferencesMap) -import HieDb ((:.) (..)) import HieDb.Query -import HieDb.Types (RefRow (refIsGenerated)) import Ide.Plugin.Error import Ide.Plugin.Properties import Ide.PluginUtils @@ -55,7 +55,9 @@ import Ide.Types import qualified Language.LSP.Protocol.Lens as L import Language.LSP.Protocol.Message import Language.LSP.Protocol.Types - +#if MIN_VERSION_ghc(9,8,0) +import qualified GHC.Types.Name.Occurrence as OccName +#endif instance Hashable (Mod a) where hash n = hash (unMod n) descriptor :: Recorder (WithPriority E.Log) -> PluginId -> PluginDescriptor IdeState @@ -86,6 +88,8 @@ prepareRenameProvider state _pluginId (PrepareRenameParams (TextDocumentIdentifi renameProvider :: PluginMethodHandler IdeState Method_TextDocumentRename renameProvider state pluginId (RenameParams _prog (TextDocumentIdentifier uri) pos newNameText) = do nfp <- getNormalizedFilePathE uri + crossModuleEnabled <- liftIO $ runAction "rename: config" state $ usePropertyAction #crossModule pluginId properties + pm <- runActionE "Rename.GetParsedModule" state (useE GetParsedModule nfp) directOldNames <- getNamesAtPos state nfp pos directRefs <- concat <$> mapM (refsAtName state nfp) directOldNames @@ -99,46 +103,109 @@ renameProvider state pluginId (RenameParams _prog (TextDocumentIdentifier uri) p where matchesDirect n = occNameFS (nameOccName n) `elem` directFS directFS = map (occNameFS . nameOccName) directOldNames - case oldNames of -- There were no Names at given position (e.g. rename triggered within a comment or on a keyword) [] -> throwError $ PluginInvalidParams "No symbol to rename at given position" _ -> do - refs <- HS.fromList . concat <$> mapM (refsAtName state nfp) oldNames + refs' <- HS.fromList . concat <$> mapM (refsAtName state nfp) oldNames + exportRefs <- exportNameLocs pm oldNames + isExported <- or <$> mapM (isNameExplicitExported pm) oldNames + let refs = HS.union refs' (HS.fromList exportRefs) + currentModule = fmap unLoc $ hsmodName $ unLoc $ pm_parsed_source pm + isLocallyDefined name = + case (nameModule_maybe name, currentModule) of + (Just nameModule, Just curMod) -> moduleName nameModule == curMod + -- No module means local + (Nothing, _) -> True + -- Has module but current has none = not local + (Just _, Nothing) -> False + renamingLocalDeclaration = not (null directOldNames) && not (null oldNames) && all isLocallyDefined oldNames + + -- We have to show CrossModule Disabled error ONLY when + -- 1. CrossModule is Disabled + -- 2. User Tries to rename Exported variable + -- We still allow local variable renaming in Disabled CrossModule mode. + when (not crossModuleEnabled && ((not renamingLocalDeclaration) || isExported)) $ throwError $ PluginInternalError "Cross-module rename is disabled." + + -- if CrossModule renaming requires Explicit Export list + -- if variable is imported somewhere else && No explicit export => ERROR + -- if variable is locally used => No ERROR + let hasExplicitExportList = isJust (hsmodExports (unLoc $ pm_parsed_source pm)) + refFiles <- forM (HS.toList refs) $ \loc -> do + (file, _) <- locToFilePos loc + pure file + let hasExternalRefs = any (/= nfp) refFiles + when ( crossModuleEnabled && not hasExplicitExportList && hasExternalRefs && renamingLocalDeclaration ) $ throwError $ PluginInvalidParams + "Cannot rename symbol: module has no explicit export list and the symbol is referenced from other modules." -- Validate rename - crossModuleEnabled <- liftIO $ runAction "rename: config" state $ usePropertyAction #crossModule pluginId properties - unless crossModuleEnabled $ failWhenImportOrExport state nfp refs oldNames + -- Indirect names are assumed safe once the direct ones are when (any isBuiltInSyntax oldNames) $ throwError $ PluginInternalError "Invalid rename of built-in syntax" -- Perform rename let newName = mkTcOcc $ T.unpack newNameText filesRefs = collectWith locToUri refs +#if MIN_VERSION_ghc(9,8,0) + -- GHC 9.8+ stores field labels and their selectors in different OccName + -- namespaces, breaking equality checks. Expand to cover both variants. + oldOccNames = HS.fromList $ concatMap (expandOcc . nameOccName) oldNames + where + expandOcc occ + | occNameSpace occ == OccName.varName = [occ, mkOccNameFS (fieldName (occNameFS occ)) (occNameFS occ)] + | isFieldNameSpace (occNameSpace occ) = [occ, mkOccNameFS OccName.varName (occNameFS occ)] + | otherwise = [occ] +#else + oldOccNames = HS.fromList $ map nameOccName oldNames +#endif getFileEdit (uri, locations) = do verTxtDocId <- liftIO $ runAction "rename: getVersionedTextDoc" state $ getVersionedTextDoc (TextDocumentIdentifier uri) - getSrcEdit state verTxtDocId (replaceRefs newName locations) + getSrcEdit state verTxtDocId (replaceRefs newName locations oldOccNames) fileEdits <- mapM getFileEdit filesRefs pure $ InL $ fold fileEdits --- | Limit renaming across modules. -failWhenImportOrExport :: - IdeState -> - NormalizedFilePath -> - HashSet Location -> - [Name] -> - ExceptT PluginError (HandlerM config) () -failWhenImportOrExport state nfp refLocs names = do - pm <- runActionE "Rename.GetParsedModule" state - (useE GetParsedModule nfp) +-- | Check if a name is exported from the module +-- Crossmodule Renaming happens only if names are Explicit Exported +isNameExplicitExported :: + Monad m => + ParsedModule -> + Name -> + ExceptT PluginError m Bool +isNameExplicitExported pm name = do let hsMod = unLoc $ pm_parsed_source pm - case (unLoc <$> hsmodName hsMod, hsmodExports hsMod) of - (mbModName, _) | not $ any (\n -> nameIsLocalOrFrom (replaceModName n mbModName) n) names - -> throwError $ PluginInternalError "Renaming of an imported name is unsupported" - (_, Just (L _ exports)) | any ((`HS.member` refLocs) . unsafeSrcSpanToLoc . getLoc) exports - -> throwError $ PluginInternalError "Renaming of an exported name is unsupported" - (Just _, Nothing) -> throwError $ PluginInternalError "Explicit export list required for renaming" - _ -> pure () + case hsmodExports hsMod of + Nothing -> pure False + Just exports -> do + let exportedOccNames = getExportedOccNames exports + nameOcc = nameOccName name + pure $ nameOcc `elem` exportedOccNames + +-- | Extract all OccNames from an export list +getExportedOccNames :: + XRec GhcPs [LIE GhcPs] -> + [OccName] +getExportedOccNames exports = + concatMap extractFromExport (unLoc exports) + where + extractFromExport :: + LIE GhcPs -> + [OccName] + extractFromExport lie = case unLocA lie of +#if MIN_VERSION_ghc(9,10,0) + IEVar _ ieWrapped _ -> handle ieWrapped + IEThingAbs _ ieWrapped _ -> handle ieWrapped + IEThingAll _ ieWrapped _ -> handle ieWrapped + IEThingWith _ ieWrapped _ _ _ -> handle ieWrapped +#else + IEVar _ ieWrapped -> handle ieWrapped + IEThingAbs _ ieWrapped -> handle ieWrapped + IEThingAll _ ieWrapped -> handle ieWrapped + IEThingWith _ ieWrapped _ _ -> handle ieWrapped +#endif + IEModuleContents{} -> [] + _ -> [] + where + handle ieWrapped = maybeToList $ fmap rdrNameOcc $ unwrapIEWrappedName (unLoc ieWrapped) --------------------------------------------------------------------------------------------------- -- Source renaming @@ -162,10 +229,10 @@ getSrcEdit state verTxtDocId updatePs = do replaceRefs :: OccName -> HashSet Location -> + HashSet OccName -> ParsedSource -> ParsedSource -replaceRefs newName refs = everywhere $ - -- there has to be a better way... +replaceRefs newName refs oldOccNames = everywhere $ mkT (replaceLoc @AnnListItem) `extT` -- replaceLoc @AnnList `extT` -- not needed -- replaceLoc @AnnParen `extT` -- not needed @@ -176,15 +243,24 @@ replaceRefs newName refs = everywhere $ where replaceLoc :: forall an. LocatedAn an RdrName -> LocatedAn an RdrName replaceLoc (L srcSpan oldRdrName) - | isRef (locA srcSpan) = L srcSpan $ replace oldRdrName + | isRef (locA srcSpan) + , isTarget oldRdrName + = L srcSpan $ replace oldRdrName replaceLoc lOldRdrName = lOldRdrName + replace :: RdrName -> RdrName replace (Qual modName _) = Qual modName newName replace _ = Unqual newName isRef :: SrcSpan -> Bool - isRef = (`HS.member` refs) . unsafeSrcSpanToLoc - + isRef srcSpan = case srcSpanToLocation srcSpan of + Just loc -> loc `HS.member` refs + Nothing -> False + + -- Only replace RdrNames whose OccName matches a rename target, preventing + -- co-located field selectors from being incorrectly renamed. + isTarget :: RdrName -> Bool + isTarget rdrName = rdrNameOcc rdrName `HS.member` oldOccNames --------------------------------------------------------------------------------------------------- -- Reference finding @@ -195,29 +271,29 @@ refsAtName :: NormalizedFilePath -> Name -> ExceptT PluginError m [Location] -refsAtName state nfp name = do - ShakeExtras{withHieDb} <- liftIO $ runAction "Rename.HieDb" state getShakeExtras - ast <- handleGetHieAst state nfp - dbRefs <- case nameModule_maybe name of - Nothing -> pure [] - Just mod -> liftIO $ mapMaybe rowToLoc <$> withHieDb (\hieDb -> - -- See Note [Generated references] - filter (\(refRow HieDb.:. _) -> refIsGenerated refRow) <$> - findReferences - hieDb - True - (nameOccName name) - (Just $ moduleName mod) - (Just $ moduleUnit mod) - [fromNormalizedFilePath nfp] - ) - pure $ nameLocs name ast ++ dbRefs - -nameLocs :: Name -> HieAstResult -> [Location] -nameLocs name (HAR _ _ rm _ _) = - concatMap (map (realSrcSpanToLocation . fst)) - (M.lookup (Right name) rm) - +refsAtName state nfp targetName = do + HAR{refMap} <- handleGetHieAst state nfp + + let localRefs = + case M.lookup (Right targetName) refMap of + Nothing -> [] + Just spans -> [ realSrcSpanToLocation sp | (sp, _) <- spans] + + let defLoc = nameSrcSpan targetName + defLocations = case srcSpanToLocation defLoc of + Just loc | isGoodSrcSpan defLoc -> [loc] + _ -> [] + + -- Only query HieDb if it's a global name + globalRefs <- + case nameModule_maybe targetName of + Nothing -> pure [] + Just mod -> do + ShakeExtras{withHieDb} <- liftIO $ runAction "Rename.HieDb" state getShakeExtras + liftIO $ withHieDb $ \hieDb -> + fmap (mapMaybe rowToLoc) $ findReferences hieDb True (nameOccName targetName) (Just $ moduleName mod) (Just $ moduleUnit mod) [] + + pure (defLocations ++ localRefs ++ globalRefs) --------------------------------------------------------------------------------------------------- -- Util @@ -272,19 +348,62 @@ getNamesAtPoint' hf pos = locToUri :: Location -> Uri locToUri (Location uri _) = uri -unsafeSrcSpanToLoc :: SrcSpan -> Location -unsafeSrcSpanToLoc srcSpan = +srcSpanToLocE :: MonadError PluginError m => SrcSpan -> m Location +srcSpanToLocE srcSpan = case srcSpanToLocation srcSpan of - Nothing -> error "Invalid conversion from UnhelpfulSpan to Location" - Just location -> location + Nothing -> throwError $ PluginInternalError "Invalid SrcSpan conversion" + Just loc -> pure loc locToFilePos :: Monad m => Location -> ExceptT PluginError m (NormalizedFilePath, Position) locToFilePos (Location uri (Range pos _)) = (,pos) <$> getNormalizedFilePathE uri -replaceModName :: Name -> Maybe ModuleName -> Module -replaceModName name mbModName = - mkModule (moduleUnit $ nameModule name) (fromMaybe (mkModuleName "Main") mbModName) +-- | Collect locations of simple exported identifiers (IEVar / IEName). +-- Only supports variable exports; complex export forms are rejected. +exportNameLocs :: + ParsedModule -> + [Name] -> + ExceptT PluginError (HandlerM config) [Location] +exportNameLocs pm names = do + let hsMod = unLoc $ pm_parsed_source pm + case hsmodExports hsMod of + Nothing -> pure [] + Just exports -> + fmap concat $ forM (unLoc exports) $ \export -> + case unLocA export of +#if MIN_VERSION_ghc(9,10,0) + IEVar _ ieWrapped _ -> matchWrapped (getLoc export) ieWrapped +#else + IEVar _ ieWrapped -> matchWrapped (getLoc export) ieWrapped +#endif + IEThingAll{} -> unsupported + IEThingWith{} -> unsupported + IEModuleContents{} -> unsupported + IEThingAbs{} -> unsupported + IEGroup{} -> unsupported + IEDoc{} -> unsupported + IEDocNamed{} -> unsupported + where + unsupported = throwError $ PluginInternalError "Renaming is unsupported for complex export forms" + + matchWrapped :: SrcSpan -> LIEWrappedName GhcPs -> ExceptT PluginError (HandlerM config) [Location] + matchWrapped l ieWrapped = + case unwrapIEWrappedName (unLoc ieWrapped) of + Just rdr + | any (matchesRdr rdr) names + -> do + loc <- srcSpanToLocE l + pure [loc] + _ -> pure [] + + matchesRdr rdr name = occNameFS (rdrNameOcc rdr) == occNameFS (nameOccName name) + +-- | Extract a RdrName from an IEWrappedName when possible. +unwrapIEWrappedName :: IEWrappedName GhcPs -> Maybe RdrName +unwrapIEWrappedName ie = + case ie of + IEName _ (L _ rdr) -> Just rdr + _ -> Nothing --------------------------------------------------------------------------------------------------- -- Config diff --git a/plugins/hls-rename-plugin/test/Main.hs b/plugins/hls-rename-plugin/test/Main.hs index b935e6563f..644ed5a89e 100644 --- a/plugins/hls-rename-plugin/test/Main.hs +++ b/plugins/hls-rename-plugin/test/Main.hs @@ -111,12 +111,78 @@ tests = testGroup "Rename" -- Make sure renaming succeeds rename doc (Position 3 0) "foo'" + , goldenWithCrossModuleRename True "Cross Module (Declaration)" "CrossMaster" "CrossFunctionClient" (Position 2 2) "fooRenamed" + , goldenWithCrossModuleRename True "Cross Module (Referenced)" "CrossFunctionClient" "CrossMaster" (Position 4 11) "crossfooRenamed" + , goldenWithCrossModuleRename True "Cross Module Qualified (Declaration)" "CrossMaster" "CrossQualifiedClient" (Position 2 2) "newFoo" + , goldenWithCrossModuleRename True "Cross Module Qualified (Referenced)" "CrossQualifiedClient" "CrossMaster" (Position 4 22) "crossfooRenamed" + , goldenWithCrossModuleSession True "Cross Error : No export list" "CrossMasterTwo" "CrossFunctionClientTwo" $ \masterDoc _clientDoc -> do + let expectedError = TResponseError + (InR ErrorCodes_InvalidParams) + "rename: Invalid Params: Cannot rename symbol: module has no explicit export list and the symbol is referenced from other modules." + Nothing + + renameExpectError expectedError masterDoc (Position 2 0) "ImpossibleRename" + , goldenWithCrossModuleSession False "Cross Error : Cross Moduel disabled" "CrossMaster" "CrossFunctionError" $ \masterDoc _clientDoc -> do + let expectedError = TResponseError + (InR ErrorCodes_InternalError) + "rename: Internal Error: Cross-module rename is disabled." + Nothing + + renameExpectError expectedError masterDoc (Position 2 0) "ImpossibleRename" ] goldenWithRename :: TestName-> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree goldenWithRename title path act = goldenWithHaskellDoc (def { plugins = M.fromList [("rename", def { plcConfig = "crossModule" .= True })] }) - renamePlugin title testDataDir path "expected" "hs" act + renamePlugin title testDataDir path "expected" "hs" + $ \_ -> do + doc <- openDoc (path <.> "hs") "haskell" + _ <- getDocumentSymbols doc + _ <- waitForBuildQueue + act doc + +goldenWithCrossModuleRename :: Bool -> TestName -> FilePath -> FilePath -> Position -> String -> TestTree +goldenWithCrossModuleRename crossModuleEnabled title primaryFile secondaryFile pos newName = + goldenWithHaskellDoc + (def { plugins = M.fromList [("rename", def { plcConfig = "crossModule" .= crossModuleEnabled })] }) + renamePlugin + title + testDataDir + secondaryFile + "expected" + "hs" + $ \_ -> do + + primaryDoc <- openDoc (primaryFile <.> "hs") "haskell" + secondaryDoc <- openDoc (secondaryFile <.> "hs") "haskell" + + _ <- getDocumentSymbols primaryDoc + _ <- getDocumentSymbols secondaryDoc + _ <- waitForBuildQueue + + + rename primaryDoc pos newName + +goldenWithCrossModuleSession :: Bool -> TestName -> FilePath -> FilePath -> (TextDocumentIdentifier -> TextDocumentIdentifier -> Session ()) -> TestTree +goldenWithCrossModuleSession crossModuleEnabled title primaryFile secondaryFile action = + goldenWithHaskellDoc + (def { plugins = M.fromList [("rename", def { plcConfig = "crossModule" .= crossModuleEnabled })] }) + renamePlugin + title + testDataDir + secondaryFile + "expected" + "hs" + $ \_ -> do + + primaryDoc <- openDoc (primaryFile <.> "hs") "haskell" + secondaryDoc <- openDoc (secondaryFile <.> "hs") "haskell" + + _ <- getDocumentSymbols primaryDoc + _ <- getDocumentSymbols secondaryDoc + _ <- waitForBuildQueue + + action primaryDoc secondaryDoc renameExpectError :: TResponseError Method_TextDocumentRename -> TextDocumentIdentifier -> Position -> Text -> Session () renameExpectError expectedError doc pos newName = do diff --git a/plugins/hls-rename-plugin/test/testdata/CrossFunctionClient.expected.hs b/plugins/hls-rename-plugin/test/testdata/CrossFunctionClient.expected.hs new file mode 100644 index 0000000000..a7ac84bca3 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/CrossFunctionClient.expected.hs @@ -0,0 +1,5 @@ +module CrossFunctionClient where + +import CrossMaster + +bar = fooRenamed diff --git a/plugins/hls-rename-plugin/test/testdata/CrossFunctionClient.hs b/plugins/hls-rename-plugin/test/testdata/CrossFunctionClient.hs new file mode 100644 index 0000000000..d9d1ce8bb8 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/CrossFunctionClient.hs @@ -0,0 +1,5 @@ +module CrossFunctionClient where + +import CrossMaster + +bar = crossfoo diff --git a/plugins/hls-rename-plugin/test/testdata/CrossFunctionClientTwo.expected.hs b/plugins/hls-rename-plugin/test/testdata/CrossFunctionClientTwo.expected.hs new file mode 100644 index 0000000000..234d178d5e --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/CrossFunctionClientTwo.expected.hs @@ -0,0 +1,5 @@ +module CrossFunctionClientTwo where + +import CrossMasterTwo + +bar = crossfooTwo diff --git a/plugins/hls-rename-plugin/test/testdata/CrossFunctionClientTwo.hs b/plugins/hls-rename-plugin/test/testdata/CrossFunctionClientTwo.hs new file mode 100644 index 0000000000..234d178d5e --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/CrossFunctionClientTwo.hs @@ -0,0 +1,5 @@ +module CrossFunctionClientTwo where + +import CrossMasterTwo + +bar = crossfooTwo diff --git a/plugins/hls-rename-plugin/test/testdata/CrossFunctionError.expected.hs b/plugins/hls-rename-plugin/test/testdata/CrossFunctionError.expected.hs new file mode 100644 index 0000000000..bd1964231e --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/CrossFunctionError.expected.hs @@ -0,0 +1,5 @@ +module CrossFunctionError where + +import CrossMaster + +bar = crossfoo diff --git a/plugins/hls-rename-plugin/test/testdata/CrossFunctionError.hs b/plugins/hls-rename-plugin/test/testdata/CrossFunctionError.hs new file mode 100644 index 0000000000..bd1964231e --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/CrossFunctionError.hs @@ -0,0 +1,5 @@ +module CrossFunctionError where + +import CrossMaster + +bar = crossfoo diff --git a/plugins/hls-rename-plugin/test/testdata/CrossMaster.expected.hs b/plugins/hls-rename-plugin/test/testdata/CrossMaster.expected.hs new file mode 100644 index 0000000000..9e2f6f13e3 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/CrossMaster.expected.hs @@ -0,0 +1,4 @@ +module CrossMaster (crossfooRenamed) where + +crossfooRenamed :: Int +crossfooRenamed = 42 diff --git a/plugins/hls-rename-plugin/test/testdata/CrossMaster.hs b/plugins/hls-rename-plugin/test/testdata/CrossMaster.hs new file mode 100644 index 0000000000..466d4a6cf5 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/CrossMaster.hs @@ -0,0 +1,4 @@ +module CrossMaster (crossfoo) where + +crossfoo :: Int +crossfoo = 42 diff --git a/plugins/hls-rename-plugin/test/testdata/CrossMasterTwo.hs b/plugins/hls-rename-plugin/test/testdata/CrossMasterTwo.hs new file mode 100644 index 0000000000..30cce07ee9 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/CrossMasterTwo.hs @@ -0,0 +1,4 @@ +module CrossMasterTwo where + +crossfooTwo :: Int +crossfooTwo = 42 diff --git a/plugins/hls-rename-plugin/test/testdata/CrossQualifiedClient.expected.hs b/plugins/hls-rename-plugin/test/testdata/CrossQualifiedClient.expected.hs new file mode 100644 index 0000000000..9c1b13e4b8 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/CrossQualifiedClient.expected.hs @@ -0,0 +1,9 @@ +module CrossQualified where + +import qualified CrossMaster + +bar = let crossfoo = CrossMaster.newFoo in + crossfoo * crossfoo + +crossfoo :: Int +crossfoo = 7 diff --git a/plugins/hls-rename-plugin/test/testdata/CrossQualifiedClient.hs b/plugins/hls-rename-plugin/test/testdata/CrossQualifiedClient.hs new file mode 100644 index 0000000000..cc037a2bae --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/CrossQualifiedClient.hs @@ -0,0 +1,9 @@ +module CrossQualified where + +import qualified CrossMaster + +bar = let crossfoo = CrossMaster.crossfoo in + crossfoo * crossfoo + +crossfoo :: Int +crossfoo = 7 diff --git a/plugins/hls-rename-plugin/test/testdata/hie.yaml b/plugins/hls-rename-plugin/test/testdata/hie.yaml index 892a7d675f..4a16a0925d 100644 --- a/plugins/hls-rename-plugin/test/testdata/hie.yaml +++ b/plugins/hls-rename-plugin/test/testdata/hie.yaml @@ -1,6 +1,12 @@ cradle: direct: arguments: + - "CrossFunctionClient" + - "CrossFunctionClientTwo" + - "CrossFunctionError" + - "CrossMaster" + - "CrossMasterTwo" + - "CrossQualified" - "DataConstructor" - "ExportedFunction" - "FieldPuns"