ghc generates more user-friendly error messages
authorMike Izbicki <mike@izbicki.me>
Thu, 20 Nov 2014 00:29:37 +0000 (18:29 -0600)
committerAustin Seipp <austin@well-typed.com>
Thu, 20 Nov 2014 01:49:37 +0000 (19:49 -0600)
Test Plan: Compiled ghc fine.  Opened ghci and fed it invalid code.  It gave the improved error messages in response.

Reviewers: austin

Subscribers: thomie, simonpj, spacekitteh, rwbarton, simonmar, carter

Differential Revision: https://phabricator.haskell.org/D201

13 files changed:
compiler/parser/Lexer.x
compiler/parser/Parser.y
compiler/typecheck/TcErrors.lhs
testsuite/tests/annotations/should_fail/annfail08.stderr
testsuite/tests/deriving/should_fail/drvfail007.stderr
testsuite/tests/ghci.debugger/scripts/break003.stderr
testsuite/tests/ghci/scripts/Defer02.stderr
testsuite/tests/mdo/should_fail/mdofail005.stderr
testsuite/tests/parser/should_fail/ParserNoLambdaCase.stderr
testsuite/tests/parser/should_fail/readFail020.stderr
testsuite/tests/parser/should_fail/readFail040.stderr
testsuite/tests/rebindable/rebindable6.stderr
testsuite/tests/typecheck/should_fail/T2846b.stderr

index 6d05bb9..b4223c8 100644 (file)
@@ -2144,6 +2144,8 @@ srcParseErr dflags buf len
          else ptext (sLit "parse error on input") <+> quotes (text token)
               $$ ppWhen (not th_enabled && token == "$") -- #7396
                         (text "Perhaps you intended to use TemplateHaskell")
+              $$ ppWhen (token == "<-")
+                        (text "Perhaps this statement should be within a 'do' block?")
   where token = lexemeToString (offsetBytes (-len) buf) len
         th_enabled = xopt Opt_TemplateHaskell dflags
 
index 2e1b777..b6db3a8 100644 (file)
@@ -1577,6 +1577,37 @@ exp10 :: { LHsExpr RdrName }
                                                     -- hdaume: core annotation
         | fexp                                  { $1 }
 
+        -- parsing error messages go below here
+        | '\\' apat apats opt_asig '->'              {% parseErrorSDoc (combineLocs $1 $5) $ text
+                                                        "parse error in lambda: no expression after '->'"
+                                                     }
+        | '\\'                                       {% parseErrorSDoc (getLoc $1) $ text
+                                                        "parse error: naked lambda expression '\'"
+                                                     }
+        | 'let' binds 'in'                           {% parseErrorSDoc (combineLocs $1 $2) $ text
+                                                        "parse error in let binding: missing expression after 'in'"
+                                                     }
+        | 'let' binds                                {% parseErrorSDoc (combineLocs $1 $2) $ text
+                                                        "parse error in let binding: missing required 'in'"
+                                                     }
+        | 'let'                                      {% parseErrorSDoc (getLoc $1) $ text
+                                                        "parse error: naked let binding"
+                                                     }
+        | 'if' exp optSemi 'then' exp optSemi 'else' {% hintIf (combineLocs $1 $5) "else clause empty" }
+        | 'if' exp optSemi 'then' exp optSemi        {% hintIf (combineLocs $1 $5) "missing required else clause" }
+        | 'if' exp optSemi 'then'                    {% hintIf (combineLocs $1 $2) "then clause empty" }
+        | 'if' exp optSemi                           {% hintIf (combineLocs $1 $2) "missing required then and else clauses" }
+        | 'if'                                       {% hintIf (getLoc $1) "naked if statement" }
+        | 'case' exp 'of'                            {% parseErrorSDoc (combineLocs $1 $2) $ text
+                                                        "parse error in case statement: missing list after '->'"
+                                                     }
+        | 'case' exp                                 {% parseErrorSDoc (combineLocs $1 $2) $ text
+                                                        "parse error in case statement: missing required 'of'"
+                                                     }
+        | 'case'                                     {% parseErrorSDoc (getLoc $1) $ text
+                                                        "parse error: naked case statement"
+                                                     }
+
 optSemi :: { Bool }
         : ';'         { True }
         | {- empty -} { False }
@@ -2377,6 +2408,14 @@ hintMultiWayIf span = do
   unless mwiEnabled $ parseErrorSDoc span $
     text "Multi-way if-expressions need MultiWayIf turned on"
 
+-- Hint about if usage for beginners
+hintIf :: SrcSpan -> String -> P (LHsExpr RdrName)
+hintIf span msg = do
+  mwiEnabled <- liftM ((Opt_MultiWayIf `xopt`) . dflags) getPState
+  if mwiEnabled
+    then parseErrorSDoc span $ text $ "parse error in if statement"
+    else parseErrorSDoc span $ text $ "parse error in if statement: "++msg
+
 -- Hint about explicit-forall, assuming UnicodeSyntax is on
 hintExplicitForall :: SrcSpan -> P ()
 hintExplicitForall span = do
index 6be82c1..0ce397a 100644 (file)
@@ -1089,10 +1089,22 @@ mk_dict_err fam_envs ctxt (ct, (matches, unifiers, safe_haskell))
                , nest 19 (ptext (sLit "to") <+> quotes (ppr ty2)) ]
                  -- The nesting makes the types line up
       | null givens && null matches
-      = ptext (sLit "No instance for")  <+> pprParendType pred
+      = ptext (sLit "No instance for")
+        <+> pprParendType pred
+        $$ if type_has_arrow pred
+            then nest 2 $ ptext (sLit "(maybe you haven't applied enough arguments to a function?)")
+            else empty
+
       | otherwise
       = ptext (sLit "Could not deduce") <+> pprParendType pred
 
