diff --git a/src/Language/PureScript/AST/Declarations.hs b/src/Language/PureScript/AST/Declarations.hs index 9c820071dc..2725f5da67 100644 --- a/src/Language/PureScript/AST/Declarations.hs +++ b/src/Language/PureScript/AST/Declarations.hs @@ -124,7 +124,7 @@ data SimpleErrorMessage | DuplicateValueDeclaration Ident | ArgListLengthsDiffer Ident | OverlappingArgNames (Maybe Ident) - | MissingClassMember Ident + | MissingClassMember (NEL.NonEmpty (Ident, Type)) | ExtraneousClassMember Ident (Qualified (ProperName 'ClassName)) | ExpectedType Type Kind -- | constructor name, expected argument count, actual argument count diff --git a/src/Language/PureScript/Errors.hs b/src/Language/PureScript/Errors.hs index 135f98aa2f..2eb422d1d5 100644 --- a/src/Language/PureScript/Errors.hs +++ b/src/Language/PureScript/Errors.hs @@ -753,8 +753,12 @@ prettyPrintSingleError (PPEOptions codeColor full level showDocs relPath) e = fl line $ "Argument list lengths differ in declaration " <> markCode (showIdent ident) renderSimpleErrorMessage (OverlappingArgNames ident) = line $ "Overlapping names in function/binder" <> foldMap ((" in declaration " <>) . showIdent) ident - renderSimpleErrorMessage (MissingClassMember ident) = - line $ "Type class member " <> markCode (showIdent ident) <> " has not been implemented." + renderSimpleErrorMessage (MissingClassMember identsAndTypes) = + paras $ [ line "The following type class members have not been implemented:" + , Box.vcat Box.left + [ markCodeBox $ Box.text (T.unpack (showIdent ident)) Box.<> " :: " Box.<> typeAsBox ty + | (ident, ty) <- NEL.toList identsAndTypes ] + ] renderSimpleErrorMessage (ExtraneousClassMember ident className) = line $ "" <> markCode (showIdent ident) <> " is not a member of type class " <> markCode (showQualified runProperName className) renderSimpleErrorMessage (ExpectedType ty kind) = diff --git a/src/Language/PureScript/Sugar/TypeClasses.hs b/src/Language/PureScript/Sugar/TypeClasses.hs index a6c80750b7..0645bcde7d 100644 --- a/src/Language/PureScript/Sugar/TypeClasses.hs +++ b/src/Language/PureScript/Sugar/TypeClasses.hs @@ -14,9 +14,11 @@ import Control.Arrow (first, second) import Control.Monad.Error.Class (MonadError(..)) import Control.Monad.State import Control.Monad.Supply.Class -import Data.List ((\\), find, sortBy) +import Data.List (find, sortBy) import qualified Data.Map as M import Data.Maybe (catMaybes, mapMaybe, isJust, fromMaybe) +import qualified Data.List.NonEmpty as NEL +import qualified Data.Set as S import Data.Text (Text) import qualified Language.PureScript.Constants as C import Language.PureScript.Crash @@ -285,12 +287,14 @@ typeInstanceDictionaryDeclaration sa@(ss, _) name mn deps className tys decls = maybe (throwError . errorMessage' ss . UnknownName $ fmap TyClassName className) return $ M.lookup (qualify mn className) m - case map fst typeClassMembers \\ mapMaybe declIdent decls of - member : _ -> throwError . errorMessage' ss $ MissingClassMember member - [] -> do - -- Replace the type arguments with the appropriate types in the member types - let memberTypes = map (second (replaceAllTypeVars (zip (map fst typeClassArguments) tys))) typeClassMembers + -- Replace the type arguments with the appropriate types in the member types + let memberTypes = map (second (replaceAllTypeVars (zip (map fst typeClassArguments) tys))) typeClassMembers + + let declaredMembers = S.fromList $ mapMaybe declIdent decls + case filter (\(ident, _) -> not $ S.member ident declaredMembers) memberTypes of + hd : tl -> throwError . errorMessage' ss $ MissingClassMember (hd NEL.:| tl) + [] -> do -- Create values for the type instance members members <- zip (map typeClassMemberName decls) <$> traverse (memberToValue memberTypes) decls diff --git a/tests/purs/failing/MissingClassMember.purs b/tests/purs/failing/MissingClassMember.purs index 488fccfc99..42a06a927f 100644 --- a/tests/purs/failing/MissingClassMember.purs +++ b/tests/purs/failing/MissingClassMember.purs @@ -1,11 +1,10 @@ -- @shouldFailWith MissingClassMember module Main where -import Prelude - class A a where a :: a -> String b :: a -> Number + c :: forall f. a -> f a instance aString :: A String where a s = s