Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 9be46df

Browse files
committedNov 28, 2020
Extended Eval Plugin (additional changes)
1 parent 9848239 commit 9be46df

17 files changed

+414
-405
lines changed
 

‎haskell-language-server.cabal

+2-3
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,12 @@ executable haskell-language-server
154154
, time
155155
, transformers
156156
, unordered-containers
157-
-- , ghc-exactprint
158157
, parser-combinators
159158
, pretty-simple
160-
, Diff == 0.4.*
161-
-- , ghc-paths
159+
, Diff
162160
, QuickCheck
163161

162+
164163
if flag(agpl)
165164
build-depends: brittany
166165
other-modules: Ide.Plugin.Brittany

‎plugins/default/src/Ide/Plugin/Eval/CodeLens.hs

+160-173
Large diffs are not rendered by default.

‎plugins/default/src/Ide/Plugin/Eval/GHC.hs

+7-14
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ module Ide.Plugin.Eval.GHC (
1010
hasPackage,
1111
addPackages,
1212
modifyFlags,
13-
gStrictTry,
1413
) where
1514

15+
import Control.DeepSeq (NFData, ($!!))
1616
import Control.Exception (SomeException)
1717
import Data.List (isPrefixOf)
1818
import Development.IDE.GHC.Error ()
@@ -43,7 +43,7 @@ import GhcPlugins (
4343
xopt_set,
4444
)
4545
import HscTypes (InteractiveContext (ic_dflags))
46-
import Ide.Plugin.Eval.Util (asS)
46+
import Ide.Plugin.Eval.Util (asS, gStrictTry)
4747
import qualified Lexer as Lexer
4848
import Module (UnitId (DefiniteUnitId))
4949
import Outputable (
@@ -60,11 +60,11 @@ import SrcLoc (mkRealSrcLoc)
6060
import StringBuffer (stringToStringBuffer)
6161

6262
{- $setup
63-
>>> import GHC
64-
>>> import GHC.Paths
65-
>>> run act = runGhc (Just libdir) (getSessionDynFlags >>= act)
66-
>>> libdir
67-
"/Users/titto/.stack/programs/x86_64-osx/ghc-8.10.2/lib/ghc-8.10.2"
63+
>>> import GHC
64+
>>> import GHC.Paths
65+
>>> run act = runGhc (Just libdir) (getSessionDynFlags >>= act)
66+
>>> libdir
67+
"/Users/titto/.stack/programs/x86_64-osx/ghc-8.10.2/lib/ghc-8.10.2"
6868
-}
6969

7070
{- | Returns true if string is an expression
@@ -200,10 +200,3 @@ instance Show DynFlags where
200200

201201
vList :: [String] -> SDoc
202202
vList = vcat . map text
203-
204-
gStrictTry :: ExceptionMonad m => m b -> m (Either String b)
205-
gStrictTry op =
206-
gcatch
207-
-- gStrictTry op = MC.catch
208-
(op >>= \v -> return $! Right $! v)
209-
(\(err :: SomeException) -> return $! Left $! show $! err)

‎plugins/default/src/Ide/Plugin/Eval/Parse/Section.hs

+68-52
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,95 @@
1-
-- |Parse a Section, a group of zero or more tests defined in a multiline comment or a sequence of one line comments.
1+
{-# LANGUAGE RecordWildCards #-}
2+
{-# LANGUAGE ViewPatterns #-}
23
{-# LANGUAGE NoMonomorphismRestriction #-}
3-
{-# LANGUAGE RecordWildCards #-}
4-
{-# LANGUAGE ViewPatterns #-}
54
{-# OPTIONS_GHC -Wwarn #-}
65
{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
76

8-
module Ide.Plugin.Eval.Parse.Section
9-
( allSections,
7+
-- |Parse a Section, a group of zero or more tests defined in a multiline comment or a sequence of one line comments.
8+
module Ide.Plugin.Eval.Parse.Section (
9+
allSections,
1010
validSections,
1111
Section (..),
12-
)
13-
where
12+
) where
1413

1514
import qualified Control.Applicative.Combinators.NonEmpty as NE
16-
import Control.Monad.Combinators (many, optional, some,
17-
(<|>))
18-
import qualified Data.List.NonEmpty as NE
19-
import Data.Maybe (catMaybes, fromMaybe)
20-
import Ide.Plugin.Eval.Parse.Parser (Parser, runParser,
21-
satisfy)
22-
import Ide.Plugin.Eval.Parse.Token (Token (BlockOpen, blockFormat, blockLanguage, blockName),
23-
TokenS, isBlockClose,
24-
isBlockOpen,
25-
isCodeLine,
26-
isPropLine,
27-
isStatement,
28-
isTextLine,
29-
unsafeContent)
30-
import Ide.Plugin.Eval.Types (Format (SingleLine),
31-
Loc,
32-
Located (Located, located, location),
33-
Section (..),
34-
Test (Example, Property),
35-
hasTests, unLoc)
15+
import Control.Monad.Combinators (
16+
many,
17+
optional,
18+
some,
19+
(<|>),
20+
)
21+
import qualified Data.List.NonEmpty as NE
22+
import Data.Maybe (catMaybes, fromMaybe)
23+
import Ide.Plugin.Eval.Parse.Parser (
24+
Parser,
25+
runParser,
26+
satisfy,
27+
)
28+
import Ide.Plugin.Eval.Parse.Token (
29+
Token (BlockOpen, blockFormat, blockLanguage, blockName),
30+
TokenS,
31+
isBlockClose,
32+
isBlockOpen,
33+
isCodeLine,
34+
isPropLine,
35+
isStatement,
36+
isTextLine,
37+
unsafeContent,
38+
)
39+
import Ide.Plugin.Eval.Types (
40+
Format (SingleLine),
41+
Loc,
42+
Located (Located, located, location),
43+
Section (..),
44+
Test (Example, Property),
45+
hasTests,
46+
unLoc,
47+
)
3648

3749
type Tk = Loc TokenS
3850

39-
4051
validSections :: [Tk] -> Either String [Section]
4152
validSections = (filter hasTests <$>) . allSections
4253

4354
allSections :: [Tk] -> Either String [Section]
4455
allSections = runParser sections
4556

46-
{-|
57+
{-
58+
>>> import Ide.Plugin.Eval.Parse.Token
4759
>>> import System.IO.Extra(readFileUTF8')
48-
>>> testSource fp = runParser sections . tokensFrom <$> readFileUTF8' fp
60+
>>> testSource_ = runParser sections . tokensFrom
61+
>>> testSource fp = testSource_ <$> readFileUTF8' fp
62+
63+
>>> testSource "plugins/default/src/Ide/Plugin/Eval/Test/TestGHC.hs"
64+
Right [Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [Located {location = 36, located = Property {testline = " \\(l::[Bool]) -> reverse (reverse l) == l", testOutput = []}}], sectionLanguage = Plain, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [Located {location = 40, located = Example {testLines = " :set -XScopedTypeVariables -XExplicitForAll" :| [" import qualified Test.QuickCheck as Q11"," runProp11 p = Q11.quickCheckWithResult Q11.stdArgs p >>= return . Q11.output"," prop11 = \\(l::[Int]) -> reverse (reverse l) == l"," runProp11 prop11"], testOutput = []}},Located {location = 46, located = Property {testline = " \\(l::[Int]) -> reverse (reverse l) == l", testOutput = []}}], sectionLanguage = Plain, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [Located {location = 50, located = Example {testLines = " t" :| [], testOutput = []}}], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [Located {location = 55, located = Example {testLines = " run $ runEval \"3+2\"" :| [], testOutput = []}}], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [Located {location = 125, located = Example {testLines = " isStmt \"\"" :| [], testOutput = ["stmt = let x =33;print x"]}}], sectionLanguage = Haddock, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine}]
4965
5066
>>> testSource "test/testdata/eval/T11.hs"
51-
Right [Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [Located {location = 5, located = Example {testLines = " :set -XTupleSections -XFlexibleInstances" :| [" (\"a\",) \"b\""], testOutput = []}}], sectionLanguage = Plain, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [Located {location = 10, located = Example {testLines = " (\"a\",) \"b\"" :| [], testOutput = []}}], sectionLanguage = Plain, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [Located {location = 15, located = Example {testLines = " :set -XWrong" :| [], testOutput = []}}], sectionLanguage = Plain, sectionFormat = MultiLine}]
67+
Right [Section {sectionName = "", sectionTests = [Located {location = 2, located = Example {testLines = " :kind! a" :| [], testOutput = []}}], sectionLanguage = Plain, sectionFormat = SingleLine}]
5268
5369
>>> testSource "test/testdata/eval/T12.hs"
54-
Right [Section {sectionName = "setup", sectionTests = [Located {location = 3, located = Example {testLines = " let a = 11" :| [" let z = 33"], testOutput = []}}], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "setup", sectionTests = [Located {location = 9, located = Example {testLines = " let x=11" :| [" let y=22"], testOutput = []}}], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [Located {location = 12, located = Example {testLines = " x+y+z" :| [], testOutput = []}}], sectionLanguage = Haddock, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [Located {location = 14, located = Example {testLines = " \"A\"" :| [], testOutput = ["\"A\""]}}], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [Located {location = 18, located = Example {testLines = " x=33" :| [" y=18"," x+y"], testOutput = ["51"]}}], sectionLanguage = Plain, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [Located {location = 25, located = Example {testLines = " let x=11" :| [" y = 22"], testOutput = []}},Located {location = 28, located = Example {testLines = " x+y" :| [" x-y"], testOutput = []}},Located {location = 31, located = Example {testLines = " x+1+m" :| [], testOutput = ["Variable not in scope: m :: Integer"]}}], sectionLanguage = Plain, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [Located {location = 35, located = Example {testLines = " \"" :| [], testOutput = ["lexical error in string/character literal at end of input"]}}], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [Located {location = 39, located = Example {testLines = " \"abc\"" :| [], testOutput = ["\"abc\""]}}], sectionLanguage = Plain, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [Located {location = 46, located = Example {testLines = " print \"ABC\"" :| [], testOutput = ["()"]}}], sectionLanguage = Plain, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [Located {location = 55, located = Example {testLines = " import System.IO" :| [" import GHC.IO.Handle"," hSetEncoding stdout utf8 >> hSetEncoding stderr utf8"], testOutput = ["()"]}},Located {location = 64, located = Example {testLines = " import Data.ByteString" :| [" Data.ByteString.pack \"\20908\29916\""], testOutput = ["Couldn't match type \8216Char\8217 with \8216Word8\8217","Expected type: [Word8]"," Actual type: [Char]"]}}], sectionLanguage = Plain, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [Located {location = 73, located = Example {testLines = " :set -XFlexibleInstances" :| [], testOutput = []}},Located {location = 75, located = Example {testLines = " class Print f where asPrint :: f -> IO String" :| [" instance Show a => Print (IO a) where asPrint io = io >>= return . show"," instance Show a => Print a where asPrint a = return (show a)"," asPrint (print \"GG\")"," asPrint \"GG\""], testOutput = []}}], sectionLanguage = Plain, sectionFormat = MultiLine}]
70+
Right [Section {sectionName = "", sectionTests = [Located {location = 6, located = Example {testLines = " type N = 1" :| [" type M = 40"," :kind N + M + 1"], testOutput = []}}], sectionLanguage = Plain, sectionFormat = SingleLine}]
5571
72+
>>> testSource_ $ "{"++"-\n -" ++ "}"
73+
Right [Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = MultiLine}]
5674
-}
5775
sections :: Parser Tk [Section]
5876
sections =
59-
catMaybes <$> many (const Nothing <$> some code <|> Just <$> section)
60-
77+
catMaybes <$> many (const Nothing <$> some code <|> Just <$> section)
6178

6279
section :: Parser Tk Section
6380
section = sectionBody >>= sectionEnd
6481

6582
sectionBody :: Parser Tk Section
6683
sectionBody =
67-
do
68-
( \(unLoc -> BlockOpen {..}) ts ->
84+
( \(unLoc -> BlockOpen{..}) ts ->
6985
Section (fromMaybe "" blockName) (catMaybes ts) blockLanguage blockFormat
70-
)
71-
<$> open <*> many (Just <$> example <|> Just <$> property <|> const Nothing <$> doc)
86+
)
87+
<$> open <*> many (Just <$> example <|> Just <$> property <|> const Nothing <$> doc)
7288

7389
sectionEnd :: Section -> Parser Tk Section
7490
sectionEnd s
75-
| sectionFormat s == SingleLine = optional code *> return s
76-
| otherwise = close *> return s
91+
| sectionFormat s == SingleLine = optional code *> return s
92+
| otherwise = close *> return s
7793

7894
-- section = do
7995
-- s <-
@@ -98,19 +114,19 @@ doc = some text
98114

99115
example, property :: Parser Tk (Loc Test)
100116
property =
101-
( \(Located l p) rs ->
102-
Located l (Property (unsafeContent p) (unsafeContent . located <$> rs))
103-
)
104-
<$> prop
105-
<*> many nonEmptyText
117+
( \(Located l p) rs ->
118+
Located l (Property (unsafeContent p) (unsafeContent . located <$> rs))
119+
)
120+
<$> prop
121+
<*> many nonEmptyText
106122
example =
107-
( \es rs ->
108-
Located
109-
(location (NE.head es))
110-
(Example (unsafeContent . located <$> es) (unsafeContent . located <$> rs))
111-
)
112-
<$> NE.some statement
113-
<*> many nonEmptyText
123+
( \es rs ->
124+
Located
125+
(location (NE.head es))
126+
(Example (unsafeContent . located <$> es) (unsafeContent . located <$> rs))
127+
)
128+
<$> NE.some statement
129+
<*> many nonEmptyText
114130

115131
open, close, statement, nonEmptyText, text, prop, code :: Parser Tk Tk
116132
statement = is isStatement

‎plugins/default/src/Ide/Plugin/Eval/Parse/Token.hs

+23-17
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,15 @@ contentOf (TextLine c) = Just c
7171
contentOf _ = Nothing
7272

7373
{- | Parse source code and return a list of located Tokens
74+
>>> import Ide.Plugin.Eval.Types (unLoc)
7475
>>> tks src = map unLoc . tokensFrom <$> readFile src
7576
7677
>>> tks "test/testdata/eval/T1.hs"
7778
[CodeLine,CodeLine,CodeLine,CodeLine,BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = SingleLine},Statement " unwords example",CodeLine,CodeLine]
7879
79-
>>> tks "test/testdata/eval/T11.hs"
80-
[BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = SingleLine},TextLine "Support for language options ",CodeLine,CodeLine,BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = MultiLine},TextLine "Multiple options can be set with a single `:set` ",TextLine "",Statement " :set -XTupleSections -XFlexibleInstances",Statement " (\"a\",) \"b\"",BlockClose,CodeLine,BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = MultiLine},TextLine "Options apply only in the section where they are defined (unless they are in the setup section).",Statement " (\"a\",) \"b\"",BlockClose,CodeLine,CodeLine,BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = MultiLine},TextLine "Wrong option names are reported.",Statement " :set -XWrong",BlockClose]
80+
>>> tks "test/testdata/eval/TLanguageOptions.hs"
81+
[BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = SingleLine},TextLine "Support for language options",CodeLine,CodeLine,CodeLine,CodeLine,BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = SingleLine},TextLine "Language options set in the module source (ScopedTypeVariables)",TextLine "also apply to tests so this works fine",Statement " f = (\\(c::Char) -> [c])",CodeLine,BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = MultiLine},TextLine "Multiple options can be set with a single `:set`",TextLine "",Statement " :set -XMultiParamTypeClasses -XFlexibleInstances",Statement " class Z a b c",BlockClose,CodeLine,BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = MultiLine},TextLine "",TextLine "",TextLine "Options apply only in the section where they are defined (unless they are in the setup section), so this will fail:",TextLine "",Statement " class L a b c",BlockClose,CodeLine,CodeLine,BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = MultiLine},TextLine "",TextLine "Options apply to all tests in the same section after their declaration.",TextLine "",TextLine "Not set yet:",TextLine "",Statement " class D",TextLine "",TextLine "Now it works:",TextLine "",Statement ":set -XMultiParamTypeClasses",Statement " class C",TextLine "",TextLine "It still works",TextLine "",Statement " class F",BlockClose,CodeLine,BlockOpen {blockName = Nothing, blockLanguage = Plain, blockFormat = MultiLine},TextLine "Wrong option names are reported.",Statement " :set -XWrong",BlockClose]
82+
8183
-}
8284
tokensFrom :: String -> [Loc (Token String)]
8385
tokensFrom = tokens . lines
@@ -104,8 +106,8 @@ tokensFrom = tokens . lines
104106
>>> tokens ["{-# LANGUAGE TupleSections","#-}"]
105107
[Located {location = 0, located = CodeLine},Located {location = 1, located = CodeLine}]
106108
107-
-- FIX
108-
>>> tokens ["{"++"--"++"}"]
109+
>>> length $ tokens ["{"++"--"++"}"]
110+
2
109111
110112
>>> tokens []
111113
[]
@@ -215,26 +217,30 @@ multiStart :: Parser Char ()
215217
multiStart = string "{-" *> optional space *> return ()
216218

