-
-
Notifications
You must be signed in to change notification settings - Fork 392
Add cradle dependencies to session loading errors #3779
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
de5cb26
e875dc6
7f7a9b9
9adea75
c758ee5
7004b69
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 |
---|---|---|
@@ -0,0 +1,106 @@ | ||
{-# LANGUAGE DeriveAnyClass #-} | ||
{-# LANGUAGE DeriveGeneric #-} | ||
|
||
module Development.IDE.Session.Diagnostics where | ||
import Control.Applicative | ||
import Control.Monad | ||
import qualified Data.Aeson as Aeson | ||
import Data.List | ||
import Data.List.Extra (split) | ||
import Data.Maybe | ||
import qualified Data.Text as T | ||
import Development.IDE.Types.Diagnostics | ||
import Development.IDE.Types.Location | ||
import GHC.Generics | ||
import qualified HIE.Bios.Cradle as HieBios | ||
import HIE.Bios.Types hiding (Log) | ||
import System.FilePath | ||
|
||
data CradleErrorDetails = | ||
CradleErrorDetails | ||
{ cabalProjectFiles :: [FilePath] | ||
-- ^ files related to the cradle error | ||
-- i.e. .cabal, cabal.project, etc. | ||
} deriving (Show, Eq, Ord, Read, Generic, Aeson.ToJSON, Aeson.FromJSON) | ||
|
||
{- | Takes a cradle error, the corresponding cradle and the file path where | ||
the cradle error occurred (of the file we attempted to load). | ||
Depicts the cradle error in a user-friendly way. | ||
-} | ||
renderCradleError :: CradleError -> Cradle a -> NormalizedFilePath -> FileDiagnostic | ||
renderCradleError (CradleError deps _ec ms) cradle nfp | ||
| HieBios.isCabalCradle cradle = | ||
let (fp, showDiag, diag) = ideErrorWithSource (Just "cradle") (Just DiagnosticSeverity_Error) nfp $ T.unlines $ map T.pack userFriendlyMessage in | ||
(fp, showDiag, diag{_data_ = Just $ Aeson.toJSON CradleErrorDetails{cabalProjectFiles=absDeps}}) | ||
| otherwise = ideErrorWithSource (Just "cradle") (Just DiagnosticSeverity_Error) nfp $ T.unlines $ map T.pack userFriendlyMessage | ||
where | ||
absDeps = fmap (cradleRootDir cradle </>) deps | ||
userFriendlyMessage :: [String] | ||
userFriendlyMessage | ||
| HieBios.isCabalCradle cradle = fromMaybe ms $ fileMissingMessage <|> mkUnknownModuleMessage | ||
| otherwise = ms | ||
|
||
mkUnknownModuleMessage :: Maybe [String] | ||
mkUnknownModuleMessage | ||
| any (isInfixOf "Failed extracting script block:") ms = | ||
Just $ unknownModuleMessage (fromNormalizedFilePath nfp) | ||
| otherwise = Nothing | ||
|
||
fileMissingMessage :: Maybe [String] | ||
fileMissingMessage = | ||
multiCradleErrMessage <$> parseMultiCradleErr ms | ||
|
||
-- | Information included in Multi Cradle error messages | ||
data MultiCradleErr = MultiCradleErr | ||
{ mcPwd :: FilePath | ||
, mcFilePath :: FilePath | ||
, mcPrefixes :: [(FilePath, String)] | ||
} deriving (Show) | ||
|
||
-- | Attempt to parse a multi-cradle message | ||
parseMultiCradleErr :: [String] -> Maybe MultiCradleErr | ||
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. Remind me again why we aren't just doing structured errors from 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. No reason :) Ill put it on my long term list of things to do after the release is finished. |
||
parseMultiCradleErr ms = do | ||
_ <- lineAfter "Multi Cradle: " | ||
wd <- lineAfter "pwd: " | ||
fp <- lineAfter "filepath: " | ||
ps <- prefixes | ||
pure $ MultiCradleErr wd fp ps | ||
|
||
where | ||
lineAfter :: String -> Maybe String | ||
lineAfter pre = listToMaybe $ mapMaybe (stripPrefix pre) ms | ||
|
||
prefixes :: Maybe [(FilePath, String)] | ||
prefixes = do | ||
pure $ mapMaybe tuple ms | ||
|
||
tuple :: String -> Maybe (String, String) | ||
tuple line = do | ||
line' <- surround '(' line ')' | ||
[f, s] <- pure $ split (==',') line' | ||
pure (f, s) | ||
|
||
-- extracts the string surrounded by required characters | ||
surround :: Char -> String -> Char -> Maybe String | ||
surround start s end = do | ||
guard (listToMaybe s == Just start) | ||
guard (listToMaybe (reverse s) == Just end) | ||
pure $ drop 1 $ take (length s - 1) s | ||
|
||
multiCradleErrMessage :: MultiCradleErr -> [String] | ||
multiCradleErrMessage e = | ||
unknownModuleMessage (mcFilePath e) | ||
<> [""] | ||
<> map prefix (mcPrefixes e) | ||
where | ||
prefix (f, r) = f <> " - " <> r | ||
|
||
unknownModuleMessage :: String -> [String] | ||
unknownModuleMessage moduleFileName = | ||
[ "Loading the module '" <> moduleFileName <> "' failed." | ||
, "" | ||
, "It may not be listed in your .cabal file!" | ||
, "Perhaps you need to add `"<> dropExtension (takeFileName moduleFileName) <> "` to other-modules or exposed-modules." | ||
, "" | ||
, "For more information, visit: https://cabal.readthedocs.io/en/3.4/developing-packages.html#modules-included-in-the-package" | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,37 +2,27 @@ | |
|
||
module FunctionalBadProject (tests) where | ||
|
||
-- import Control.Lens hiding (List) | ||
-- import Control.Monad.IO.Class | ||
-- import qualified Data.Text as T | ||
-- import Language.LSP.Test hiding (message) | ||
-- import Language.LSP.Types as LSP | ||
-- import Language.LSP.Types.Lens as LSP hiding (contents, error ) | ||
import Control.Lens | ||
import qualified Data.Text as T | ||
import qualified Language.LSP.Protocol.Lens as L | ||
import Test.Hls | ||
import Test.Hls.Command | ||
|
||
|
||
-- --------------------------------------------------------------------- | ||
-- TODO: Currently this can not succeed, since such an error is thrown in "runActionWithContext" which | ||
-- can produce diagnostics at the moment. Needs more investigation | ||
-- TODO: @fendor: Add issue link here | ||
-- | ||
tests :: TestTree | ||
tests = testGroup "behaviour on malformed projects" [ | ||
testCase "no test executed" $ True @?= True | ||
tests = testGroup "behaviour on malformed projects" | ||
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. This is much more sophisticated than what I was thinking (although I imagine we'll want it anyway). I was just thinking of a unit test for the string matching function! 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. Does that mean we don't want it? 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. IDK, potentially good to have both? 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. I think the tests are there and we shouldn't have them forever, any way. Ideally cabal gives us better error messages and hie-bios doesn't demand the parsing. Should be good enough, imo. |
||
[ testCase "Missing module diagnostic" $ do | ||
runSession hlsCommand fullCaps "test/testdata/missingModuleTest/missingModule/" $ do | ||
doc <- openDoc "src/MyLib.hs" "haskell" | ||
[diag] <- waitForDiagnosticsFrom doc | ||
liftIO $ assertBool "missing module name" $ "MyLib" `T.isInfixOf` (diag ^. L.message) | ||
liftIO $ assertBool "module missing context" $ "may not be listed" `T.isInfixOf` (diag ^. L.message) | ||
, testCase "Missing module diagnostic - no matching prefix" $ do | ||
runSession hlsCommand fullCaps "test/testdata/missingModuleTest/noPrefixMatch/" $ do | ||
doc <- openDoc "app/Other.hs" "haskell" | ||
[diag] <- waitForDiagnosticsFrom doc | ||
liftIO $ assertBool "missing module name" $ | ||
"Other" `T.isInfixOf` (diag ^. L.message) | ||
liftIO $ assertBool "hie-bios message" $ | ||
"Cabal {component = Just \"exe:testExe\"}" `T.isInfixOf` (diag ^. L.message) | ||
] | ||
|
||
-- testCase "deals with cabal file with unsatisfiable dependency" $ | ||
-- runSession hlsCommandExamplePlugin codeActionSupportCaps "test/testdata/badProjects/cabal" $ do | ||
-- _doc <- openDoc "Foo.hs" "haskell" | ||
|
||
-- diags@(d:_) <- waitForDiagnosticsSource "bios" | ||
-- -- liftIO $ show diags @?= "" | ||
-- -- liftIO $ putStrLn $ show diags | ||
-- -- liftIO $ putStrLn "a" | ||
-- liftIO $ do | ||
-- length diags @?= 1 | ||
-- d ^. range @?= Range (Position 0 0) (Position 1 0) | ||
-- d ^. severity @?= (Just DsError) | ||
-- d ^. code @?= Nothing | ||
-- d ^. source @?= Just "bios" | ||
-- d ^. message @?= | ||
-- (T.pack "readCreateProcess: stack \"build\" \"--only-configure\" \".\" (exit 1): failed\n") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
packages: ./ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
cradle: | ||
cabal: |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
cabal-version: 3.4 | ||
name: missingModule | ||
version: 0.1.0.0 | ||
build-type: Simple | ||
|
||
library | ||
hs-source-dirs: ./src/ | ||
exposed-modules: | ||
build-depends: base | ||
default-language: Haskell2010 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module MyLib where | ||
|
||
someFunc :: IO () | ||
someFunc = do | ||
putStrLn "someFunc" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
main :: IO () | ||
main = do | ||
putStrLn "someFunc" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module Other where |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
packages: ./ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
cradle: | ||
cabal: | ||
- path: ./app/Main.hs | ||
component: exe:testExe |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
cabal-version: 3.4 | ||
name: noPrefixMatch | ||
version: 0.1.0.0 | ||
build-type: Simple | ||
|
||
executable testExe | ||
main-is: Main.hs | ||
hs-source-dirs: app | ||
build-depends: base |
Uh oh!
There was an error while loading. Please reload this page.