Skip to content

Commit b70aabf

Browse files
LiamGoodacrepaf31
authored andcommitted
Add support for user defined warnings via the Warn type class (purescript#2569)
* Add support for user defined warnings via the Warn type class * Deal with failing to print the type string
1 parent 1bb6acd commit b70aabf

File tree

7 files changed

+50
-9
lines changed

7 files changed

+50
-9
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-- @shouldWarnWith UserDefinedWarning
2+
module Main where
3+
4+
foo :: forall t. Warn (TypeConcat "Custom warning " (TypeString t)) => t -> t
5+
foo x = x
6+
7+
bar :: Int
8+
bar = foo 42
9+

src/Language/PureScript/AST/Declarations.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ data SimpleErrorMessage
141141
| CannotUseBindWithDo
142142
-- | instance name, type class, expected argument count, actual argument count
143143
| ClassInstanceArityMismatch Ident (Qualified (ProperName 'ClassName)) Int Int
144+
-- | a user-defined warning raised by using the Warn type class
145+
| UserDefinedWarning Type
144146
deriving (Show)
145147

146148
-- | Error message hints, providing more detailed information about failure.

src/Language/PureScript/Constants.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,9 @@ pattern Partial = Qualified (Just Prim) (ProperName "Partial")
369369
pattern Fail :: Qualified (ProperName 'ClassName)
370370
pattern Fail = Qualified (Just Prim) (ProperName "Fail")
371371

372+
pattern Warn :: Qualified (ProperName 'ClassName)
373+
pattern Warn = Qualified (Just Prim) (ProperName "Warn")
374+
372375
typ :: forall a. (IsString a) => a
373376
typ = "Type"
374377

src/Language/PureScript/Docs/Prim.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ primDocsModule = Module
2424
, boolean
2525
, partial
2626
, fail
27+
, warn
2728
, typeConcat
2829
, typeString
2930
, kindType
@@ -225,6 +226,14 @@ fail = primClass "Fail" $ T.unlines
225226
, "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)."
226227
]
227228

229+
warn :: Declaration
230+
warn = primClass "Warn" $ T.unlines
231+
[ "The Warn type class allows a custom compiler warning to be displayed."
232+
, ""
233+
, "For more information, see"
234+
, "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)."
235+
]
236+
228237
typeConcat :: Declaration
229238
typeConcat = primType "TypeConcat" $ T.unlines
230239
[ "The TypeConcat type constructor concatenates two Symbols in a custom type"

src/Language/PureScript/Environment.hs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ primKinds =
347347

348348
-- |
349349
-- The primitive types in the external javascript environment with their
350-
-- associated kinds. There are also pseudo `Fail` and `Partial` types
350+
-- associated kinds. There are also pseudo `Fail`, `Warn`, and `Partial` types
351351
-- that correspond to the classes with the same names.
352352
--
353353
primTypes :: M.Map (Qualified (ProperName 'TypeName)) (Kind, TypeKind)
@@ -363,20 +363,23 @@ primTypes =
363363
, (primName "Boolean", (kindType, ExternData))
364364
, (primName "Partial", (kindType, ExternData))
365365
, (primName "Fail", (FunKind kindSymbol kindType, ExternData))
366+
, (primName "Warn", (FunKind kindSymbol kindType, ExternData))
366367
, (primName "TypeString", (FunKind kindType kindSymbol, ExternData))
367368
, (primName "TypeConcat", (FunKind kindSymbol (FunKind kindSymbol kindSymbol), ExternData))
368369
]
369370

370371
-- |
371-
-- The primitive class map. This just contains the `Fail` and `Partial`
372+
-- The primitive class map. This just contains the `Fail`, `Warn`, and `Partial`
372373
-- classes. `Partial` is used as a kind of magic constraint for partial
373-
-- functions. `Fail` is used for user-defined type errors.
374+
-- functions. `Fail` is used for user-defined type errors. `Warn` for
375+
-- user-defined warnings.
374376
--
375377
primClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData
376378
primClasses =
377379
M.fromList
378380
[ (primName "Partial", (makeTypeClassData [] [] [] []))
379381
, (primName "Fail", (makeTypeClassData [("message", Just kindSymbol)] [] [] []))
382+
, (primName "Warn", (makeTypeClassData [("message", Just kindSymbol)] [] [] []))
380383
]
381384

382385
-- |

src/Language/PureScript/Errors.hs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ errorCode em = case unwrapErrorMessage em of
173173
ExpectedWildcard{} -> "ExpectedWildcard"
174174
CannotUseBindWithDo{} -> "CannotUseBindWithDo"
175175
ClassInstanceArityMismatch{} -> "ClassInstanceArityMismatch"
176+
UserDefinedWarning{} -> "UserDefinedWarning"
176177

177178
-- |
178179
-- A stack trace for an error
@@ -889,6 +890,12 @@ prettyPrintSingleError (PPEOptions codeColor full level showDocs) e = flip evalS
889890
T.pack (show actual) <> "."
890891
]
891892

893+
renderSimpleErrorMessage (UserDefinedWarning msgTy) =
894+
let msg = fromMaybe (typeAsBox msgTy) (toTypelevelString msgTy) in
895+
paras [ line "A custom warning occurred while solving type class constraints:"
896+
, indent msg
897+
]
898+
892899
renderHint :: ErrorMessageHint -> Box.Box -> Box.Box
893900
renderHint (ErrorUnifyingTypes t1 t2) detail =
894901
paras [ detail

src/Language/PureScript/TypeChecker/Entailment.hs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ import qualified Language.PureScript.Constants as C
4545
data Evidence
4646
= NamedInstance (Qualified Ident)
4747
-- ^ An existing named instance
48+
| WarnInstance Type
49+
-- ^ Computed instance of the Warn type class with a user-defined warning message
4850
| IsSymbolInstance PSString
4951
-- ^ Computed instance of the IsSymbol type class for a given Symbol literal
5052
| CompareSymbolInstance
@@ -144,6 +146,8 @@ entails SolverOptions{..} constraint context hints =
144146
solve constraint
145147
where
146148
forClassName :: InstanceContext -> Qualified (ProperName 'ClassName) -> [Type] -> [TypeClassDict]
149+
forClassName _ C.Warn [msg] =
150+
[TypeClassDictionaryInScope (WarnInstance msg) [] C.Warn [msg] Nothing]
147151
forClassName _ C.IsSymbol [TypeLevelString sym] =
148152
[TypeClassDictionaryInScope (IsSymbolInstance sym) [] C.IsSymbol [TypeLevelString sym] Nothing]
149153
forClassName _ C.CompareSymbol [arg0@(TypeLevelString lhs), arg1@(TypeLevelString rhs), _] =
@@ -216,8 +220,9 @@ entails SolverOptions{..} constraint context hints =
216220
let subst'' = fmap (substituteType currentSubst') subst'
217221
-- Solve any necessary subgoals
218222
args <- solveSubgoals subst'' (tcdDependencies tcd)
223+
initDict <- lift . lift $ mkDictionary (tcdValue tcd) args
219224
let match = foldr (\(superclassName, index) dict -> subclassDictionaryValue dict superclassName index)
220-
(mkDictionary (tcdValue tcd) args)
225+
initDict
221226
(tcdPath tcd)
222227
return match
223228
Unsolved unsolved -> do
@@ -308,15 +313,18 @@ entails SolverOptions{..} constraint context hints =
308313
Just <$> traverse (go (work + 1) . mapConstraintArgs (map (replaceAllTypeVars (M.toList subst)))) subgoals
309314

310315
-- Make a dictionary from subgoal dictionaries by applying the correct function
311-
mkDictionary :: Evidence -> Maybe [Expr] -> Expr
312-
mkDictionary (NamedInstance n) args = foldl App (Var n) (fold args)
316+
mkDictionary :: Evidence -> Maybe [Expr] -> m Expr
317+
mkDictionary (NamedInstance n) args = return $ foldl App (Var n) (fold args)
318+
mkDictionary (WarnInstance msg) _ = do
319+
tell . errorMessage $ UserDefinedWarning msg
320+
return $ TypeClassDictionaryConstructorApp C.Warn (Literal (ObjectLiteral []))
313321
mkDictionary (IsSymbolInstance sym) _ =
314322
let fields = [ ("reflectSymbol", Abs (Left (Ident C.__unused)) (Literal (StringLiteral sym))) ] in
315-
TypeClassDictionaryConstructorApp C.IsSymbol (Literal (ObjectLiteral fields))
323+
return $ TypeClassDictionaryConstructorApp C.IsSymbol (Literal (ObjectLiteral fields))
316324
mkDictionary CompareSymbolInstance _ =
317-
TypeClassDictionaryConstructorApp C.CompareSymbol (Literal (ObjectLiteral []))
325+
return $ TypeClassDictionaryConstructorApp C.CompareSymbol (Literal (ObjectLiteral []))
318326
mkDictionary AppendSymbolInstance _ =
319-
TypeClassDictionaryConstructorApp C.AppendSymbol (Literal (ObjectLiteral []))
327+
return $ TypeClassDictionaryConstructorApp C.AppendSymbol (Literal (ObjectLiteral []))
320328

321329
-- Turn a DictionaryValue into a Expr
322330
subclassDictionaryValue :: Expr -> Qualified (ProperName a) -> Integer -> Expr

0 commit comments

Comments
 (0)