217219
multiClose :: TParser
218-
multiClose = string "-}" >> return (InCode, [BlockClose])
220+
multiClose = many space *> string "-}" >> return (InCode, [BlockClose])
219221

220222
optionStart :: Parser Char (State, [Token s])
221223
optionStart = string "{-#" *> tillEnd *> return (InCode, [CodeLine])
222224

223225
name :: Parser Char [Char]
224226
name = (:) <$> letterChar <*> many (alphaNumChar <|> char '_')
225227

226-
-- |
227-
-- >>>runParser languageAndName "|$"
228-
-- Right (Just Haddock,Just "")
229-
--
230-
-- >>>runParser languageAndName "|$start"
231-
-- Right (Just Haddock,Just "start")
232-
--
233-
-- >>>runParser languageAndName "^"
234-
-- Right (Just Haddock,Nothing)
235-
--
236-
-- >>>runParser languageAndName "$start"
237-
-- Right (Nothing,Just "start")
228+
{- |
229+
>>>runParser languageAndName "|$"
230+
Right (Just Haddock,Just "")
231+
232+
>>>runParser languageAndName "|$start"
233+
Right (Just Haddock,Just "start")
234+
235+
>>>runParser languageAndName "| $start"
236+
Right (Just Haddock,Just "start")
237+
238+
>>>runParser languageAndName "^"
239+
Right (Just Haddock,Nothing)
240+
241+
>>>runParser languageAndName "$start"
242+
Right (Nothing,Just "start")
243+
-}
238244
languageAndName :: Parser Char (Maybe Language, Maybe String)
239245
languageAndName =
240246
(,) <$> optional ((char '|' <|> char '^') >> pure Haddock)

