[Git][ghc/ghc][wip/21101] Improve error message for record wildcards with fieldless constructors
Sasha Bogicevic pushed to branch wip/21101 at Glasgow Haskell Compiler / GHC Commits: 3ccd4e66 by Sasha Bogicevic at 2026-07-14T17:21:11+02:00 Improve error message for record wildcards with fieldless constructors TcRnIllegalWildcardsInConstructor now stores a RecordFieldPart, so the message distinguishes record patterns from record constructions, and its suggested fixes are structured GhcHints (SuggestEmptyRecordBraces, SuggestExplicitConstructorArguments) that tools like HLS can turn into code actions. Storing HsRecFieldContext directly is not possible: GHC.Tc.Errors.Types is reachable from the parser via GHC.Types.Error.Codes, while GHC.Rename.Pat depends on the parser — so we reuse the existing RecordFieldPart mirror and toRecordFieldPart. Fixes #21101 - - - - - 12 changed files: - compiler/GHC/Rename/Pat.hs - compiler/GHC/Tc/Errors/Ppr.hs - compiler/GHC/Tc/Errors/Types.hs - compiler/GHC/Types/Hint.hs - compiler/GHC/Types/Hint/Ppr.hs - + testsuite/tests/rename/should_fail/T21101.hs - + testsuite/tests/rename/should_fail/T21101.stderr - testsuite/tests/rename/should_fail/T9815.stderr - testsuite/tests/rename/should_fail/T9815b.stderr - testsuite/tests/rename/should_fail/T9815bghci.stderr - testsuite/tests/rename/should_fail/T9815ghci.stderr - testsuite/tests/rename/should_fail/all.T Changes: ===================================== compiler/GHC/Rename/Pat.hs ===================================== @@ -874,7 +874,7 @@ rnHsRecFields ctxt mk_arg (HsRecFields { rec_flds = flds, rec_dotdot = dotdot }) ; checkErr dd_flag (needFlagDotDot ctxt) ; (rdr_env, lcl_env) <- getRdrEnvs ; conInfo <- lookupConstructorInfo qcon - ; when (conFieldInfo conInfo == ConHasPositionalArgs) (addErr (TcRnIllegalWildcardsInConstructor con)) + ; when (conFieldInfo conInfo == ConHasPositionalArgs) (addErr (TcRnIllegalWildcardsInConstructor (toRecordFieldPart ctxt) con)) ; let present_flds = mkOccSet $ map rdrNameOcc (getFieldRdrs flds) -- For constructor uses (but not patterns) ===================================== compiler/GHC/Tc/Errors/Ppr.hs ===================================== @@ -357,18 +357,12 @@ instance Diagnostic TcRnMessage where -> mkSimpleDecorated $ vcat [text "Illegal view pattern: " <+> ppr pat] TcRnCharLiteralOutOfRange c -> mkSimpleDecorated $ text "character literal out of range: '\\" <> char c <> char '\'' - TcRnIllegalWildcardsInConstructor con - -> mkSimpleDecorated $ - vcat [ text "The data constructor" - <+> quotes (ppr con) - <+> text "does not have named record fields, so a pattern match" - <+> quotes (ppr con) - <+> text " { .. } is incorrect." - , text "Possible fixes:" - , nest 2 (text "* Replace the pattern '" <+> quotes (ppr con) <+> text "'{ .. }' with" <+> quotes (ppr con)) - , nest 2 (text "* Replace the pattern '" <+> quotes (ppr con) <+> text "'{ .. }' with" <+> quotes (ppr con) <+> text "{}") - , nest 4 (text "This version works even if you add/remove fields to " <+> quotes (ppr con) <+> text "later") - ] + TcRnIllegalWildcardsInConstructor ctx con + -> mkSimpleDecorated $ + text "The data constructor" <+> quotes (ppr con) + <+> text "does not have named record fields, so the record" + <+> pprRecordFieldPart ctx + <+> quotes (ppr con <> text "{..}") <+> text "is invalid." TcRnIgnoringAnnotations anns -> mkSimpleDecorated $ text "Ignoring ANN annotation" <> plural anns <> comma @@ -2797,8 +2791,10 @@ instance Diagnostic TcRnMessage where -> [suggestExtension LangExt.ViewPatterns] TcRnCharLiteralOutOfRange{} -> noHints - TcRnIllegalWildcardsInConstructor{} - -> noHints + TcRnIllegalWildcardsInConstructor ctx con + -> case ctx of + RecordFieldPattern{} -> [SuggestEmptyRecordBraces con] + _ -> [SuggestExplicitConstructorArguments con] TcRnIgnoringAnnotations{} -> noHints TcRnAnnotationInSafeHaskell ===================================== compiler/GHC/Tc/Errors/Types.hs ===================================== @@ -817,17 +817,32 @@ data TcRnMessage where TcRnNegativeNumTypeLiteral :: IntegralLit GhcRn -> TcRnMessage {-| TcRnIllegalWildcardsInConstructor is an error that occurs whenever - the record wildcards '..' are used inside a constructor without labeled fields. + the record wildcards '..' are used with a constructor whose fields are + positional (unlabelled). The 'RecordFieldPart' field records whether + the wildcards occurred in a record construction (an expression) or in + a record pattern, so that the message and its suggested fixes can be + worded accordingly. Constructors with no fields at all do not trigger + this error: since GHC proposal 496 ("Nullary record wildcards"), + @C {..}@ is legal for nullary constructors. - Examples(s): None + Example(s): + + data D = D Int Bool + + f :: D -> () + f D{..} = () -- record pattern + + g :: D + g = D{..} -- record construction Test cases: rename/should_fail/T9815.hs rename/should_fail/T9815b.hs rename/should_fail/T9815ghci.hs rename/should_fail/T9815bghci.hs + rename/should_fail/T21101.hs -} - TcRnIllegalWildcardsInConstructor :: !Name -> TcRnMessage + TcRnIllegalWildcardsInConstructor :: !RecordFieldPart -> !Name -> TcRnMessage {-| TcRnIgnoringAnnotations is a warning that occurs when the source code contains annotation pragmas but the platform in use does not support an ===================================== compiler/GHC/Types/Hint.hs ===================================== @@ -548,6 +548,22 @@ data GhcHint | SuggestUpgradeForSemaphoreVersionMismatch !SemaphoreUpgradeTarget !Int -- ^ The 'Int' is the required protocol version. + {-| Suggest replacing a record wildcard pattern @C {..}@ with @C {}@, + which matches a constructor without binding its fields. + + Triggered by 'GHC.Tc.Errors.Types.TcRnIllegalWildcardsInConstructor' + in a record pattern. + -} + | SuggestEmptyRecordBraces !Name + + {-| Suggest applying a constructor directly to its arguments instead + of record syntax, for constructors without labelled fields. + + Triggered by 'GHC.Tc.Errors.Types.TcRnIllegalWildcardsInConstructor' + in a record construction. + -} + | SuggestExplicitConstructorArguments !Name + -- | What the user should upgrade to resolve an @-jsem@ semaphore -- protocol version mismatch. data SemaphoreUpgradeTarget ===================================== compiler/GHC/Types/Hint/Ppr.hs ===================================== @@ -345,6 +345,11 @@ instance Outputable GhcHint where text "The jobserver uses a newer semaphore protocol than this GHC." $$ (text "Upgrade GHC to a version that supports semaphore protocol v" <> int required <> text " to resolve this.") + SuggestEmptyRecordBraces con + -> text "Use" <+> quotes (ppr con <> text "{}") <+> text "instead," + <+> text "which matches" <+> quotes (ppr con) <+> text "regardless of its fields" + SuggestExplicitConstructorArguments con + -> text "Apply" <+> quotes (ppr con) <+> text "to its arguments instead" perhapsAsPat :: SDoc perhapsAsPat = text "Perhaps you meant an as-pattern, which must not be surrounded by whitespace" ===================================== testsuite/tests/rename/should_fail/T21101.hs ===================================== @@ -0,0 +1,7 @@ +{-# LANGUAGE RecordWildCards #-} +module T21101 where + +data D = D Int Bool + +f :: D -> () +f D{..} = () ===================================== testsuite/tests/rename/should_fail/T21101.stderr ===================================== @@ -0,0 +1,5 @@ +T21101.hs:7:3: error: [GHC-47217] + The data constructor ‘D’ does not have named record fields, so the record pattern ‘D{..}’ is invalid. + Suggested fix: + Use ‘D{}’ instead, which matches ‘D’ regardless of its fields + ===================================== testsuite/tests/rename/should_fail/T9815.stderr ===================================== @@ -1,5 +1,4 @@ - T9815.hs:6:13: error: [GHC-47217] - Illegal `{..}' notation for constructor ‘N’ - Record wildcards may not be used for constructors with unlabelled fields. - Possible fix: Remove the `{..}' and add a match for each field of the constructor. + The data constructor ‘N’ does not have named record fields, so the record construction ‘N{..}’ is invalid. + Suggested fix: Apply ‘N’ to its arguments instead + ===================================== testsuite/tests/rename/should_fail/T9815b.stderr ===================================== @@ -1,5 +1,4 @@ - T9815.hs:6:13: error: [GHC-47217] - Illegal `{..}' notation for constructor ‘N’ - Record wildcards may not be used for constructors with unlabelled fields. - Possible fix: Remove the `{..}' and add a match for each field of the constructor. + The data constructor ‘N’ does not have named record fields, so the record construction ‘N{..}’ is invalid. + Suggested fix: Apply ‘N’ to its arguments instead + ===================================== testsuite/tests/rename/should_fail/T9815bghci.stderr ===================================== @@ -1,5 +1,4 @@ +<interactive>:5:7: error: [GHC-47217] + The data constructor ‘Arg’ does not have named record fields, so the record construction ‘Arg{..}’ is invalid. + Suggested fix: Apply ‘Arg’ to its arguments instead -<interactive>:5:7: [GHC-47217] - Illegal `{..}' notation for constructor ‘Arg’ - Record wildcards may not be used for constructors with unlabelled fields. - Possible fix: Remove the `{..}' and add a match for each field of the constructor. ===================================== testsuite/tests/rename/should_fail/T9815ghci.stderr ===================================== @@ -1,5 +1,4 @@ +<interactive>:3:7: error: [GHC-47217] + The data constructor ‘Data.Semigroup.Arg’ does not have named record fields, so the record construction ‘Data.Semigroup.Arg{..}’ is invalid. + Suggested fix: Apply ‘Data.Semigroup.Arg’ to its arguments instead -<interactive>:3:7: [GHC-47217] - Illegal `{..}' notation for constructor ‘Data.Semigroup.Arg’ - Record wildcards may not be used for constructors with unlabelled fields. - Possible fix: Remove the `{..}' and add a match for each field of the constructor. ===================================== testsuite/tests/rename/should_fail/all.T ===================================== @@ -186,6 +186,7 @@ test('T18138', normal, compile_fail, ['']) test('T20147', normal, compile_fail, ['']) test('RnEmptyStatementGroup1', normal, compile_fail, ['']) test('RnImplicitBindInMdoNotation', normal, compile_fail, ['']) +test('T21101', normal, compile_fail, ['']) test('T21605a', normal, compile_fail, ['']) test('T21605b', normal, compile_fail, ['']) test('T21605c', normal, compile_fail, ['']) View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/3ccd4e661b84f772470a9f0ef01af9cd... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/3ccd4e661b84f772470a9f0ef01af9cd... You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
participants (1)
-
Sasha Bogicevic (@Bogicevic)