mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 09:17:43 +03:00
Separate diagnostics from rule results (#1423)
This commit is contained in:
parent
34d8d705c7
commit
330ede08b5
13
WORKSPACE
13
WORKSPACE
@ -444,9 +444,9 @@ load("@bazel_skylib//lib:dicts.bzl", "dicts")
|
||||
# For the time being we build with GMP. See https://github.com/digital-asset/daml/issues/106
|
||||
use_integer_simple = not is_windows
|
||||
|
||||
HASKELL_LSP_COMMIT = "6e34a2193464dd49b09e223111c57a39c44fb1c7"
|
||||
HASKELL_LSP_COMMIT = "4dfbe392ab235048bb42991a5e259ff5f317244f"
|
||||
|
||||
HASKELL_LSP_HASH = "5804143e94ede0f99962007f94e7f84268f220c05ac3aeae3dda3cdc186d077f"
|
||||
HASKELL_LSP_HASH = "164311a83a7fc8a733c71354b87e3e6425afbbbba033a64f38e88b81c32c1fbd"
|
||||
|
||||
hazel_repositories(
|
||||
core_packages = dicts.add(
|
||||
@ -508,13 +508,16 @@ hazel_repositories(
|
||||
hazel_hackage("terminal-progress-bar", "0.4.0.1", "c5a9720fcbcd9d83f9551e431ee3975c61d7da6432aa687aef0c0e04e59ae277") +
|
||||
hazel_hackage("rope-utf16-splay", "0.3.1.0", "cbf878098355441ed7be445466fcb72d45390073a298b37649d762de2a7f8cc6") +
|
||||
hazel_hackage("unix-compat", "0.5.1", "a39d0c79dd906763770b80ba5b6c5cb710e954f894350e9917de0d73f3a19c52") +
|
||||
# This is a special version of Haskell LSP without GPL dependencies
|
||||
hazel_github(
|
||||
# This is a version of haskell-lsp that has not yet been released and contains
|
||||
# a few fixes for the uri <-> filepath conversion.
|
||||
hazel_github_external(
|
||||
"alanz",
|
||||
"haskell-lsp",
|
||||
HASKELL_LSP_COMMIT,
|
||||
HASKELL_LSP_HASH,
|
||||
) +
|
||||
hazel_github(
|
||||
hazel_github_external(
|
||||
"alanz",
|
||||
"haskell-lsp",
|
||||
HASKELL_LSP_COMMIT,
|
||||
HASKELL_LSP_HASH,
|
||||
|
@ -21,6 +21,7 @@ depends = [
|
||||
"mtl",
|
||||
"pretty",
|
||||
"safe-exceptions",
|
||||
"sorted-list",
|
||||
"shake",
|
||||
"stm",
|
||||
"syb",
|
||||
|
@ -42,10 +42,6 @@ newtype GlobalDirtyFiles = GlobalDirtyFiles (Var DirtyFiles)
|
||||
instance IsIdeGlobal GlobalDirtyFiles
|
||||
|
||||
|
||||
|
||||
-- | Get the modification time of a file.
|
||||
type instance RuleResult GetModificationTime = UTCTime
|
||||
|
||||
-- | Get the contents of a file, either dirty (if the buffer is modified) or from disk.
|
||||
type instance RuleResult GetFileContents = (UTCTime, StringBuffer)
|
||||
|
||||
@ -58,11 +54,6 @@ data GetFileExists = GetFileExists
|
||||
instance Hashable GetFileExists
|
||||
instance NFData GetFileExists
|
||||
|
||||
data GetModificationTime = GetModificationTime
|
||||
deriving (Eq, Show, Generic)
|
||||
instance Hashable GetModificationTime
|
||||
instance NFData GetModificationTime
|
||||
|
||||
data GetFileContents = GetFileContents
|
||||
deriving (Eq, Show, Generic)
|
||||
instance Hashable GetFileContents
|
||||
|
@ -24,7 +24,7 @@
|
||||
-- useStale.
|
||||
module Development.IDE.State.Shake(
|
||||
IdeState,
|
||||
IdeRule, IdeResult,
|
||||
IdeRule, IdeResult, GetModificationTime(..),
|
||||
shakeOpen, shakeShut,
|
||||
shakeRun,
|
||||
shakeProfile,
|
||||
@ -55,19 +55,18 @@ import Data.List.Extra
|
||||
import qualified Data.Text as T
|
||||
import Development.IDE.Logger as Logger
|
||||
import Development.IDE.Types.LSP
|
||||
import Development.IDE.Types.Diagnostics
|
||||
import Development.IDE.Types.Diagnostics hiding (getAllDiagnostics)
|
||||
import qualified Development.IDE.Types.Diagnostics as D
|
||||
import Control.Concurrent.Extra
|
||||
import Control.Exception
|
||||
import Control.DeepSeq
|
||||
import System.Time.Extra
|
||||
import Data.Typeable
|
||||
import Data.Tuple.Extra
|
||||
import System.Directory
|
||||
import System.FilePath
|
||||
import System.FilePath hiding (makeRelative)
|
||||
import qualified Development.Shake as Shake
|
||||
import Control.Monad.Extra
|
||||
import qualified Data.Set as Set
|
||||
import Data.Time
|
||||
import GHC.Generics
|
||||
import System.IO.Unsafe
|
||||
import Numeric.Extra
|
||||
|
||||
@ -79,6 +78,7 @@ data ShakeExtras = ShakeExtras
|
||||
,logger :: Logger.Handle
|
||||
,globals :: Var (Map.HashMap TypeRep Dynamic)
|
||||
,state :: Var Values
|
||||
,diagnostics :: Var (ProjectDiagnostics Key)
|
||||
}
|
||||
|
||||
getShakeExtras :: Action ShakeExtras
|
||||
@ -116,12 +116,7 @@ getIdeGlobalState = getIdeGlobalExtras . shakeExtras
|
||||
|
||||
|
||||
-- | The state of the all values - nested so you can easily find all errors at a given file.
|
||||
type Values =
|
||||
Map.HashMap FilePath
|
||||
(Map.HashMap Key
|
||||
(IdeResult Dynamic)
|
||||
)
|
||||
|
||||
type Values = Map.HashMap (FilePath, Key) (Maybe Dynamic)
|
||||
|
||||
-- | Key type
|
||||
data Key = forall k . (Typeable k, Hashable k, Eq k, Show k) => Key k
|
||||
@ -198,13 +193,10 @@ setValues :: IdeRule k v
|
||||
=> Var Values
|
||||
-> k
|
||||
-> FilePath
|
||||
-> IdeResult v
|
||||
-> IO (Maybe [FileDiagnostic], [FileDiagnostic]) -- ^ (before, after)
|
||||
setValues state key file val = modifyVar state $ \inVal -> do
|
||||
let k = Key key
|
||||
outVal = Map.insertWith Map.union file (Map.singleton k $ second (fmap toDyn) val) inVal
|
||||
f = concatMap fst . Map.elems
|
||||
return (outVal, (f <$> Map.lookup file inVal, f $ outVal Map.! file))
|
||||
-> Maybe v
|
||||
-> IO ()
|
||||
setValues state key file val = modifyVar_ state $
|
||||
pure . Map.insert (file, Key key) (fmap toDyn val)
|
||||
|
||||
-- | The outer Maybe is Nothing if this function hasn't been computed before
|
||||
-- the inner Maybe is Nothing if the result of the previous computation failed to produce
|
||||
@ -213,9 +205,8 @@ getValues :: forall k v. IdeRule k v => Var Values -> k -> FilePath -> IO (Maybe
|
||||
getValues state key file = do
|
||||
vs <- readVar state
|
||||
return $ do
|
||||
f <- Map.lookup file vs
|
||||
v <- Map.lookup (Key key) f
|
||||
pure $ fmap (fromJust . fromDynamic @v) $ snd v
|
||||
v <- Map.lookup (file, Key key) vs
|
||||
pure $ fmap (fromJust . fromDynamic @v) v
|
||||
|
||||
-- | Open a 'IdeState', should be shut using 'shakeShut'.
|
||||
shakeOpen :: (Event -> IO ()) -- ^ diagnostic handler
|
||||
@ -223,8 +214,12 @@ shakeOpen :: (Event -> IO ()) -- ^ diagnostic handler
|
||||
-> ShakeOptions
|
||||
-> Rules ()
|
||||
-> IO IdeState
|
||||
shakeOpen diags shakeLogger opts rules = do
|
||||
shakeExtras <- ShakeExtras diags shakeLogger <$> newVar Map.empty <*> newVar Map.empty
|
||||
shakeOpen eventer logger opts rules = do
|
||||
shakeExtras <- do
|
||||
globals <- newVar Map.empty
|
||||
state <- newVar Map.empty
|
||||
diagnostics <- newVar emptyDiagnostics
|
||||
pure ShakeExtras{..}
|
||||
(shakeDb, shakeClose) <- shakeOpenDatabase opts{shakeExtra = addShakeExtra shakeExtras $ shakeExtra opts} rules
|
||||
shakeAbort <- newVar $ return ()
|
||||
shakeDb <- shakeDb
|
||||
@ -263,20 +258,22 @@ useStale IdeState{shakeExtras=ShakeExtras{state}} k fp =
|
||||
|
||||
|
||||
getAllDiagnostics :: IdeState -> IO [FileDiagnostic]
|
||||
getAllDiagnostics IdeState{shakeExtras = ShakeExtras{state}} = do
|
||||
val <- readVar state
|
||||
return $ concatMap (concatMap fst . Map.elems) $ Map.elems val
|
||||
getAllDiagnostics IdeState{shakeExtras = ShakeExtras{diagnostics}} = do
|
||||
val <- readVar diagnostics
|
||||
return $ D.getAllDiagnostics val
|
||||
|
||||
-- | FIXME: This function is temporary! Only required because the files of interest doesn't work
|
||||
unsafeClearAllDiagnostics :: IdeState -> IO ()
|
||||
unsafeClearAllDiagnostics IdeState{shakeExtras = ShakeExtras{state}} = modifyVar_ state $
|
||||
return . Map.map (Map.map (\(_, x) -> ([], x)))
|
||||
unsafeClearAllDiagnostics IdeState{shakeExtras = ShakeExtras{diagnostics}} =
|
||||
writeVar diagnostics emptyDiagnostics
|
||||
|
||||
-- | Clear the results for all files that do not match the given predicate.
|
||||
garbageCollect :: (FilePath -> Bool) -> Action ()
|
||||
garbageCollect keep = do
|
||||
ShakeExtras{state} <- getShakeExtras
|
||||
liftIO $ modifyVar_ state $ return . Map.filterWithKey (\file _ -> keep file)
|
||||
ShakeExtras{state, diagnostics} <- getShakeExtras
|
||||
liftIO $
|
||||
do modifyVar_ state $ return . Map.filterWithKey (\(file, _) _ -> keep file)
|
||||
modifyVar_ diagnostics $ return . filterDiagnostics keep
|
||||
|
||||
define
|
||||
:: IdeRule k v
|
||||
@ -354,7 +351,7 @@ defineEarlyCutoff
|
||||
=> (k -> FilePath -> Action (Maybe BS.ByteString, IdeResult v))
|
||||
-> Rules ()
|
||||
defineEarlyCutoff op = addBuiltinRule noLint noIdentity $ \(Q (key, file)) old mode -> do
|
||||
ShakeExtras{state} <- getShakeExtras
|
||||
extras@ShakeExtras{state} <- getShakeExtras
|
||||
val <- case old of
|
||||
Just old | mode == RunDependenciesSame -> do
|
||||
v <- liftIO $ getValues state key file
|
||||
@ -365,46 +362,39 @@ defineEarlyCutoff op = addBuiltinRule noLint noIdentity $ \(Q (key, file)) old m
|
||||
case val of
|
||||
Just res -> return res
|
||||
Nothing -> do
|
||||
(bs, res) <- actionCatch
|
||||
(bs, (diags, res)) <- actionCatch
|
||||
(do v <- op key file; liftIO $ evaluate $ force v) $
|
||||
\(e :: SomeException) -> pure (Nothing, ([ideErrorText file $ T.pack $ show e | not $ isBadDependency e],Nothing))
|
||||
res <- return $ first (map $ \(_,d) -> (file,d)) res
|
||||
|
||||
(before, after) <- liftIO $ setValues state key file res
|
||||
updateFileDiagnostics file before after
|
||||
liftIO $ setValues state key file res
|
||||
updateFileDiagnostics file (Key key) extras $ map snd diags
|
||||
let eq = case (bs, fmap unwrap old) of
|
||||
(Just a, Just (Just b)) -> a == b
|
||||
_ -> False
|
||||
return $ RunResult
|
||||
(if eq then ChangedRecomputeSame else ChangedRecomputeDiff)
|
||||
(wrap bs)
|
||||
$ A (snd res) bs
|
||||
$ A res bs
|
||||
where
|
||||
wrap = maybe BS.empty (BS.cons '_')
|
||||
unwrap x = if BS.null x then Nothing else Just $ BS.tail x
|
||||
|
||||
|
||||
updateFileDiagnostics ::
|
||||
FilePath
|
||||
-> Maybe [FileDiagnostic] -- ^ previous results for this file
|
||||
-> [FileDiagnostic] -- ^ current results
|
||||
-> Key
|
||||
-> ShakeExtras
|
||||
-> [Diagnostic] -- ^ current results
|
||||
-> Action ()
|
||||
updateFileDiagnostics afp previousAll currentAll = do
|
||||
-- TODO (MK) We canonicalize to make sure that the two files agree on use of
|
||||
-- / and \ and other shenanigans.
|
||||
-- Once we have finished the migration to haskell-lsp we should make sure that
|
||||
-- this is no longer necessary.
|
||||
afp' <- liftIO $ canonicalizePath afp
|
||||
let filtM diags = do
|
||||
diags' <-
|
||||
filterM
|
||||
(\x -> fmap (== afp') (canonicalizePath $ fst x))
|
||||
diags
|
||||
pure (Set.fromList diags')
|
||||
previous <- liftIO $ traverse filtM previousAll
|
||||
current <- liftIO $ filtM currentAll
|
||||
when (Just current /= previous) $
|
||||
sendEvent $ EventFileDiagnostics $ (afp, map snd $ Set.toList current)
|
||||
updateFileDiagnostics fp k ShakeExtras{diagnostics, state} current = do
|
||||
(newDiags, oldDiags) <- liftIO $ do
|
||||
modTime <- join <$> getValues state GetModificationTime fp
|
||||
modifyVar diagnostics $ \old -> do
|
||||
let oldDiags = getFileDiagnostics fp old
|
||||
let newDiagsStore = setStageDiagnostics fp modTime k current old
|
||||
let newDiags = getFileDiagnostics fp newDiagsStore
|
||||
pure (newDiagsStore, (newDiags, oldDiags))
|
||||
when (newDiags /= oldDiags) $
|
||||
sendEvent $ EventFileDiagnostics (fp, newDiags)
|
||||
|
||||
|
||||
setPriority :: (Enum a) => a -> Action ()
|
||||
@ -424,3 +414,11 @@ logDebug, logSeriousError
|
||||
:: IdeState -> T.Text -> IO ()
|
||||
logDebug = sl Logger.logDebug
|
||||
logSeriousError = sl Logger.logSeriousError
|
||||
|
||||
data GetModificationTime = GetModificationTime
|
||||
deriving (Eq, Show, Generic)
|
||||
instance Hashable GetModificationTime
|
||||
instance NFData GetModificationTime
|
||||
|
||||
-- | Get the modification time of a file.
|
||||
type instance RuleResult GetModificationTime = UTCTime
|
||||
|
@ -27,12 +27,16 @@ module Development.IDE.Types.Diagnostics (
|
||||
ideTryIOException,
|
||||
showDiagnostics,
|
||||
showDiagnosticsColored,
|
||||
prettyDiagnosticStore,
|
||||
defDiagnostic,
|
||||
addDiagnostics,
|
||||
filterSeriousErrors,
|
||||
filePathToUri,
|
||||
getDiagnosticsFromStore
|
||||
uriToFilePath',
|
||||
ProjectDiagnostics,
|
||||
emptyDiagnostics,
|
||||
setStageDiagnostics,
|
||||
getAllDiagnostics,
|
||||
filterDiagnostics,
|
||||
getFileDiagnostics,
|
||||
prettyDiagnostics
|
||||
) where
|
||||
|
||||
import Control.Exception
|
||||
@ -40,14 +44,17 @@ import Data.Either.Combinators
|
||||
import Data.Maybe as Maybe
|
||||
import Data.Foldable
|
||||
import qualified Data.Map as Map
|
||||
import Data.Time.Clock
|
||||
import Data.Time.Clock.POSIX
|
||||
import qualified Data.Text as T
|
||||
import Data.Text.Prettyprint.Doc.Syntax
|
||||
import qualified Data.SortedList as SL
|
||||
import qualified Text.PrettyPrint.Annotated.HughesPJClass as Pretty
|
||||
import Language.Haskell.LSP.Types as LSP (
|
||||
import qualified Language.Haskell.LSP.Types as LSP
|
||||
import Language.Haskell.LSP.Types as LSP (
|
||||
DiagnosticSeverity(..)
|
||||
, Diagnostic(..)
|
||||
, filePathToUri
|
||||
, uriToFilePath
|
||||
, List(..)
|
||||
, DiagnosticRelatedInformation(..)
|
||||
, Uri(..)
|
||||
@ -56,6 +63,15 @@ import Language.Haskell.LSP.Diagnostics
|
||||
|
||||
import Development.IDE.Types.Location
|
||||
|
||||
-- | We use an empty string as a filepath when we don’t have a file.
|
||||
-- However, haskell-lsp doesn’t support that in uriToFilePath and given
|
||||
-- that it is not a valid filepath it does not make sense to upstream a fix.
|
||||
-- So we have our own wrapper here that supports empty filepaths.
|
||||
uriToFilePath' :: Uri -> Maybe FilePath
|
||||
uriToFilePath' uri
|
||||
| uri == filePathToUri "" = Just ""
|
||||
| otherwise = LSP.uriToFilePath uri
|
||||
|
||||
ideErrorText :: FilePath -> T.Text -> FileDiagnostic
|
||||
ideErrorText fp = errorDiag fp "Ide Error"
|
||||
|
||||
@ -96,28 +112,6 @@ defDiagnostic _range _message = LSP.Diagnostic {
|
||||
, _relatedInformation = Nothing
|
||||
}
|
||||
|
||||
filterSeriousErrors ::
|
||||
FilePath ->
|
||||
[LSP.Diagnostic] ->
|
||||
[LSP.Diagnostic]
|
||||
filterSeriousErrors fp =
|
||||
filter (maybe False hasSeriousErrors . LSP._relatedInformation)
|
||||
where
|
||||
hasSeriousErrors :: List DiagnosticRelatedInformation -> Bool
|
||||
hasSeriousErrors (List a) = any ((/=) uri . _uri . _location) a
|
||||
uri = LSP.filePathToUri fp
|
||||
|
||||
addDiagnostics ::
|
||||
FilePath ->
|
||||
[LSP.Diagnostic] ->
|
||||
DiagnosticStore -> DiagnosticStore
|
||||
addDiagnostics fp diags ds =
|
||||
updateDiagnostics
|
||||
ds
|
||||
(LSP.filePathToUri fp)
|
||||
Nothing $
|
||||
partitionBySource diags
|
||||
|
||||
ideTryIOException :: FilePath -> IO a -> IO (Either FileDiagnostic a)
|
||||
ideTryIOException fp act =
|
||||
mapLeft (\(e :: IOException) -> ideErrorText fp $ T.pack $ show e) <$> try act
|
||||
@ -167,20 +161,61 @@ prettyDiagnostic (fp, LSP.Diagnostic{..}) =
|
||||
where
|
||||
sev = fromMaybe LSP.DsError _severity
|
||||
|
||||
prettyDiagnosticStore :: DiagnosticStore -> Doc SyntaxClass
|
||||
prettyDiagnosticStore ds =
|
||||
vcat $
|
||||
map (\(uri, diags) -> prettyFileDiagnostics (fromMaybe noFilePath $ uriToFilePath uri, diags)) $
|
||||
Map.assocs $
|
||||
Map.map getDiagnosticsFromStore ds
|
||||
|
||||
prettyFileDiagnostics :: FileDiagnostics -> Doc SyntaxClass
|
||||
prettyFileDiagnostics (filePath, diags) =
|
||||
slabel_ "Compiler error in" $ vcat
|
||||
[ slabel_ "File:" $ pretty filePath
|
||||
, slabel_ "Errors:" $ vcat $ map (prettyDiagnostic . (filePath,)) diags
|
||||
]
|
||||
|
||||
getDiagnosticsFromStore :: StoreItem -> [Diagnostic]
|
||||
getDiagnosticsFromStore (StoreItem _ diags) =
|
||||
toList =<< Map.elems diags
|
||||
|
||||
-- | This represents every diagnostic in a LSP project, the stage type variable is
|
||||
-- the type of the compiler stages, in this project that is always the Key data
|
||||
-- type found in Development.IDE.State.Shake
|
||||
newtype ProjectDiagnostics stage = ProjectDiagnostics {getStore :: DiagnosticStore}
|
||||
deriving Show
|
||||
|
||||
emptyDiagnostics :: ProjectDiagnostics stage
|
||||
emptyDiagnostics = ProjectDiagnostics mempty
|
||||
|
||||
-- | Sets the diagnostics for a file and compilation step
|
||||
-- if you want to clear the diagnostics call this with an empty list
|
||||
setStageDiagnostics ::
|
||||
Show stage =>
|
||||
FilePath ->
|
||||
Maybe UTCTime ->
|
||||
-- ^ the time that the file these diagnostics originate from was last edited
|
||||
stage ->
|
||||
[LSP.Diagnostic] ->
|
||||
ProjectDiagnostics stage ->
|
||||
ProjectDiagnostics stage
|
||||
setStageDiagnostics fp timeM stage diags (ProjectDiagnostics ds) =
|
||||
ProjectDiagnostics $ updateDiagnostics ds uri posixTime diagsBySource
|
||||
where
|
||||
diagsBySource = Map.singleton (Just $ T.pack $ show stage) (SL.toSortedList diags)
|
||||
posixTime :: Maybe Int
|
||||
posixTime = fmap (fromEnum . utcTimeToPOSIXSeconds) timeM
|
||||
uri = filePathToUri fp
|
||||
|
||||
fromUri :: LSP.Uri -> FilePath
|
||||
fromUri = fromMaybe noFilePath . uriToFilePath'
|
||||
|
||||
getAllDiagnostics ::
|
||||
ProjectDiagnostics stage ->
|
||||
[FileDiagnostic]
|
||||
getAllDiagnostics =
|
||||
concatMap (\(k,v) -> map (fromUri k,) $ getDiagnosticsFromStore v) . Map.toList . getStore
|
||||
|
||||
getFileDiagnostics ::
|
||||
FilePath ->
|
||||
ProjectDiagnostics stage ->
|
||||
[LSP.Diagnostic]
|
||||
getFileDiagnostics fp ds =
|
||||
maybe [] getDiagnosticsFromStore $
|
||||
Map.lookup (filePathToUri fp) $
|
||||
getStore ds
|
||||
|
||||
filterDiagnostics ::
|
||||
(FilePath -> Bool) ->
|
||||
ProjectDiagnostics stage ->
|
||||
ProjectDiagnostics stage
|
||||
filterDiagnostics keep =
|
||||
ProjectDiagnostics .
|
||||
Map.filterWithKey (\uri _ -> maybe True keep $ uriToFilePath' uri) .
|
||||
getStore
|
||||
|
@ -44,7 +44,7 @@ import Development.IDE.State.Rules.Daml
|
||||
import qualified Development.IDE.Logger as Logger
|
||||
import Development.IDE.Types.LSP
|
||||
import DA.Daml.GHC.Compiler.Options (defaultOptionsIO)
|
||||
import Language.Haskell.LSP.Types (uriToFilePath, Range)
|
||||
import Language.Haskell.LSP.Types (Range)
|
||||
|
||||
-- * external dependencies
|
||||
import Control.Concurrent.STM
|
||||
@ -263,7 +263,7 @@ cursorPosition (_absPath, line, col) = D.Position line col
|
||||
|
||||
locationStartCursor :: D.Location -> Cursor
|
||||
locationStartCursor (D.Location path (D.Range (D.Position line col) _)) =
|
||||
(fromMaybe D.noFilePath $ uriToFilePath path, line, col)
|
||||
(fromMaybe D.noFilePath $ D.uriToFilePath' path, line, col)
|
||||
|
||||
-- | Same as Cursor, but passing a list of columns, so you can specify a range
|
||||
-- such as (foo,1,[10..20]).
|
||||
@ -376,7 +376,7 @@ matchGoToDefinitionPattern = \case
|
||||
In m -> \l -> fromMaybe False $ do
|
||||
l' <- l
|
||||
let uri = D._uri l'
|
||||
fp <- uriToFilePath uri
|
||||
fp <- D.uriToFilePath' uri
|
||||
pure $ isSuffixOf (moduleNameToFilePath m) fp
|
||||
|
||||
-- | Expect "go to definition" to point us at a certain location or to fail.
|
||||
|
@ -207,7 +207,7 @@ checkDiagnostics log expected got = do
|
||||
(\expFields -> not $ any (\diag -> all (checkField diag) expFields) got)
|
||||
expected
|
||||
pure $ if
|
||||
| length expected /= length got -> Just $ "Wrong number of diagnostics, expected " ++ show (length expected)
|
||||
| length expected /= length got -> Just $ "Wrong number of diagnostics, expected " ++ show (length expected) ++ ", but got " ++ show (length got)
|
||||
| null bad -> Nothing
|
||||
| otherwise -> Just $ unlines ("Could not find matching diagnostics:" : map show bad)
|
||||
where checkField :: D.FileDiagnostic -> DiagnosticField -> Bool
|
||||
|
@ -1,182 +0,0 @@
|
||||
// Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import * as DT from '../daml-test';
|
||||
import * as fs from 'fs';
|
||||
import * as assert from 'assert';
|
||||
import * as path from 'path';
|
||||
import * as Rx from 'rxjs'
|
||||
|
||||
describe("issue reports", function() {
|
||||
|
||||
|
||||
// DEL-6241: DAML-GHC not unloading modules from
|
||||
// scenario service, leading to internal error
|
||||
// when hashes are missing for definitions.
|
||||
//
|
||||
// The test opens two modules A and B, where
|
||||
// A depends on definition in B. A has a failing scenario.
|
||||
// The module A is then closed and we expect the
|
||||
// scenario diagnostics to clear. Finally we remove
|
||||
// the definition from B that A depends on and add
|
||||
// a failing scenario to A.
|
||||
//
|
||||
// Since currently scenario diagnostics are not properly reported
|
||||
// this test only validates that the "Ide Error" diagnostic is gone
|
||||
// that was shown when the scenario service context update fails.
|
||||
DT.testDaml("DEL-6241", connection => done => {
|
||||
const uriA = DT.utils.toRequestUri("A.daml");
|
||||
const aContents0 = `
|
||||
daml 1.2 module A where
|
||||
|
||||
x : Int
|
||||
x = 123
|
||||
`;
|
||||
const aContents1 = `
|
||||
daml 1.2 module A where
|
||||
|
||||
-- x is gone
|
||||
`;
|
||||
|
||||
const uriB = DT.utils.toRequestUri("B.daml");
|
||||
const bContents = `
|
||||
daml 1.2 module B where
|
||||
import A
|
||||
|
||||
testScenario = scenario do
|
||||
pure x
|
||||
`;
|
||||
|
||||
enum steps {
|
||||
step1_DiagnosticsForB,
|
||||
step2_ClosedB
|
||||
};
|
||||
var currentStep = steps.step1_DiagnosticsForB;
|
||||
var step2timeout = null;
|
||||
|
||||
var step2_seenA = false;
|
||||
const step = (diagnostic) => {
|
||||
switch(currentStep) {
|
||||
case steps.step1_DiagnosticsForB: {
|
||||
// step 1: We wait for diagnostics for B before we close it.
|
||||
if (diagnostic.uri.indexOf("B.daml") >= 0) {
|
||||
step2timeout = setTimeout(done, 10000);
|
||||
currentStep = steps.step2_ClosedB;
|
||||
connection.closeDocument(uriB);
|
||||
connection.changeDocument(
|
||||
uriA, 1, aContents1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case steps.step2_ClosedB: {
|
||||
// step 2: We check that no errors arrive for A
|
||||
// before 'step2timeout' fires.
|
||||
if (diagnostic.uri.indexOf("A.daml") >= 0) {
|
||||
const eq = DT.utils.deepEqual(
|
||||
diagnostic.diagnostics.map(d => d.message),
|
||||
[]);
|
||||
if(!eq.equal) {
|
||||
clearTimeout(step2timeout);
|
||||
done(eq.message);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
connection
|
||||
.diagnostics
|
||||
.subscribe(
|
||||
{ next: step,
|
||||
error: (err) => done(err)
|
||||
});
|
||||
|
||||
connection.openDocument(uriA, aContents0);
|
||||
connection.openDocument(uriB, bContents);
|
||||
})
|
||||
|
||||
// NOTE(JM): Disabled due to DEL-5741. Re-enable once fixed.
|
||||
/*
|
||||
|
||||
// see https://digitalasset.atlassian.net/browse/PROD-3898
|
||||
// Test works by loading a file with an import that hasn't been fully
|
||||
// typed (as in written) yet.
|
||||
// It then simulates typing the import by changing the file one
|
||||
// character at a time.
|
||||
// At the end we don't want any diagnostics left for the file.
|
||||
DT.testDaml("PROD-3898", (connection) => (done) => {
|
||||
const theImport = "tests/issue-reports/PROD-3898/TheImport.daml";
|
||||
const main0 = "tests/issue-reports/PROD-3898/Main.daml";
|
||||
const main1 = "tests/issue-reports/PROD-3898/Main1.daml";
|
||||
const main2 = "tests/issue-reports/PROD-3898/Main2.daml";
|
||||
const text0 = fs.readFileSync(main0, 'UTF-8');
|
||||
const text1 = fs.readFileSync(main1, 'UTF-8');
|
||||
const text2 = fs.readFileSync(main2, 'UTF-8');
|
||||
const file0 = DT.utils.toRequestUri(main0);
|
||||
const theImportText = fs.readFileSync(theImport, 'UTF-8');
|
||||
const theImportFile = DT.utils.toRequestUri(theImport);
|
||||
|
||||
const mainDiagnostics = connection.diagnostics
|
||||
.filter((diagnostic) => diagnostic.uri.indexOf("Main.daml") >= 0)
|
||||
.map((diagnostic) => diagnostic.diagnostics.map((d) => d.message));
|
||||
|
||||
enum steps { step1, step2, step3 }
|
||||
let currentStep = steps.step1;
|
||||
|
||||
let expectedDiagnostic1 =
|
||||
[ 'Module could not be found: TheImpo'
|
||||
];
|
||||
let expectedDiagnostic2 =
|
||||
[ 'Module could not be found: TheImpor'
|
||||
];
|
||||
|
||||
mainDiagnostics.subscribe(
|
||||
{
|
||||
next: (diagnostic) => {
|
||||
switch(currentStep) {
|
||||
case steps.step1: {
|
||||
const eq = DT.utils.deepEqual(diagnostic, expectedDiagnostic1);
|
||||
if(eq.equal) {
|
||||
connection.changeDocument(file0, 1, text1);
|
||||
currentStep = steps.step2;
|
||||
}
|
||||
else {
|
||||
done(eq.message);
|
||||
}
|
||||
break
|
||||
}
|
||||
case steps.step2: {
|
||||
const eq = DT.utils.deepEqual(diagnostic, expectedDiagnostic2);
|
||||
if(eq.equal) {
|
||||
connection.changeDocument(file0, 2, text2);
|
||||
currentStep = steps.step3;
|
||||
}
|
||||
else {
|
||||
done(eq.message);
|
||||
}
|
||||
break
|
||||
}
|
||||
case steps.step3: {
|
||||
const eq = DT.utils.deepEqual(diagnostic, []);
|
||||
if(eq.equal) {
|
||||
done();
|
||||
}
|
||||
else {
|
||||
done(eq.message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
error: (err) => done(err)
|
||||
}
|
||||
);
|
||||
|
||||
connection.openDocument(theImportFile, theImportText);
|
||||
connection.openDocument(file0, text0);
|
||||
|
||||
});
|
||||
|
||||
*/
|
||||
});
|
@ -30,9 +30,5 @@
|
||||
"source": "Compiler",
|
||||
"message": "/tests/diagnostics/failed-name-resolution/Main.daml:11:10: error:\n • Variable not in scope: abdd : Int -> Int -> Int\n • Perhaps you meant ‘add’ (line 8)"
|
||||
}
|
||||
],
|
||||
"file:/tests/diagnostics/failed-name-resolution/Prelude.daml": [],
|
||||
"file:/tests/diagnostics/failed-name-resolution/DA/Internal/Record.daml": [],
|
||||
"file:/tests/diagnostics/failed-name-resolution/DA/Internal/Desugar.daml": [],
|
||||
"file:/tests/diagnostics/failed-name-resolution/DA/Internal/RebindableSyntax.daml": []
|
||||
]
|
||||
}
|
||||
|
@ -16,10 +16,6 @@
|
||||
"message": "Cyclic module dependency between A, B, Main"
|
||||
}
|
||||
],
|
||||
"file:/tests/diagnostics/import-cycle/Prelude.daml": [],
|
||||
"file:/tests/diagnostics/import-cycle/DA/Internal/Desugar.daml": [],
|
||||
"file:/tests/diagnostics/import-cycle/DA/Internal/Record.daml": [],
|
||||
"file:/tests/diagnostics/import-cycle/DA/Internal/RebindableSyntax.daml": [],
|
||||
"file:/tests/diagnostics/import-cycle/A.daml": [
|
||||
{
|
||||
"severity": 1,
|
||||
@ -54,4 +50,4 @@
|
||||
"message": "Cyclic module dependency between A, B, Main"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,5 @@
|
||||
"source": "Compiler",
|
||||
"message": "/tests/diagnostics/import-error/Main.daml:7:8: error:\n Could not find module ‘Oops’\nIt is not a module in the current program, or in any known package."
|
||||
}
|
||||
],
|
||||
"file:/tests/diagnostics/import-error/Prelude.daml": [],
|
||||
"file:/tests/diagnostics/import-error/DA/Internal/Desugar.daml": [],
|
||||
"file:/tests/diagnostics/import-error/DA/Internal/Record.daml": [],
|
||||
"file:/tests/diagnostics/import-error/DA/Internal/RebindableSyntax.daml": [],
|
||||
"file:/tests/diagnostics/import-error/Oops.daml": []
|
||||
]
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"file:/tests/diagnostics/library-modules/Main.daml": [],
|
||||
"file:/tests/diagnostics/library-modules/Prelude.daml": [],
|
||||
"file:/tests/diagnostics/library-modules/DA/Internal/Desugar.daml": [],
|
||||
"file:/tests/diagnostics/library-modules/DA/Internal/Record.daml": [],
|
||||
"file:/tests/diagnostics/library-modules/DA/Internal/RebindableSyntax.daml": []
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
-- Copyright (c) 2019, Digital Asset (Switzerland) GmbH and/or its affiliates.
|
||||
-- All rights reserved.
|
||||
|
||||
daml 1.2
|
||||
module Extra where
|
||||
|
||||
x = 1
|
||||
|
||||
oops =
|
@ -1,8 +0,0 @@
|
||||
-- Copyright (c) 2019, Digital Asset (Switzerland) GmbH and/or its affiliates.
|
||||
-- All rights reserved.
|
||||
|
||||
daml 1.2
|
||||
module LibraryModules where
|
||||
|
||||
import Main
|
||||
import Extra
|
@ -1,7 +0,0 @@
|
||||
-- Copyright (c) 2019, Digital Asset (Switzerland) GmbH and/or its affiliates.
|
||||
-- All rights reserved.
|
||||
|
||||
daml 1.2
|
||||
module Main where
|
||||
|
||||
test_main = 10
|
@ -1,9 +1,4 @@
|
||||
{
|
||||
"file:/tests/diagnostics/multi-module-funny/Main.daml": [],
|
||||
"file:/tests/diagnostics/multi-module-funny/Prelude.daml": [],
|
||||
"file:/tests/diagnostics/multi-module-funny/DA/Internal/Desugar.daml": [],
|
||||
"file:/tests/diagnostics/multi-module-funny/DA/Internal/Record.daml": [],
|
||||
"file:/tests/diagnostics/multi-module-funny/DA/Internal/RebindableSyntax.daml": [],
|
||||
"file:/tests/diagnostics/multi-module-funny/Libs/A.daml": [
|
||||
{
|
||||
"severity": 1,
|
||||
@ -37,10 +32,5 @@
|
||||
"source": "Typechecker",
|
||||
"message": "/tests/diagnostics/multi-module-funny/Libs/B.daml:7:1: warning:\n The import of ‘Libs.C’ is redundant\n except perhaps to import instances from ‘Libs.C’\n To import instances alone, use: import Libs.C()"
|
||||
}
|
||||
],
|
||||
"file:/tests/diagnostics/multi-module-funny/Libs/Prelude.daml": [],
|
||||
"file:/tests/diagnostics/multi-module-funny/Libs/DA/Internal/Desugar.daml": [],
|
||||
"file:/tests/diagnostics/multi-module-funny/Libs/DA/Internal/Record.daml": [],
|
||||
"file:/tests/diagnostics/multi-module-funny/Libs/DA/Internal/RebindableSyntax.daml": [],
|
||||
"file:/tests/diagnostics/multi-module-funny/Libs/C.daml": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"file:/tests/diagnostics/multi-module/Main.daml": [],
|
||||
"file:/tests/diagnostics/multi-module/Prelude.daml": [],
|
||||
"file:/tests/diagnostics/multi-module/DA/Internal/Desugar.daml": [],
|
||||
"file:/tests/diagnostics/multi-module/DA/Internal/Record.daml": [],
|
||||
"file:/tests/diagnostics/multi-module/DA/Internal/RebindableSyntax.daml": [],
|
||||
"file:/tests/diagnostics/multi-module/Libs/A.daml": [],
|
||||
"file:/tests/diagnostics/multi-module/Libs/B.daml": []
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
-- Copyright (c) 2019, Digital Asset (Switzerland) GmbH and/or its affiliates.
|
||||
-- All rights reserved.
|
||||
|
||||
daml 1.2
|
||||
module Libs.A where
|
||||
|
||||
import Libs.B
|
||||
|
||||
double : Int -> Int
|
||||
double x = idInt x * idInt x
|
@ -1,8 +0,0 @@
|
||||
-- Copyright (c) 2019, Digital Asset (Switzerland) GmbH and/or its affiliates.
|
||||
-- All rights reserved.
|
||||
|
||||
daml 1.2
|
||||
module Libs.B where
|
||||
|
||||
idInt : Int -> Int
|
||||
idInt x = x
|
@ -1,10 +0,0 @@
|
||||
-- Copyright (c) 2019, Digital Asset (Switzerland) GmbH and/or its affiliates.
|
||||
-- All rights reserved.
|
||||
|
||||
daml 1.2
|
||||
module Main where
|
||||
|
||||
import Libs.A
|
||||
|
||||
test_main : Int
|
||||
test_main = double 10
|
@ -15,9 +15,5 @@
|
||||
"source": "Scenario",
|
||||
"message": "Scenario execution failed on commit at Main:28:5:\n #0: create of Main:Agree at DA.Internal.Prelude:356:1\n failed due to a missing authorization from 'Alice'\n\nLast source location: DA.Internal.Prelude:356:1\n\nLedger time: 1970-01-01T00:00:00Z\n\nPartial transaction:\n Sub-transactions:\n #0\n └─> create Main:Agree\n with\n p1 = 'Foo'; p2 = 'Alice'"
|
||||
}
|
||||
],
|
||||
"file:/tests/diagnostics/scenario-error/Prelude.daml": [],
|
||||
"file:/tests/diagnostics/scenario-error/DA/Internal/Record.daml": [],
|
||||
"file:/tests/diagnostics/scenario-error/DA/Internal/Desugar.daml": [],
|
||||
"file:/tests/diagnostics/scenario-error/DA/Internal/RebindableSyntax.daml": []
|
||||
]
|
||||
}
|
||||
|
@ -15,9 +15,5 @@
|
||||
"source": "Compiler",
|
||||
"message": "/tests/diagnostics/type-error/Main.daml:7:12: error:\n • Couldn't match expected type ‘Int’ with actual type ‘Text’\n • In the second argument of ‘(+)’, namely ‘\"foo\"’\n In the expression: 1 + \"foo\"\n In an equation for ‘oops’: oops = 1 + \"foo\""
|
||||
}
|
||||
],
|
||||
"file:/tests/diagnostics/type-error/Prelude.daml": [],
|
||||
"file:/tests/diagnostics/type-error/DA/Internal/Desugar.daml": [],
|
||||
"file:/tests/diagnostics/type-error/DA/Internal/Record.daml": [],
|
||||
"file:/tests/diagnostics/type-error/DA/Internal/RebindableSyntax.daml": []
|
||||
]
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ This page contains release notes for the SDK.
|
||||
|
||||
HEAD — ongoing
|
||||
--------------
|
||||
- Fixed a bug where type check errors would persist if there was a subsequent parse error
|
||||
|
||||
0.12.21 - 2019-05-28
|
||||
--------------------
|
||||
|
Loading…
Reference in New Issue
Block a user