+    type_has_arrow (TyVarTy _)      = False
+    type_has_arrow (AppTy t1 t2)    = type_has_arrow t1 || type_has_arrow t2
+    type_has_arrow (TyConApp _ ts)  = or $ map type_has_arrow ts
+    type_has_arrow (FunTy _ _)      = True
+    type_has_arrow (ForAllTy _ t)   = type_has_arrow t
+    type_has_arrow (LitTy _)        = False
+
     drv_fixes = case orig of
                    DerivOrigin      -> [drv_fix]
                    DerivOriginDC {} -> [drv_fix]
index b2d119d..0449033 100644 (file)
@@ -1,9 +1,12 @@
 
 annfail08.hs:9:1:
     No instance for (Data.Data.Data (a0 -> a0))
+      (maybe you haven't applied enough arguments to a function?)
       arising from an annotation
     In the annotation: {-# ANN f (id + 1) #-}
 
 annfail08.hs:9:15:
-    No instance for (Num (a0 -> a0)) arising from a use of ‘+’
+    No instance for (Num (a0 -> a0))
+      (maybe you haven't applied enough arguments to a function?)
+      arising from a use of ‘+’
     In the annotation: {-# ANN f (id + 1) #-}
index 183a5ff..91cf8bf 100644 (file)
@@ -1,6 +1,7 @@
 
 drvfail007.hs:4:38:
     No instance for (Eq (Int -> Int))
+      (maybe you haven't applied enough arguments to a function?)
       arising from the first field of ‘Foo’ (type ‘Int -> Int’)
     Possible fix:
       use a standalone 'deriving instance' declaration,
index 5baf41b..7a9c08e 100644 (file)
@@ -1,4 +1,6 @@
 
 <interactive>:5:1:
-    No instance for (Show (t -> t1)) arising from a use of ‘print’
+    No instance for (Show (t -> t1))
+      (maybe you haven't applied enough arguments to a function?)
+      arising from a use of ‘print’
     In a stmt of an interactive GHCi command: print it
index cbd2f3b..4f58b02 100644 (file)
@@ -27,7 +27,9 @@
     In an equation for ‘c’: c (C2 x) = True
 
 ../../typecheck/should_run/Defer01.hs:28:5: Warning:
-    No instance for (Num (a -> a)) arising from the literal ‘1’
+    No instance for (Num (a -> a))
+      (maybe you haven't applied enough arguments to a function?)
+      arising from the literal ‘1’
     In the expression: 1
     In an equation for ‘d’: d = 1
 
     In the first argument of ‘c’, namely ‘(C2 True)’
     In the first argument of ‘print’, namely ‘(c (C2 True))’
 *** Exception: ../../typecheck/should_run/Defer01.hs:28:5:
-    No instance for (Num (a -> a)) arising from the literal ‘1’
+    No instance for (Num (a -> a))
+      (maybe you haven't applied enough arguments to a function?)
+      arising from the literal ‘1’
     In the expression: 1
     In an equation for ‘d’: d = 1
 (deferred type error)
index 5481291..594fea2 100644 (file)
@@ -1,2 +1,4 @@
 
-mdofail005.hs:11:14: parse error on input ‘<-’
+mdofail005.hs:11:14:
+    parse error on input ‘<-’
+    Perhaps this statement should be within a 'do' block?
index 147c8fe..5a3f1cc 100644 (file)
@@ -1,2 +1,2 @@
 
-ParserNoLambdaCase.hs:3:6: parse error on input ‘case’
+ParserNoLambdaCase.hs:3:5: parse error: naked lambda expression ''
index 0e3bde4..0c00cdc 100644 (file)
@@ -1,2 +1,3 @@
 
-readFail020.hs:3:16: parse error on input ‘}’
+readFail020.hs:3:5:
+    parse error in let binding: missing required 'in'
index 6cbb3ce..728090b 100644 (file)
@@ -1,2 +1,4 @@
 
-readFail040.hs:7:11: parse error on input ‘<-’
+readFail040.hs:7:11:
+    parse error on input ‘<-’
+    Perhaps this statement should be within a 'do' block?
index a02563f..b9ae820 100644 (file)
@@ -1,6 +1,7 @@
 
 rebindable6.hs:106:17:
     No instance for (HasSeq (IO a -> t0 -> IO b))
+      (maybe you haven't applied enough arguments to a function?)
       arising from a do statement
     The type variable ‘t0’ is ambiguous
     Relevant bindings include
@@ -24,6 +25,7 @@ rebindable6.hs:106:17:
 
 rebindable6.hs:107:17:
     No instance for (HasFail ([Prelude.Char] -> t1))
+      (maybe you haven't applied enough arguments to a function?)
       arising from a do statement
     The type variable ‘t1’ is ambiguous
     Note: there is a potential instance available:
@@ -42,6 +44,7 @@ rebindable6.hs:107:17:
 
 rebindable6.hs:108:17:
     No instance for (HasReturn (b -> t1))
+      (maybe you haven't applied enough arguments to a function?)
       arising from a use of ‘return’
     The type variable ‘t1’ is ambiguous
     Relevant bindings include
index 34d24ae..ccf4f14 100644 (file)
@@ -1,5 +1,7 @@
 
 T2846b.hs:5:5:
-    No instance for (Show (Num a0 => a0)) arising from a use of ‘show’
+    No instance for (Show (Num a0 => a0))
+      (maybe you haven't applied enough arguments to a function?)
+      arising from a use of ‘show’
     In the expression: show ([1, 2, 3] :: [Num a => a])
     In an equation for ‘f’: f = show ([1, 2, 3] :: [Num a => a])