[Git][ghc/ghc][master] 3 commits: Improve parser error messages for the @-operator

Marge Bot gitlab at gitlab.haskell.org
Mon Jun 1 10:41:30 UTC 2020



 Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC


Commits:
730fcd54 by Vladislav Zavialov at 2020-06-01T06:41:18-04:00
Improve parser error messages for the @-operator

Since GHC diverges from the Haskell Report by allowing the user
to define (@) as an infix operator, we better give a good
error message when the user does so unintentionally.

In general, this is rather hard to do, as some failures will be
discovered only in the renamer or the type checker:

	x :: (Integer, Integer)
	x @ (a, b) = (1, 2)

This patch does *not* address this general case.

However, it gives much better error messages when the binding
is not syntactically valid:

	pairs xs @ (_:xs') = zip xs xs'

Before this patch, the error message was rather puzzling:

	<interactive>:1:1: error: Parse error in pattern: pairs

After this patch, the error message includes a hint:

	<interactive>:1:1: error:
	    Parse error in pattern: pairs
	    In a function binding for the ‘@’ operator.
	    Perhaps you meant an as-pattern, which must not be surrounded by whitespace

- - - - -
0fde5377 by Vladislav Zavialov at 2020-06-01T06:41:18-04:00
Improve parser error messages for TypeApplications

With this patch, we always parse  f @t  as a type application,
thereby producing better error messages.

This steals two syntactic forms:

* Prefix form of the @-operator in expressions. Since the @-operator is
  a divergence from the Haskell Report anyway, this is not a major loss.

* Prefix form of @-patterns. Since we are stealing loose infix form
  anyway, might as well sacrifice the prefix form for the sake of much
  better error messages.

- - - - -
c68e7e1e by Vladislav Zavialov at 2020-06-01T06:41:18-04:00
Improve parser error messages for TemplateHaskellQuotes

While [e| |], [t| |], [d| |], and so on, steal syntax from list
comprehensions, [| |] and [|| ||] do not steal any syntax.

Thus we can improve error messages by always accepting them in the
lexer. Turns out the renamer already performs necessary validation.

- - - - -


19 changed files:

- compiler/GHC/Parser.y
- compiler/GHC/Parser/Lexer.x
- compiler/GHC/Parser/PostProcess.hs
- docs/users_guide/bugs.rst
- + testsuite/tests/parser/should_fail/T18251a.hs
- + testsuite/tests/parser/should_fail/T18251a.stderr
- + testsuite/tests/parser/should_fail/T18251b.hs
- + testsuite/tests/parser/should_fail/T18251b.stderr
- + testsuite/tests/parser/should_fail/T18251c.hs
- + testsuite/tests/parser/should_fail/T18251c.stderr
- + testsuite/tests/parser/should_fail/T18251d.hs
- + testsuite/tests/parser/should_fail/T18251d.stderr
- + testsuite/tests/parser/should_fail/T18251e.hs
- + testsuite/tests/parser/should_fail/T18251e.stderr
- + testsuite/tests/parser/should_fail/T18251f.hs
- + testsuite/tests/parser/should_fail/T18251f.stderr
- testsuite/tests/parser/should_fail/all.T
- testsuite/tests/th/T12411.stderr
- testsuite/tests/typecheck/should_fail/T15527.stderr


Changes:

=====================================
compiler/GHC/Parser.y
=====================================
@@ -2738,11 +2738,9 @@ fexp    :: { ECP }
                                           mkHsAppPV (comb2 $1 $>) $1 $2 }
 
         -- See Note [Whitespace-sensitive operator parsing] in GHC.Parser.Lexer
