packages feed

hic-0.0.0.1: src/Language/Cimple/Analysis/Errors.hs

{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE KindSignatures    #-}
{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}
module Language.Cimple.Analysis.Errors
    ( Context(..)
    , MismatchReason(..)
    , Qualifier(..)
    , MismatchContext(..)
    , MismatchDetail(..)
    , Provenance(..)
    , TypeError(..)
    , ErrorInfo(..)
    ) where

import           Data.Aeson                          (ToJSON (..), object, (.=))
import           Data.Text                           (Text)
import qualified Data.Text                           as T
import           GHC.Generics                        (Generic)
import           Language.Cimple                     (Lexeme (..), Node)
import           Language.Cimple.Analysis.TypeSystem (ArbitraryTemplateId (..),
                                                      Phase (..),
                                                      Qualifier (..), TypeInfo)
import           Prettyprinter                       (Doc, defaultLayoutOptions,
                                                      layoutPretty, unAnnotate)
import           Prettyprinter.Render.Terminal       (AnsiStyle)
import qualified Prettyprinter.Render.Text           as TR
import           Test.QuickCheck                     (Arbitrary (..), oneof,
                                                      scale)

-- | Context in which type checking is occurring
data Context (p :: Phase)
    = InFile FilePath
    | InFunction Text
    | InMacro Text
    | InMemberAccess Text
    | InExpr (Node (Lexeme Text))
    | InStmt (Node (Lexeme Text))
    | InInitializer (Node (Lexeme Text))
    | InUnification (TypeInfo p) (TypeInfo p) MismatchReason
    deriving (Show, Eq, Ord, Generic)

instance ArbitraryTemplateId p => Arbitrary (Context p) where
    arbitrary = oneof
        [ InFile <$> arbitrary
        , InFunction . T.pack <$> arbitrary
        , InMacro . T.pack <$> arbitrary
        , InMemberAccess . T.pack <$> arbitrary
        , InUnification <$> scale (\x -> x - 1) arbitrary <*> scale (\x -> x - 1) arbitrary <*> arbitrary
        ]

instance ToJSON (Context p)

-- | Reason for a type mismatch
data MismatchReason
    = GeneralMismatch
    | ReturnMismatch
    | ArgumentMismatch Int -- Index
    | AssignmentMismatch
    | InitializerMismatch
    deriving (Show, Eq, Ord, Generic)

instance Arbitrary MismatchReason where
    arbitrary = oneof
        [ return GeneralMismatch
        , return ReturnMismatch
        , ArgumentMismatch <$> arbitrary
        , return AssignmentMismatch
        , return InitializerMismatch
        ]

instance ToJSON MismatchReason

data MismatchContext
    = InPointer
    | InArray
    | InFunctionReturn
    | InFunctionParam Int
    deriving (Show, Eq, Ord, Generic)

instance Arbitrary MismatchContext where
    arbitrary = oneof
        [ return InPointer
        , return InArray
        , return InFunctionReturn
        , InFunctionParam <$> arbitrary
        ]

instance ToJSON MismatchContext

data MismatchDetail (p :: Phase)
    = MismatchDetail
        { mismatchExpected :: TypeInfo p
        , mismatchActual   :: TypeInfo p
        , mismatchReason   :: MismatchReason
        , mismatchInner    :: Maybe (MismatchContext, MismatchDetail p)
        }
    | MissingQualifier Qualifier (TypeInfo p) (TypeInfo p)
    | UnexpectedQualifier Qualifier (TypeInfo p) (TypeInfo p)
    | BaseMismatch (TypeInfo p) (TypeInfo p)
    | ArityMismatch Int Int -- Expected, Actual
    deriving (Show, Eq, Ord, Generic)

instance ToJSON (MismatchDetail p)

-- | Origin of a type or binding
data Provenance (p :: Phase)
    = FromDefinition Text (Maybe (Lexeme Text)) -- Symbol name and definition site
    | FromContext (ErrorInfo p)                     -- Context where binding happened
    | FromInference (Node (Lexeme Text))         -- Expression that caused inference
    | Builtin                                   -- Language builtin
    deriving (Show, Generic)

-- instance ToJSON Provenance -- ErrorInfo doesn't have ToJSON yet, might be complex due to Doc

-- | Structured type error
data TypeError (p :: Phase)
    = TypeMismatch (TypeInfo p) (TypeInfo p) MismatchReason (Maybe (MismatchDetail p))
    | UndefinedVariable Text
    | UndefinedType Text
    | MemberNotFound Text (TypeInfo p)
    | NotAStruct (TypeInfo p)
    | TooManyArgs { expectedCount :: Int, actualCount :: Int }
    | TooFewArgs { expectedCount :: Int, actualCount :: Int }
    | NotALValue
    | CallingNonFunction Text (TypeInfo p)
    | SwitchConditionNotIntegral (TypeInfo p)
    | DereferencingNonPointer (TypeInfo p)
    | ArrayAccessNonArray (TypeInfo p)
    | MacroArgumentMismatch Text Int Int -- Name, expected, actual
    | MissingReturnValue (TypeInfo p)
    | InfiniteType Text (TypeInfo p)
    | CustomError Text
    deriving (Show, Generic)

instance ToJSON (TypeError p)

-- | Error information with context
data ErrorInfo (p :: Phase) = ErrorInfo
    { errLoc         :: Maybe (Lexeme Text)
    , errContext     :: [Context p]
    , errType        :: TypeError p
    , errExplanation :: [Doc AnsiStyle]
    }
    deriving (Show, Generic)

instance ToJSON (ErrorInfo p) where
    toJSON ErrorInfo{..} = object
        [ "loc" .= errLoc
        , "context" .= errContext
        , "type" .= errType
        , "explanation" .= map (TR.renderStrict . layoutPretty defaultLayoutOptions . unAnnotate) errExplanation
        ]