diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 1f2d478335..9faa7ab4e4 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -804,6 +804,10 @@ suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_ = mod_srcspan >>= uncurry (suggestions hsmodImports binding) | otherwise = [] where + canUseDatacon = case extractNotInScopeName _message of + Just NotInScopeTypeConstructorOrClass{} -> False + _ -> True + suggestions decls binding mod srcspan | range <- case [ x | (x,"") <- readSrcSpan (T.unpack srcspan)] of [s] -> let x = realSrcSpanToRange s @@ -823,7 +827,7 @@ suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_ -- Only for the situation that data constructor name is same as type constructor name, -- let ident with parent be in front of the one without. , sortedMatch <- sortBy (\ident1 ident2 -> parent ident2 `compare` parent ident1) (Set.toList match) - , idents <- filter (\ident -> moduleNameText ident == mod) sortedMatch + , idents <- filter (\ident -> moduleNameText ident == mod && (canUseDatacon || not (isDatacon ident))) sortedMatch , (not . null) idents -- Ensure fallback while `idents` is empty , ident <- head idents = Just ident @@ -1318,8 +1322,9 @@ hideImplicitPreludeSymbol :: T.Text -> NewImport hideImplicitPreludeSymbol symbol = newUnqualImport "Prelude" symbol True canUseIdent :: NotInScope -> IdentInfo -> Bool -canUseIdent NotInScopeDataConstructor{} = isDatacon -canUseIdent _ = const True +canUseIdent NotInScopeDataConstructor{} = isDatacon +canUseIdent NotInScopeTypeConstructorOrClass{} = not . isDatacon +canUseIdent _ = const True data NotInScope = NotInScopeDataConstructor T.Text diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 8d3cb7d155..d04d6b9e30 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -730,7 +730,7 @@ codeActionTests = testGroup "code actions" , typeWildCardActionTests , removeImportTests , extendImportTests - , suggesImportClassMethodTests + , suggestImportClassMethodTests , suggestImportTests , suggestHideShadowTests , suggestImportDisambiguationTests @@ -1436,6 +1436,25 @@ extendImportTests = testGroup "extend import actions" , "f :: Foo" , "f = Foo 1" ]) + , testSession "type constructor name same as data constructor name, data constructor extraneous" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "data Foo = Foo" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA()" + , "f :: Foo" + , "f = undefined" + ]) + (Range (Position 2 4) (Position 2 6)) + ["Add Foo to the import list of ModuleA"] + (T.unlines + [ "module ModuleB where" + , "import ModuleA(Foo)" + , "f :: Foo" + , "f = undefined" + ]) ] where codeActionTitle CodeAction{_title=x} = x @@ -1486,8 +1505,8 @@ extendImportTestsRegEx = testGroup "regex parsing" template message expected = do liftIO $ matchRegExMultipleImports message @=? expected -suggesImportClassMethodTests :: TestTree -suggesImportClassMethodTests = +suggestImportClassMethodTests :: TestTree +suggestImportClassMethodTests = testGroup "suggest import class methods" [ testGroup @@ -1566,6 +1585,8 @@ suggestImportTests = testGroup "suggest import actions" , test False [] "f = quickCheck" [] "import Test.QuickCheck (quickCheck)" -- don't omit the parent data type of a constructor , test False [] "f ExitSuccess = ()" [] "import System.Exit (ExitSuccess)" + -- don't suggest data constructor when we only need the type + , test False [] "f :: Bar" [] "import Bar (Bar(Bar))" ] , testGroup "want suggestion" [ wantWait [] "f = foo" [] "import Foo (foo)"