‎plugins/default/src/Ide/Plugin/Eval/Tutorial.hs

+14-25
Original file line numberDiff line numberDiff line change
@@ -59,31 +59,34 @@ All tests in the same comment sections are executed together.
5959
This is very convenient to execute multiple tests on the same function, as in this example:
6060
6161
>>> double 0
62+
0
6263
6364
>>> double 11
65+
22
6466
6567
>>> double 22
68+
44
6669
-}
6770
double :: Num a => a -> a
68-
double n = n*2
71+
double n = n * 2
6972

70-
-- A section prefixed with '$setup' has a special meaning, its code is executed before any other test.
73+
-- You can define a '$setup' section, whose code is executed before any other test.
7174

72-
-- $setup
73-
-- >>> x = 11
74-
-- >>> y = 22
75+
{- $setup
76+
>>> x = 11
77+
>>> y = 22
78+
-}
7579

7680
-- 'x' and 'y' are available in any test:
7781
-- >>> (x,y)
7882
-- (11,22)
7983

80-
8184
{- |
8285
Haddock comments, like this one, constitute the external module's documentation.
8386
84-
Theirs tests are part of the module functions' definitions and their results are not supposed to change.
87+
Their tests are part of the module functions' definitions and their results are not supposed to change.
8588
86-
So, whenever tests in Haddock comments are refreshed their current result is compared with the previous one and differences are displayed.
89+
So, whenever tests in Haddock comments are refreshed, their current result is compared with the previous one and differences are displayed.
8790
8891
If by mistake we change the definition of 'evens', we get a warning:
8992
@@ -146,8 +149,8 @@ prop> \(l::[Int]) -> reverse (reverse l) == l
146149
+++ OK, passed 100 tests.
147150
148151
prop> \(l::[Bool]) -> reverse l == l
149-
*** Failed! Falsified (after 5 tests and 2 shrinks):
150-
[True,False]
152+
*** Failed! Falsified (after 11 tests and 3 shrinks):
153+
[False,True]
151154
152155
And finally expressions:
153156
@@ -206,7 +209,7 @@ You could, for example, use the pretty-simple package:
206209
207210
>>> import Text.Pretty.Simple
208211
>>> pShowNoColor [1..3]
209-
"[ 1\n, 2\n, 3\n] "
212+
"[ 1\n, 2\n, 3\n]"
210213
211214
But what we get is just a String.
212215
@@ -226,17 +229,3 @@ To 'print' it properly, we can exploit the fact that the output of an error is d
226229
, 3
227230
]
228231
-}
229-
230-
{-
231-
Tip: prop> can be customised.
232-
233-
propEvaluation is the function used to evaluate a property, it can be redefined (e.g. to use something different than QuickCheck):
234-
235-
prop> \(l::[Bool]) -> reverse (reverse l) == l
236-
+++ OK, passed 100 tests.
237-
238-
>>> propEvaluation p = return "Your property is a joke and it miserably failed!"
239-
240-
prop> \(l::[Bool]) -> reverse (reverse l) == l
241-
"Your property is a joke and it miserably failed!"
242-
-}
+49-61
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,42 @@
1-
{-# OPTIONS_GHC -Wwarn #-}
21
{-# LANGUAGE DeriveAnyClass #-}
3-
{-# LANGUAGE DeriveFunctor #-}
4-
{-# LANGUAGE DeriveGeneric #-}
5-
module Ide.Plugin.Eval.Types
6-
( locate
7-
, locate0
8-
, Test(..)
9-
, isProperty
10-
, Format(..)
11-
, Language(..)
12-
, Section(..)
13-
, hasTests
14-
, hasPropertyTest
15-
, splitSections
16-
-- , ExpectedResult
17-
-- , ExpectedLine(..)
18-
-- , LineChunk(..)
19-
, Loc
20-
, Located(..)
21-
, unLoc
22-
, Txt
23-
)
24-
where
25-
import Data.Aeson (FromJSON, ToJSON)
26-
import Data.String
27-
import GHC.Generics (Generic)
28-
-- import Ide.Plugin.Eval.Location
29-
import Control.DeepSeq (NFData)
30-
import Control.DeepSeq (NFData (rnf), deepseq)
31-
import Data.List (partition)
32-
import Data.List.NonEmpty (NonEmpty)
33-
-- import Data.String (String)
2+
{-# LANGUAGE DeriveFunctor #-}
3+
{-# LANGUAGE DeriveGeneric #-}
4+
{-# OPTIONS_GHC -Wwarn #-}
5+
6+
module Ide.Plugin.Eval.Types (
7+
locate,
8+
locate0,
9+
Test (..),
10+
isProperty,
11+
Format (..),
12+
Language (..),
13+
Section (..),
14+
hasTests,
15+
hasPropertyTest,
16+
splitSections,
17+
Loc,
18+
Located (..),
19+
unLoc,
20+
Txt,
21+
) where
22+
23+
import Control.DeepSeq (NFData (rnf), deepseq)
24+
import Data.Aeson (FromJSON, ToJSON)
25+
import Data.List (partition)
26+
import Data.List.NonEmpty (NonEmpty)
27+
import Data.String (IsString (..))
28+
import GHC.Generics (Generic)
3429

3530
-- | A thing with a location attached.
36-
data Located l a = Located {location::l,located:: a}
37-
deriving (Eq, Show, Ord,Functor,Generic, FromJSON, ToJSON)
31+
data Located l a = Located {location :: l, located :: a}
32+
deriving (Eq, Show, Ord, Functor, Generic, FromJSON, ToJSON)
3833

3934
-- | Discard location information.
4035
unLoc :: Located l a -> a
4136
unLoc (Located _ a) = a
4237

43-
-- type Located = LocatedAt Location
44-
45-
-- location :: LocatedAt l a -> l
46-
-- location (Located l _) = l
47-
48-
instance (NFData l,NFData a) => NFData (Located l a) where
49-
rnf (Located loc a) = loc `deepseq` a `deepseq` ()
38+
instance (NFData l, NFData a) => NFData (Located l a) where
39+
rnf (Located loc a) = loc `deepseq` a `deepseq` ()
5040

5141
type Loc = Located Line
5242

@@ -60,13 +50,13 @@ locate0 = locate . Located 0
6050

6151
type Txt = String
6252

63-
data Section = Section
64-
{sectionName::Txt
65-
,sectionTests::[Loc Test]
66-
,sectionLanguage::Language
67-
,sectionFormat::Format
68-
}
69-
deriving (Eq, Show, Generic,FromJSON, ToJSON,NFData)
53+
data Section = Section
54+
{ sectionName :: Txt
55+
, sectionTests :: [Loc Test]
56+
, sectionLanguage :: Language
57+
, sectionFormat :: Format
58+
}
59+
deriving (Eq, Show, Generic, FromJSON, ToJSON, NFData)
7060

7161
hasTests :: Section -> Bool
7262
hasTests = not . null . sectionTests
@@ -78,29 +68,27 @@ hasPropertyTest = any (isProperty . unLoc) . sectionTests
7868
splitSections :: [Section] -> ([Section], [Section])
7969
splitSections = partition ((== "setup") . sectionName)
8070

81-
data Test
82-
= Example {testLines::(NonEmpty Txt),testOutput:: [Txt]}
83-
| Property {testline::Txt,testOutput::[Txt]}
84-
--data Test line = Example (NonEmpty line) [line] | Property line [line]
85-
deriving (Eq, Show, Generic, FromJSON, ToJSON,NFData)
71+
data Test
72+
= Example {testLines :: NonEmpty Txt, testOutput :: [Txt]}
73+
| Property {testline :: Txt, testOutput :: [Txt]}
74+
deriving (Eq, Show, Generic, FromJSON, ToJSON, NFData)
8675

8776
isProperty :: Test -> Bool
8877
isProperty (Property _ _) = True
89-
isProperty _ = False
90-
78+
isProperty _ = False
9179

92-
data Format = SingleLine | MultiLine deriving (Eq, Show, Ord,Generic, FromJSON, ToJSON,NFData)
93-
data Language = Plain | Haddock deriving (Eq, Show, Generic, Ord,FromJSON, ToJSON,NFData)
80+
data Format = SingleLine | MultiLine deriving (Eq, Show, Ord, Generic, FromJSON, ToJSON, NFData)
9481

82+
data Language = Plain | Haddock deriving (Eq, Show, Generic, Ord, FromJSON, ToJSON, NFData)
9583

9684
data ExpectedLine = ExpectedLine [LineChunk] | WildCardLine
97-
deriving (Eq, Show, Generic, FromJSON, ToJSON,NFData)
85+
deriving (Eq, Show, Generic, FromJSON, ToJSON, NFData)
9886

9987
instance IsString ExpectedLine where
100-
fromString = ExpectedLine . return . LineChunk
88+
fromString = ExpectedLine . return . LineChunk
10189

10290
data LineChunk = LineChunk String | WildCardChunk
103-
deriving (Eq, Show, Generic, FromJSON, ToJSON,NFData)
91+
deriving (Eq, Show, Generic, FromJSON, ToJSON, NFData)
10492

10593
instance IsString LineChunk where
106-
fromString = LineChunk
94+
fromString = LineChunk

‎plugins/default/src/Ide/Plugin/Eval/Util.hs

+35-7
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
1+
{-# LANGUAGE ImplicitParams #-}
2+
{-# LANGUAGE ScopedTypeVariables #-}
13
{-# LANGUAGE NoMonomorphismRestriction #-}
24
{-# OPTIONS_GHC -Wno-orphans #-}
35

46
-- |Debug utilities
57
module Ide.Plugin.Eval.Util (
68
asS,
7-
debug,
89
timed,
910
isLiterate,
1011
handleMaybe,
1112
handleMaybeM,
1213
response,
1314
response',
15+
gStrictTry,
16+
dbg,
1417
) where
1518

19+
import Control.Monad (join)
1620
import Control.Monad.IO.Class (MonadIO (liftIO))
1721
import Control.Monad.Trans.Class (lift)
1822
import Control.Monad.Trans.Except (
@@ -30,6 +34,9 @@ import Development.IDE (
3034
ideLogger,
3135
logPriority,
3236
)
37+
import Exception (ExceptionMonad, SomeException (..), evaluate, gcatch)
38+
import GHC.Exts (toList)
39+
import GHC.Stack (srcLocStartCol, srcLocStartLine, srcLocFile, HasCallStack, callStack)
3340
import Language.Haskell.LSP.Types (
3441
ErrorCode (InternalError),
3542
ResponseError (ResponseError),
@@ -54,11 +61,20 @@ timed out name op = do
5461
_ <- out name (showDuration secs)
5562
return r
5663

57-
debug :: MonadIO m => T.Text -> IdeState -> T.Text -> T.Text -> m ()
58-
debug mdlName state key val =
64+
dbg :: (HasCallStack, MonadIO m, Show a1, Show a2) => IdeState -> a1 -> a2 -> m ()
65+
dbg state key val =
5966
liftIO . logPriority (ideLogger state) logLevel $
6067
T.unwords
61-
[T.concat [mdlName, T.pack ":", key], val]
68+
[T.pack dbgPos, asT key, asT val]
69+
where
70+
dbgPos =
71+
let stk = toList callStack
72+
pr pos = concat [srcLocFile pos, ":", show . srcLocStartLine $ pos, ":", show . srcLocStartCol $ pos]
73+
-- pr = show
74+
in if null stk then "" else pr . snd . head $ stk
75+
76+
asT :: Show a => a -> T.Text
77+
asT = T.pack . show
6278

6379
logLevel :: Priority
6480
logLevel = Info -- Debug
@@ -72,16 +88,28 @@ handleMaybe msg = maybe (throwE msg) return
7288
handleMaybeM :: Monad m => e -> m (Maybe b) -> ExceptT e m b
7389
handleMaybeM msg act = maybe (throwE msg) return =<< lift act
7490

75-
response :: ExceptT String IO a -> IO (Either ResponseError a)
91+
response :: Functor f => ExceptT String f c -> f (Either ResponseError c)
7692
response =
7793
fmap (first (\msg -> ResponseError InternalError (fromString msg) Nothing))
7894
. runExceptT
7995

8096
response' :: ExceptT String IO a -> IO (Either ResponseError Value, Maybe a)
8197
response' act = do
82-
res <- runExceptT act
83-
case res of
98+
res <- gStrictTry $ runExceptT act
99+
case join res of
84100
Left e ->
85101
return
86102
(Left (ResponseError InternalError (fromString e) Nothing), Nothing)
87103
Right a -> return (Right Null, Just a)
104+
105+
gStrictTry :: ExceptionMonad m => m b -> m (Either String b)
106+
gStrictTry op =
107+
gcatch
108+
(op >>= fmap Right . gevaluate)
109+
showErr
110+
111+
gevaluate :: MonadIO m => a -> m a
112+
gevaluate = liftIO . evaluate
113+
114+
showErr :: Monad m => SomeException -> m (Either String b)
115+
showErr = return . Left . show

‎stack-8.6.5.yaml

+13-9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ ghc-options:
1313

1414
extra-deps:
1515
- aeson-1.5.2.0
16+
- apply-refact-0.8.2.1
1617
- ansi-terminal-0.10.3
1718
- base-compat-0.10.5
1819
- github: bubba/brittany
@@ -21,49 +22,52 @@ extra-deps:
2122
- Cabal-3.0.2.0
2223
- cabal-plan-0.6.2.0
2324
- clock-0.7.2
25+
- Diff-0.4.0
2426
- extra-1.7.3
2527
- floskell-0.10.4
26-
- fourmolu-0.1.0.0@rev:1
28+
- fourmolu-0.3.0.0
2729
- fuzzy-0.1.0.0
2830
# - ghcide-0.1.0
2931
- ghc-check-0.5.0.1
30-
- ghc-exactprint-0.6.2
31-
- ghc-lib-parser-8.10.2.20200808
32+
- ghc-exactprint-0.6.3.2
33+
- ghc-lib-8.10.2.20200916
34+
- ghc-lib-parser-8.10.2.20200916
3235
- ghc-lib-parser-ex-8.10.0.16
3336
- ghc-source-gen-0.4.0.0
3437
- haddock-api-2.22.0@rev:1
3538
- haddock-library-1.8.0
3639
- haskell-lsp-0.22.0.0
3740
- haskell-lsp-types-0.22.0.0
3841
- hie-bios-0.7.1
42+
- hlint-3.2
3943
- HsYAML-0.2.1.0@rev:1
4044
- HsYAML-aeson-0.2.0.0@rev:2
45+
- implicit-hie-cradle-0.3.0.0
46+
- implicit-hie-0.1.2.3
4147
- indexed-profunctors-0.1
4248
- lens-4.18
43-
- lsp-test-0.11.0.5
49+
- lsp-test-0.11.0.6
4450
- monad-dijkstra-0.1.1.2
4551
- opentelemetry-0.4.2
4652
- optics-core-0.2
4753
- optparse-applicative-0.15.1.0
48-
- ormolu-0.1.2.0
54+
- ormolu-0.1.4.1
4955
- parser-combinators-1.2.1
5056
- primitive-0.7.1.0
51-
- refinery-0.1.0.0
57+
- refinery-0.3.0.0
5258
- regex-base-0.94.0.0
5359
- regex-pcre-builtin-0.95.1.1.8.43
5460
- regex-tdfa-1.3.1.0
5561
- retrie-0.1.1.1
5662
- semialign-1.1
5763
# - github: wz1000/shake
5864
# commit: fb3859dca2e54d1bbb2c873e68ed225fa179fbef
59-
- stylish-haskell-0.11.0.3
65+
- stylish-haskell-0.12.2.0
6066
- tasty-rerun-1.1.17
6167
- temporary-1.2.1.1
6268
- these-1.1.1.1
6369
- type-equality-1
6470
- topograph-1
65-
- implicit-hie-cradle-0.2.0.1
66-
- implicit-hie-0.1.1.0
6771
- Diff-0.4.0@sha256:b5cfbeed498f555a18774ffd549bbeff7a24bdfe5984154dcfc9f4328a3c2847,1275
6872

6973
flags:

‎test/functional/Eval.hs

+35-37
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{-# LANGUAGE CPP #-}
2-
{-# LANGUAGE NamedFieldPuns #-}
32
{-# LANGUAGE OverloadedStrings #-}
43
{-# LANGUAGE ScopedTypeVariables #-}
54

@@ -12,6 +11,7 @@ import Control.Applicative.Combinators (
1211
)
1312
import Control.Monad (when)
1413
import Control.Monad.IO.Class (MonadIO (liftIO))
14+
import qualified Data.Text as T
1515
import qualified Data.Text.IO as T
1616
import Language.Haskell.LSP.Test (
1717
anyMessage,
@@ -39,7 +39,7 @@ import System.FilePath (
3939
(<.>),
4040
(</>),
4141
)
42-
import Test.Hls.Util (hieCommand)
42+
import Test.Hls.Util (hlsCommand)
4343
import Test.Tasty (
4444
TestTree,
4545
testGroup,
@@ -58,36 +58,30 @@ tests =
5858
testGroup
5959
"eval"
6060
[ testCase "Produces Evaluate code lenses" $
61-
runSession hieCommand fullCaps evalPath $ do
61+
runSession hlsCommand fullCaps evalPath $ do
6262
doc <- openDoc "T1.hs" "haskell"
6363
lenses <- getCodeLenses doc
6464
liftIO $ map (fmap _title . _command) lenses @?= [Just "Evaluate..."]
6565
, testCase "Produces Refresh code lenses" $
66-
runSession hieCommand fullCaps evalPath $ do
66+
runSession hlsCommand fullCaps evalPath $ do
6767
doc <- openDoc "T2.hs" "haskell"
6868
lenses <- getCodeLenses doc
6969
liftIO $ map (fmap _title . _command) lenses @?= [Just "Refresh..."]
7070
, testCase "Code lenses have ranges" $
71-
runSession hieCommand fullCaps evalPath $ do
71+
runSession hlsCommand fullCaps evalPath $ do
7272
doc <- openDoc "T1.hs" "haskell"
7373
lenses <- getCodeLenses doc
74-
liftIO $
75-
map _range lenses
76-
@?= [Range (Position 4 0) (Position 5 0)]
77-
, testCase "Multi-line expressions have a multi-line range" $
78-
runSession hieCommand fullCaps evalPath $ do
74+
liftIO $ map _range lenses @?= [Range (Position 4 0) (Position 5 0)]
75+
, testCase "Multi-line expressions have a multi-line range" $ do
76+
runSession hlsCommand fullCaps evalPath $ do
7977
doc <- openDoc "T3.hs" "haskell"
8078
lenses <- getCodeLenses doc
81-
liftIO $
82-
map _range lenses
83-
@?= [Range (Position 3 0) (Position 5 0)]
84-
, testCase "Executed expressions range covers only the expression" $
85-
runSession hieCommand fullCaps evalPath $ do
79+
liftIO $ map _range lenses @?= [Range (Position 3 0) (Position 5 0)]
80+
, testCase "Executed expressions range covers only the expression" $ do
81+
runSession hlsCommand fullCaps evalPath $ do
8682
doc <- openDoc "T2.hs" "haskell"
8783
lenses <- getCodeLenses doc
88-
liftIO $
89-
map _range lenses
90-
@?= [Range (Position 4 0) (Position 5 0)]
84+
liftIO $ map _range lenses @?= [Range (Position 4 0) (Position 5 0)]
9185
, testCase "Evaluation of expressions" $ goldenTest "T1.hs"
9286
, testCase "Reevaluation of expressions" $ goldenTest "T2.hs"
9387
, testCase "Evaluation of expressions w/ imports" $ goldenTest "T3.hs"
@@ -103,25 +97,25 @@ tests =
10397
, testCase "Shows a kind with :kind" $ goldenTest "T12.hs"
10498
, testCase "Reports an error for an incorrect type with :kind" $
10599
goldenTest "T13.hs"
106-
, testCase "Returns a fully-instantiated type for :type" $ goldenTest "T14.hs"
107-
, testCase
108-
"Returns an uninstantiated type for :type +v, admitting multiple whitespaces around arguments"
109-
$ goldenTest "T15.hs"
110-
, testCase
111-
"Returns defaulted type for :type +d, admitting multiple whitespaces around arguments"
112-
$ goldenTest "T16.hs"
100+
, testCase "Returns a fully-instantiated type for :type" $
101+
goldenTest "T14.hs"
102+
, testCase "Returns an uninstantiated type for :type +v, admitting multiple whitespaces around arguments" $
103+
goldenTest "T15.hs"
104+
, testCase "Returns defaulted type for :type +d, admitting multiple whitespaces around arguments" $
105+
goldenTest "T16.hs"
113106
, testCase ":type reports an error when given with unknown +x option" $
114107
goldenTest "T17.hs"
115108
, testCase "Reports an error when given with unknown command" $
116109
goldenTest "T18.hs"
117-
, testCase
118-
"Returns defaulted type for :type +d reflecting the default declaration specified in the >>> prompt"
119-
$ goldenTest "T19.hs"
110+
, testCase "Returns defaulted type for :type +d reflecting the default declaration specified in the >>> prompt" $
111+
goldenTest "T19.hs"
120112
, expectFailBecause "known issue - see a note in P.R. #361" $
121113
testCase ":type +d reflects the `default' declaration of the module" $
122114
goldenTest "T20.hs"
123-
, testCase ":type handles a multilined result properly" $ goldenTest "T21.hs"
124-
, testCase ":t behaves exactly the same as :type" $ goldenTest "T22.hs"
115+
, testCase ":type handles a multilined result properly" $
116+
goldenTest "T21.hs"
117+
, testCase ":t behaves exactly the same as :type" $
118+
goldenTest "T22.hs"
125119
, testCase ":type does \"dovetails\" for short identifiers" $
126120
goldenTest "T23.hs"
127121
, testCase ":kind! treats a multilined result properly" $
@@ -163,23 +157,27 @@ goldenTest = goldenTestBy id
163157
Compare results with the contents of corresponding '.expected' file (and creates it, if missing)
164158
-}
165159
goldenTestBy :: ([CodeLens] -> [CodeLens]) -> FilePath -> IO ()
166-
goldenTestBy filter input = runSession hieCommand fullCaps evalPath $ do
160+
goldenTestBy filter input = runSession hlsCommand fullCaps evalPath $ do
167161
doc <- openDoc input "haskell"
168162

169163
-- Execute lenses backwards, to avoid affecting their position in the source file
170164
codeLenses <- reverse . filter <$> getCodeLenses doc
171165
mapM_ executeCommand $ [c | CodeLens{_command = Just c} <- codeLenses]
172166
_resp :: ApplyWorkspaceEditRequest <- skipManyTill anyMessage message
173-
edited <- documentContents doc
167+
edited <- replaceUnicodeQuotes <$> documentContents doc
168+
169+
let expectedFile = evalPath </> input <.> "expected"
174170

175-
let expected = evalPath </> input <.> "expected"
176171
liftIO $ do
177172
-- Write expected file if missing
178-
missingExpected <- not <$> doesFileExist expected
179-
when missingExpected $ T.writeFile expected edited
173+
missingExpected <- not <$> doesFileExist expectedFile
174+
when missingExpected $ T.writeFile expectedFile edited
175+
176+
expected <- liftIO $ T.readFile expectedFile
177+
liftIO $ edited @?= expected
180178

181-
expected <- T.readFile expected
182-
edited @?= expected
179+
replaceUnicodeQuotes :: T.Text -> T.Text
180+
replaceUnicodeQuotes = T.replace "" "'" . T.replace "" "'"
183181

184182
evalPath :: FilePath
185183
evalPath = "test/testdata/eval"

‎test/testdata/eval/T11.hs.expected

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
module T11 where
22

33
-- >>> :kind! a
4-
-- Not in scope: type variable ‘a’
4+
-- Not in scope: type variable 'a'

‎test/testdata/eval/T13.hs.expected

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
module T13 where
22

33
-- >>> :kind a
4-
-- Not in scope: type variable ‘a’
4+
-- Not in scope: type variable 'a'

‎test/testdata/eval/T17.hs.expected

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
module T17 where
22

33
-- >>> :type +no 42
4-
-- parse error on input ‘+’
4+
-- parse error on input '+'

‎test/testdata/eval/T8.hs.expected

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module T8 where
55
-- Variable not in scope: noFunctionWithThisName
66

77
-- >>> "a" + "bc"
8-
-- No instance for (Num [Char]) arising from a use of ‘+’
8+
-- No instance for (Num [Char]) arising from a use of '+'
99

1010
-- >>> "
1111
-- lexical error in string/character literal at end of input

‎test/testdata/eval/TAllComments.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ module TAllComments where
2020
twice :: [a] -> [a]
2121
twice a = a ++ a
2222
{- ^ Also in backward Haddock comments
23-
>>> twice "ABC"
23+
>>> twice "ABC"
2424
-}
2525

2626
-- Ignored as it doesn't start on the first column.

‎test/testdata/eval/TAllComments.hs.expected

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ module TAllComments where
2323
twice :: [a] -> [a]
2424
twice a = a ++ a
2525
{- ^ Also in backward Haddock comments
26-
>>> twice "ABC"
26+
>>> twice "ABC"
27+
"ABCABC"
2728
-}
2829

2930
-- Ignored as it doesn't start on the first column.

‎test/testdata/eval/test.cabal

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ build-type: Simple
1111
cabal-version: >=1.10
1212

1313
library
14-
exposed-modules: T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14
14+
exposed-modules: T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TAllComments, Util
1515
build-depends: base >= 4.7 && < 5, QuickCheck
1616
default-language: Haskell2010
1717
ghc-options: -Wall -fwarn-unused-imports

0 commit comments

Comments
 (0)
Please sign in to comment.