packages feed

hs-bindgen-1.0.0.0: src-internal/HsBindgen/Frontend/LanguageC/Error.hs

-- | Errors arising from interacting with @language-c@
--
-- Defined in its own module to avoid cyclic module dependencies.
module HsBindgen.Frontend.LanguageC.Error (
    Error(..)
  ) where

import GHC.Stack
import Text.SimplePrettyPrint qualified as PP

import HsBindgen.Util.Tracer

{-------------------------------------------------------------------------------
  Definition
-------------------------------------------------------------------------------}

data Error =
    -- | We encountered something unexpected in the AST from language-c
    --
    -- This indicates a bug or something not yet implemented.
    UpdateUnexpected CallStack String

    -- | We encountered something in the language-c AST we don't not support
    --
    -- It is useful to distinguish known-to-be-unsupported from
    -- unknown-to-be-supported ('UpdateUnexpected'): the latter indicates that
    -- it's simply something we haven't considered yet, the former is a
    -- conscious decision about features we currently don't want to support.
  | UpdateUnsupported String

    -- | We encountered something in the language-c AST that allows us to skip
    -- the current reparse
    --
    -- The reparser constructs an hs-bindgen AST from a language-c AST,
    -- inserting references to macro types. We can skip the current reparse if
    -- we are in a known case where no references to macro types can be inserted
    -- anymore, which would mean reparsing is just the identity funcion. Such is
    -- the case when we try to reparse a type that references a struct or union.
    -- In such cases skipping is useful because a reference to a *nested*
    -- struct\/union would otherwise incur an 'UpdateUnsupported' or
    -- 'UpdateUnexpected' error.
    --
    -- We reparse four types of declarations:
    -- * global variable declarations
    -- * typedef declarations
    -- * function declarations
    -- * struct/union field declarations
    --
    -- The types of nesting that can occur are:
    -- * struct/union (field) nested in global variable
    -- * struct/union (field) nested in typedef
    -- * struct/union (field) nested in function
    -- * struct/union (field) nested in struct/union field
    --
    -- An enclosing declaration can not contain both a reference to a (nested)
    -- struct or (nested) union, and a reference to a macro type. Therefore, we
    -- know that we can skip reparsing if we identify a reference to a struct or
    -- union.
    --
    -- === Example
    --
    -- For example, if we are reparsing typedef @foo@:
    --
    -- > #define MyInt int
    -- > #define MyConst const
    -- > typedef MyConst struct S { MyInt x; } * foo;
    --
    -- Even though @MyConst@ is referenced from @foo@, it is not parsed as a
    -- macro, so there is nothing to do for the reparser there. Then we can skip
    -- reparsing @foo@ once we see that @foo@ references struct @S@, because we
    -- will reparse the @x@ field of struct @S@ separately.
    --
  | UpdateSkipped String
  deriving stock (Show)

instance PrettyForTrace Error where
  prettyForTrace (UpdateUnexpected cs str) = PP.vsep [
        PP.hsep [
            "Encountered unexpected node in the language-c AST: "
          , PP.string str
          ]
      , PP.string (prettyCallStack cs)
      ]
  prettyForTrace (UpdateUnsupported err) =
        PP.hsep [
            "Unsupported: "
          , PP.string err
          ]
  prettyForTrace (UpdateSkipped msg) =
        PP.hsep [
            "Skipped: "
          , PP.string msg
          ]

-- | Unsupported features are warnings
instance IsTrace Level Error where
  getDefaultLogLevel = \case
      UpdateUnexpected{}  -> Info
      UpdateUnsupported{} -> Info
      UpdateSkipped{}     -> Debug
  getSource  = const HsBindgen
  getTraceId _ = "parse-macro"