-        | fexp PREFIX_AT atype       {% runECP_P $1 >>= \ $1 ->
-                                        runPV (checkExpBlockArguments $1) >>= \_ ->
-                                        fmap ecpFromExp $
-                                        ams (sLL $1 $> $ HsAppType noExtField $1 (mkHsWildCardBndrs $3))
-                                            [mj AnnAt $2] }
+        | fexp PREFIX_AT atype       { ECP $
+                                        runECP_PV $1 >>= \ $1 ->
+                                        amms (mkHsAppTypePV (comb2 $1 $>) $1 $3) [mj AnnAt $2] }
 
         | 'static' aexp              {% runECP_P $2 >>= \ $2 ->
                                         fmap ecpFromExp $


=====================================
compiler/GHC/Parser/Lexer.x
=====================================
@@ -366,15 +366,20 @@ $tab          { warnTab }
 -- "special" symbols
 
 <0> {
-  "[|"        / { ifExtension ThQuotesBit } { token (ITopenExpQuote NoE NormalSyntax) }
-  "[||"       / { ifExtension ThQuotesBit } { token (ITopenTExpQuote NoE) }
+
+  -- Don't check ThQuotesBit here as the renamer can produce a better
+  -- error message than the lexer (see the thQuotesEnabled check in rnBracket).
+  "[|"  { token (ITopenExpQuote NoE NormalSyntax) }
+  "[||" { token (ITopenTExpQuote NoE) }
+  "|]"  { token (ITcloseQuote NormalSyntax) }
+  "||]" { token ITcloseTExpQuote }
+
+  -- Check ThQuotesBit here as to not steal syntax.
   "[e|"       / { ifExtension ThQuotesBit } { token (ITopenExpQuote HasE NormalSyntax) }
   "[e||"      / { ifExtension ThQuotesBit } { token (ITopenTExpQuote HasE) }
   "[p|"       / { ifExtension ThQuotesBit } { token ITopenPatQuote }
   "[d|"       / { ifExtension ThQuotesBit } { layout_token ITopenDecQuote }
   "[t|"       / { ifExtension ThQuotesBit } { token ITopenTypQuote }
-  "|]"        / { ifExtension ThQuotesBit } { token (ITcloseQuote NormalSyntax) }
-  "||]"       / { ifExtension ThQuotesBit } { token ITcloseTExpQuote }
 
   "[" @varid "|"  / { ifExtension QqBit }   { lex_quasiquote_tok }
 
@@ -1449,7 +1454,7 @@ qconsym buf len = ITqconsym $! splitQualName buf len False
 -- See Note [Whitespace-sensitive operator parsing]
 varsym_prefix :: Action
 varsym_prefix = sym $ \exts s ->
-  if | TypeApplicationsBit `xtest` exts, s == fsLit "@"
+  if | s == fsLit "@"  -- regardless of TypeApplications for better error messages
      -> return ITtypeApp
      | ThQuotesBit `xtest` exts, s == fsLit "$"
      -> return ITdollar
@@ -2461,7 +2466,6 @@ data ExtBits
   | BinaryLiteralsBit
   | NegativeLiteralsBit
   | HexFloatLiteralsBit
-  | TypeApplicationsBit
   | StaticPointersBit
   | NumericUnderscoresBit
   | StarIsTypeBit
@@ -2548,7 +2552,6 @@ mkParserFlags' warningFlags extensionFlags thisPackage
       .|. NegativeLiteralsBit         `xoptBit` LangExt.NegativeLiterals
       .|. HexFloatLiteralsBit         `xoptBit` LangExt.HexFloatLiterals
       .|. PatternSynonymsBit          `xoptBit` LangExt.PatternSynonyms
-      .|. TypeApplicationsBit         `xoptBit` LangExt.TypeApplications
       .|. StaticPointersBit           `xoptBit` LangExt.StaticPointers
       .|. NumericUnderscoresBit       `xoptBit` LangExt.NumericUnderscores
       .|. StarIsTypeBit               `xoptBit` LangExt.StarIsType


=====================================
compiler/GHC/Parser/PostProcess.hs
=====================================
@@ -1137,6 +1137,13 @@ checkAPat loc e0 = do
                       | nPlusKPatterns && (plus == plus_RDR)
                       -> return (mkNPlusKPat (L nloc n) (L lloc lit))
 
+   -- Improve error messages for the @-operator when the user meant an @-pattern
+   PatBuilderOpApp _ op _ | opIsAt (unLoc op) -> do
+     addError (getLoc op) $
+       text "Found a binding for the" <+> quotes (ppr op) <+> text "operator in a pattern position." $$
+       perhaps_as_pat
+     return (WildPat noExtField)
+
    PatBuilderOpApp l (L cl c) r
      | isRdrDataCon c -> do
          l <- checkLPat l
@@ -1171,6 +1178,9 @@ patFail loc e = addFatalError loc $ text "Parse error in pattern:" <+> ppr e
 patIsRec :: RdrName -> Bool
 patIsRec e = e == mkUnqual varName (fsLit "rec")
 
+opIsAt :: RdrName -> Bool
+opIsAt e = e == mkUnqual varName (fsLit "@")
+
 ---------------------------------------------------------------------------
 -- Check Equation Syntax
 
@@ -1203,7 +1213,7 @@ checkFunBind :: SrcStrictness
              -> Located (GRHSs GhcPs (LHsExpr GhcPs))
              -> P ([AddAnn],HsBind GhcPs)
 checkFunBind strictness ann lhs_loc fun is_infix pats (L rhs_span grhss)
-  = do  ps <- mapM checkPattern pats
+  = do  ps <- runPV_msg param_hint (mapM checkLPat pats)
         let match_span = combineSrcSpans lhs_loc rhs_span
         -- Add back the annotations stripped from any HsPar values in the lhs
         -- mapM_ (\a -> a match_span) ann
@@ -1217,6 +1227,15 @@ checkFunBind strictness ann lhs_loc fun is_infix pats (L rhs_span grhss)
                                        , m_grhss = grhss })])
         -- The span of the match covers the entire equation.
         -- That isn't quite right, but it'll do for now.
