Skip to content

Commit 2a67821

Browse files
authored
Avoid file path normalization in moduleImportPath (#152)
This fixes some issues where we used an uppercase drive letter in the import path even though the LSP client uses lowercase drive letters
1 parent 726af7f commit 2a67821

File tree

4 files changed

+57
-21
lines changed

4 files changed

+57
-21
lines changed

src/Development/IDE/Core/Compile.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,9 @@ upgradeWarningToError (nfp, fd) =
159159
warn2err :: T.Text -> T.Text
160160
warn2err = T.intercalate ": error:" . T.splitOn ": warning:"
161161

162-
addRelativeImport :: ParsedModule -> DynFlags -> DynFlags
163-
addRelativeImport modu dflags = dflags
164-
{importPaths = nubOrd $ maybeToList (moduleImportPath modu) ++ importPaths dflags}
162+
addRelativeImport :: NormalizedFilePath -> ParsedModule -> DynFlags -> DynFlags
163+
addRelativeImport fp modu dflags = dflags
164+
{importPaths = nubOrd $ maybeToList (moduleImportPath fp modu) ++ importPaths dflags}
165165

166166
mkTcModuleResult
167167
:: GhcMonad m

src/Development/IDE/Core/Rules.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ getLocatedImportsRule =
153153
let ms = pm_mod_summary pm
154154
let imports = [(False, imp) | imp <- ms_textual_imps ms] ++ [(True, imp) | imp <- ms_srcimps ms]
155155
env <- hscEnv <$> use_ GhcSession file
156-
let dflags = addRelativeImport pm $ hsc_dflags env
156+
let dflags = addRelativeImport file pm $ hsc_dflags env
157157
opt <- getIdeOptions
158158
(diags, imports') <- fmap unzip $ forM imports $ \(isSource, (mbPkgName, modName)) -> do
159159
diagOrImp <- locateModule dflags (optExtensions opt) getFileExists modName mbPkgName isSource

src/Development/IDE/GHC/Util.hs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ import qualified Data.Text as T
3939
import StringBuffer
4040
import System.FilePath
4141

42+
import Development.IDE.Types.Location
43+
4244

4345
----------------------------------------------------------------------
4446
-- GHC setup
@@ -103,26 +105,22 @@ fakeDynFlags = defaultDynFlags settings mempty
103105
, pc_WORD_SIZE=8
104106
}
105107

106-
moduleImportPath :: GHC.ParsedModule -> Maybe FilePath
107-
moduleImportPath pm
108-
| rootModDir == "." = Just rootPathDir
109-
| otherwise = do
110-
dir <- dropTrailingPathSeparator <$> stripSuffix (normalise rootModDir) (normalise rootPathDir)
111-
-- For modules with more than one component, this can be empty, e.g.,
112-
-- stripSuffix (normalise ./A) (normalise ./A) for A/B.daml.
113-
-- We make a best effort attemp at not duplicating file paths
114-
-- by mapping the current directory to '.' if 'rootPathDir' starts with '.' and
115-
-- to an empty string otherwise.
116-
pure $! if null dir then dotDir else dir
108+
moduleImportPath :: NormalizedFilePath -> GHC.ParsedModule -> Maybe FilePath
109+
-- The call to takeDirectory is required since DAML does not require that
110+
-- the file name matches the module name in the last component.
111+
-- Once that has changed we can get rid of this.
112+
moduleImportPath (takeDirectory . fromNormalizedFilePath -> pathDir) pm
113+
-- This happens for single-component modules since takeDirectory "A" == "."
114+
| modDir == "." = Just pathDir
115+
| otherwise = dropTrailingPathSeparator <$> stripSuffix modDir pathDir
117116
where
118-
dotDir = if "." `isPrefixOf` rootPathDir then "." else ""
119117
ms = GHC.pm_mod_summary pm
120-
file = GHC.ms_hspp_file ms
121118
mod' = GHC.ms_mod ms
122-
-- ./src/A for file ./src/A/B.daml
123-
rootPathDir = takeDirectory file
124119
-- A for module A.B
125-
rootModDir = takeDirectory . moduleNameSlashes . GHC.moduleName $ mod'
120+
modDir =
121+
takeDirectory $
122+
fromNormalizedFilePath $ toNormalizedFilePath $
123+
moduleNameSlashes $ GHC.moduleName mod'
126124

127125
-- | An HscEnv with equality.
128126
data HscEnvEq = HscEnvEq Unique HscEnv

test/exe/Main.hs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@
77

88
module Main (main) where
99

10-
import Control.Monad (void)
10+
import Control.Applicative.Combinators
11+
import Control.Monad
1112
import Control.Monad.IO.Class (liftIO)
13+
import Data.Char (toLower)
14+
import Data.Foldable
1215
import qualified Data.Text as T
1316
import Development.IDE.Test
1417
import Development.IDE.Test.Runfiles
1518
import Language.Haskell.LSP.Test
1619
import Language.Haskell.LSP.Types
1720
import Language.Haskell.LSP.Types.Capabilities
1821
import System.Environment.Blank (setEnv)
22+
import System.FilePath
1923
import System.IO.Extra
2024
import System.Directory
2125
import Test.Tasty
@@ -329,6 +333,40 @@ diagnosticTests = testGroup "diagnostics"
329333
]
330334
)
331335
]
336+
, testSessionWait "lower-case drive" $ do
337+
let aContent = T.unlines
338+
[ "module A.A where"
339+
, "import A.B ()"
340+
]
341+
bContent = T.unlines
342+
[ "{-# OPTIONS_GHC -Wall #-}"
343+
, "module A.B where"
344+
, "import Data.List"
345+
]
346+
uriB <- getDocUri "A/B.hs"
347+
Just pathB <- pure $ uriToFilePath uriB
348+
uriB <- pure $
349+
let (drive, suffix) = splitDrive pathB
350+
in filePathToUri (joinDrive (map toLower drive ) suffix)
351+
liftIO $ createDirectoryIfMissing True (takeDirectory pathB)
352+
liftIO $ writeFileUTF8 pathB $ T.unpack bContent
353+
uriA <- getDocUri "A/A.hs"
354+
Just pathA <- pure $ uriToFilePath uriA
355+
uriA <- pure $
356+
let (drive, suffix) = splitDrive pathA
357+
in filePathToUri (joinDrive (map toLower drive ) suffix)
358+
let itemA = TextDocumentItem uriA "haskell" 0 aContent
359+
let a = TextDocumentIdentifier uriA
360+
sendNotification TextDocumentDidOpen (DidOpenTextDocumentParams itemA)
361+
diagsNot <- skipManyTill anyMessage message :: Session PublishDiagnosticsNotification
362+
let PublishDiagnosticsParams fileUri diags = _params (diagsNot :: PublishDiagnosticsNotification)
363+
-- Check that if we put a lower-case drive in for A.A
364+
-- the diagnostics for A.B will also be lower-case.
365+
liftIO $ fileUri @?= uriB
366+
let msg = _message (head (toList diags) :: Diagnostic)
367+
liftIO $ unless ("redundant" `T.isInfixOf` msg) $
368+
assertFailure ("Expected redundant import but got " <> T.unpack msg)
369+
closeDoc a
332370
]
333371

334372
codeActionTests :: TestTree

0 commit comments

Comments
 (0)