on master help sign in
#279, support preprocessors (#282)

* Support preprocessors * Add a preprocessor for testing * Add a preprocessor test

commit70cb92cc013029f387d2213fa599b3e066e2c6a6 authorNeil Mitchell <ndmitchell@gmail.com> dateThu 19 Dec 2019, 11:06 (7 years ago) parentb1435e2
4 files changed+63−4
ghcide.cabal+10−1 src/Development/IDE/Core/Preprocessor.hs+27−3 test/exe/Main.hs+16 test/preprocessor/Main.hs+10
ghcide.cabal+10−1
@@ -131,6 +131,14 @@ library
131131 Development.IDE.Spans.Type
132132 ghc-options: -Wall -Wno-name-shadowing
133133
134+executable ghcide-test-preprocessor
135+ default-language: Haskell2010
136+ hs-source-dirs: test/preprocessor
137+ ghc-options: -Wall
138+ main-is: Main.hs
139+ build-depends:
140+ base == 4.*
141+
134142executable ghcide
135143 if flag(ghc-lib)
136144 buildable: False
@@ -169,7 +177,8 @@ test-suite ghcide-tests
169177 type: exitcode-stdio-1.0
170178 default-language: Haskell2010
171179 build-tool-depends:
172− ghcide:ghcide
180+ ghcide:ghcide,
181+ ghcide:ghcide-test-preprocessor
173182 build-depends:
174183 base,
175184 bytestring,
src/Development/IDE/Core/Preprocessor.hs+27−3
@@ -20,7 +20,7 @@ import DynFlags
2020import qualified HeaderInfo as Hdr
2121import Development.IDE.Types.Diagnostics
2222import Development.IDE.GHC.Error
23−import SysTools (Option (..), runUnlit)
23+import SysTools (Option (..), runUnlit, runPp)
2424import Control.Monad.Trans.Except
2525import qualified GHC.LanguageExtensions as LangExt
2626import Data.Maybe
@@ -43,10 +43,19 @@ preprocessor filename mbContents = do
4343
4444 -- Perform cpp
4545 dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
46− if not $ xopt LangExt.Cpp dflags then
46+ (isOnDisk, contents, dflags) <-
47+ if not $ xopt LangExt.Cpp dflags then
48+ return (isOnDisk, contents, dflags)
49+ else do
50+ contents <- liftIO $ runCpp dflags filename $ if isOnDisk then Nothing else Just contents
51+ dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
52+ return (False, contents, dflags)
53+
54+ -- Perform preprocessor
55+ if not $ gopt Opt_Pp dflags then
4756 return (contents, dflags)
4857 else do
49− contents <- liftIO $ runCpp dflags filename $ if isOnDisk then Nothing else Just contents
58+ contents <- liftIO $ runPreprocessor dflags filename $ if isOnDisk then Nothing else Just contents
5059 dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
5160 return (contents, dflags)
5261
@@ -132,3 +141,18 @@ runCpp dflags filename contents = withTempDir $ \dir -> do
132141 = "# " <> num <> " \"" <> map (\x -> if isPathSeparator x then '/' else x) filename <> "\""
133142 | otherwise = x
134143 stringToStringBuffer . unlines . map tweak . lines <$> readFileUTF8' out
144+
145+
146+-- | Run a preprocessor on a file
147+runPreprocessor :: DynFlags -> FilePath -> Maybe SB.StringBuffer -> IO SB.StringBuffer
148+runPreprocessor dflags filename contents = withTempDir $ \dir -> do
149+ let out = dir </> takeFileName filename <.> "out"
150+ inp <- case contents of
151+ Nothing -> return filename
152+ Just contents -> do
153+ let inp = dir </> takeFileName filename <.> "hs"
154+ withBinaryFile inp WriteMode $ \h ->
155+ hPutStringBuffer h contents
156+ return inp
157+ runPp dflags [SysTools.Option filename, SysTools.Option inp, SysTools.FileOption "" out]
158+ SB.hGetStringBuffer out
test/exe/Main.hs+16
@@ -42,6 +42,7 @@ main = defaultMain $ testGroup "HIE"
4242 , codeLensesTests
4343 , findDefinitionAndHoverTests
4444 , pluginTests
45+ , preprocessorTests
4546 , thTests
4647 ]
4748
@@ -914,6 +915,21 @@ pluginTests = testSessionWait "plugins" $ do
914915 )
915916 ]
916917
918+preprocessorTests :: TestTree
919+preprocessorTests = testSessionWait "preprocessor" $ do
920+ let content =
921+ T.unlines
922+ [ "{-# OPTIONS_GHC -F -pgmF=ghcide-test-preprocessor #-}"
923+ , "module Testing where"
924+ , "y = x + z" -- plugin replaces x with y, making this have only one diagnostic
925+ ]
926+ _ <- openDoc' "Testing.hs" "haskell" content
927+ expectDiagnostics
928+ [ ( "Testing.hs",
929+ [(DsError, (2, 8), "Variable not in scope: z")]
930+ )
931+ ]
932+
917933thTests :: TestTree
918934thTests =
919935 testGroup
test/preprocessor/Main.hsnew file+10
@@ -0,0 +1,10 @@
1+
2+module Main(main) where
3+
4+import System.Environment
5+
6+main :: IO ()
7+main = do
8+ _:input:output:_ <- getArgs
9+ let f = map (\x -> if x == 'x' then 'y' else x)
10+ writeFile output . f =<< readFile input