+  where
+    param_hint
+      | Infix <- is_infix
+      = text "In a function binding for the" <+> quotes (ppr fun) <+> text "operator." $$
+        if opIsAt (unLoc fun) then perhaps_as_pat else empty
+      | otherwise = empty
+
+perhaps_as_pat :: SDoc
+perhaps_as_pat = text "Perhaps you meant an as-pattern, which must not be surrounded by whitespace"
 
 makeFunBind :: Located RdrName -> [LMatch GhcPs (LHsExpr GhcPs)]
             -> HsBind GhcPs
@@ -1792,6 +1811,8 @@ class b ~ (Body b) GhcPs => DisambECP b where
   superFunArg :: (DisambECP (FunArg b) => PV (Located b)) -> PV (Located b)
   -- | Disambiguate "f x" (function application)
   mkHsAppPV :: SrcSpan -> Located b -> Located (FunArg b) -> PV (Located b)
+  -- | Disambiguate "f @t" (visible type application)
+  mkHsAppTypePV :: SrcSpan -> Located b -> LHsType GhcPs -> PV (Located b)
   -- | Disambiguate "if ... then ... else ..."
   mkHsIfPV :: SrcSpan
          -> LHsExpr GhcPs
@@ -1906,6 +1927,7 @@ instance DisambECP (HsCmd GhcPs) where
     checkCmdBlockArguments c
     checkExpBlockArguments e
     return $ L l (HsCmdApp noExtField c e)
+  mkHsAppTypePV l c t = cmdFail l (ppr c <+> text "@" <> ppr t)
   mkHsIfPV l c semi1 a semi2 b = do
     checkDoAndIfThenElse c semi1 a semi2 b
     return $ L l (mkHsCmdIf c a b)
@@ -1963,6 +1985,9 @@ instance DisambECP (HsExpr GhcPs) where
     checkExpBlockArguments e1
     checkExpBlockArguments e2
     return $ L l (HsApp noExtField e1 e2)
+  mkHsAppTypePV l e t = do
+    checkExpBlockArguments e
+    return $ L l (HsAppType noExtField e (mkHsWildCardBndrs t))
   mkHsIfPV l c semi1 a semi2 b = do
     checkDoAndIfThenElse c semi1 a semi2 b
     return $ L l (mkHsIf c a b)
@@ -2045,6 +2070,8 @@ instance DisambECP (PatBuilder GhcPs) where
   type FunArg (PatBuilder GhcPs) = PatBuilder GhcPs
   superFunArg m = m
   mkHsAppPV l p1 p2 = return $ L l (PatBuilderApp p1 p2)
+  mkHsAppTypePV l _ _ = addFatalError l $
+    text "Type applications in patterns are not yet supported"
   mkHsIfPV l _ _ _ _ _ = addFatalError l $ text "(if ... then ... else ...)-syntax in pattern"
   mkHsDoPV l _ = addFatalError l $ text "do-notation in pattern"
   mkHsParPV l p = return $ L l (PatBuilderPar p)


=====================================
docs/users_guide/bugs.rst
=====================================
@@ -76,13 +76,20 @@ Lexical syntax
    See `GHC Proposal #229 <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0229-whitespace-bang-patterns.rst>`__
    for the precise rules.
 
--  As-patterns must not be surrounded by whitespace::
+-  As-patterns must not be surrounded by whitespace on either side::
 
      f p@(x, y, z) = ...    -- accepted by both GHC and the Haskell Report
-     f p @ (x, y, z) = ...  -- accepted by the Haskell Report but not GHC
 
-   When surrounded by whitespace, ``(@)`` is treated by GHC as a regular infix
-   operator.
+     -- accepted by the Haskell Report but not GHC:
+     f p @ (x, y, z) = ...
+     f p @(x, y, z) = ...
+     f p@ (x, y, z) = ...
+
+   When surrounded by whitespace on both sides, ``(@)`` is treated by GHC as a
+   regular infix operator.
+
+   When preceded but not followed by whitespace, ``(@)`` is treated as a
+   visible type application.
 
    See `GHC Proposal #229 <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0229-whitespace-bang-patterns.rst>`__
    for the precise rules.


=====================================
testsuite/tests/parser/should_fail/T18251a.hs
=====================================
@@ -0,0 +1,3 @@
+module T18251a where
+
+pairs xs @ (_:xs') = zip xs xs'


=====================================
testsuite/tests/parser/should_fail/T18251a.stderr
=====================================
@@ -0,0 +1,5 @@
+
+T18251a.hs:3:1: error:
+    Parse error in pattern: pairs
+    In a function binding for the ‘@’ operator.
+    Perhaps you meant an as-pattern, which must not be surrounded by whitespace


=====================================
testsuite/tests/parser/should_fail/T18251b.hs
=====================================
@@ -0,0 +1,3 @@
+module T18251a where
+
+pairs (xs @ (_:xs')) = zip xs xs'


=====================================
testsuite/tests/parser/should_fail/T18251b.stderr
=====================================
@@ -0,0 +1,4 @@
+
+T18251b.hs:3:11: error:
+    Found a binding for the ‘@’ operator in a pattern position.
+    Perhaps you meant an as-pattern, which must not be surrounded by whitespace


=====================================
testsuite/tests/parser/should_fail/T18251c.hs
=====================================
@@ -0,0 +1,3 @@
+module T18251c where
+
+f = id @Int


=====================================
testsuite/tests/parser/should_fail/T18251c.stderr
=====================================
@@ -0,0 +1,4 @@
+
+T18251c.hs:3:5: error:
+    Illegal visible type application ‘@Int’
+      Perhaps you intended to use TypeApplications


=====================================
testsuite/tests/parser/should_fail/T18251d.hs
=====================================
@@ -0,0 +1,6 @@
+{-# LANGUAGE ExplicitForAll #-}
+
+module T18251d where
+
+f :: forall a. a -> ()
+f @a _ = ()


=====================================
testsuite/tests/parser/should_fail/T18251d.stderr
=====================================
@@ -0,0 +1,3 @@
+
+T18251d.hs:6:1: error:
+    Type applications in patterns are not yet supported


=====================================
testsuite/tests/parser/should_fail/T18251e.hs
=====================================
@@ -0,0 +1,3 @@
+module T18251e where
+
+a = [| id |]


=====================================
testsuite/tests/parser/should_fail/T18251e.stderr
=====================================
@@ -0,0 +1,5 @@
+
+T18251e.hs:3:5: error:
+    • Syntax error on [| id |]
+      Perhaps you intended to use TemplateHaskell or TemplateHaskellQuotes
+    • In the Template Haskell quotation [| id |]


=====================================
testsuite/tests/parser/should_fail/T18251f.hs
=====================================
@@ -0,0 +1,3 @@
+module T18251f where
+
+f ! x y = x + y


=====================================
testsuite/tests/parser/should_fail/T18251f.stderr
=====================================
@@ -0,0 +1,4 @@
+
+T18251f.hs:3:5: error:
+    Parse error in pattern: x
+    In a function binding for the ‘!’ operator.


=====================================
testsuite/tests/parser/should_fail/all.T
=====================================
@@ -166,4 +166,10 @@ test('T17162', normal, compile_fail, [''])
 test('proposal-229c', normal, compile_fail, [''])
 test('T15730', normal, compile_fail, [''])
 test('T15730b', normal, compile_fail, [''])
-test('T18130Fail', normal, compile_fail, ['']) 
+test('T18130Fail', normal, compile_fail, [''])
+test('T18251a', normal, compile_fail, [''])
+test('T18251b', normal, compile_fail, [''])
+test('T18251c', normal, compile_fail, [''])
+test('T18251d', normal, compile_fail, [''])
+test('T18251e', normal, compile_fail, [''])
+test('T18251f', normal, compile_fail, [''])


=====================================
testsuite/tests/th/T12411.stderr
=====================================
@@ -1,8 +1,6 @@
 
-T12411.hs:4:6: error:
-    Variable not in scope:
-      (@)
-        :: (a1 -> f0 a1) -> t0 -> Language.Haskell.TH.Lib.Internal.DecsQ
+T12411.hs:4:1: error:
+    Illegal visible type application ‘@Q’
+      Perhaps you intended to use TypeApplications
 
-T12411.hs:4:7: error:
-    Data constructor not in scope: Q :: [a0] -> t0
+T12411.hs:4:7: error: Not in scope: type constructor or class ‘Q’


=====================================
testsuite/tests/typecheck/should_fail/T15527.stderr
=====================================
@@ -1,8 +1,4 @@
 
-T15527.hs:4:10: error:
-    Variable not in scope:
-      (@)
-        :: ((b0 -> c0) -> (a0 -> b0) -> a0 -> c0)
-           -> t0 -> (Int -> Int) -> (Int -> Int) -> Int -> Int
-
-T15527.hs:4:11: error: Data constructor not in scope: Int
+T15527.hs:4:6: error:
+    Illegal visible type application ‘@Int’
+      Perhaps you intended to use TypeApplications



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/95da76c2b9ffe2a4fb4230de0061918de3fc89a9...c68e7e1e4c0bddd8b07cd8a2b3651c8cbb4b7851

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/95da76c2b9ffe2a4fb4230de0061918de3fc89a9...c68e7e1e4c0bddd8b07cd8a2b3651c8cbb4b7851
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20200601/791efc3d/attachment-0001.html>


More information about the ghc-commits mailing list