hs-bindgen-1.0.0.0: src-internal/HsBindgen/Frontend/Pass/Parse/Msg.hs
-- | Parse messages
module HsBindgen.Frontend.Pass.Parse.Msg (
-- * Immediate parse messages
ImmediateParseMsg(..)
-- * Delayed parse messages
, DelayedParseMsg(..)
, UnsupportedFloatType(..)
, ParseImplicitFieldsMsg(..)
) where
import Control.Exception (Exception (..))
import Foreign.C (CInt)
import Text.SimplePrettyPrint ((><))
import Text.SimplePrettyPrint qualified as PP
import Clang.Enum.Simple
import Clang.HighLevel.Types
import Clang.LowLevel.Core
import Clang.Paths
import HsBindgen.Errors
import HsBindgen.Imports
import HsBindgen.IR.C qualified as C
import HsBindgen.Macro.Error
import HsBindgen.Util.Tracer
{-------------------------------------------------------------------------------
Immediate parse messages
-------------------------------------------------------------------------------}
-- | Parse messages that we emit immediately
--
-- Reasons for immediate emission:
--
-- - The info/debug message is useful to developers
--
-- - The declaration we fail to parse may affect other declarations
data ImmediateParseMsg =
-- | We do not support getting macro expansions for declarations spanning
-- multiple files.
ParseGetMacroExpansionsMultipleFiles (Range (SingleLoc RealPath))
-- | At a macro expansion site, we failed to get the name of the expanded
-- macro.
| ParseMacroExpansionNoMacroName
-- TODO <https://github.com/well-typed/hs-bindgen/issues/1820>
-- | We failed to parse a declaration that is required for scoping.
| ParseOfDeclarationRequiredForScopingFailed
-- | Source-order indices were populated, using
-- 'clang_isBeforeInTranslationUnit'
--
-- Source-order indices record the order in which declarations appear in the
-- C source. Populating them requires Clang >= 20.1.
| ParseSourceOrderPopulated
-- | Source-order indices could not be populated
--
-- 'clang_isBeforeInTranslationUnit' is not available; this requires
-- Clang >= 20.1.
| ParseSourceOrderUnavailable
-- | We failed to analyse the macro definitions for ambiguity
--
-- Either the definition could not be split, or the name of the split
-- definition disagrees with the name @libclang@ reported for the cursor.
-- The macros are treated as ambiguous.
--
-- The payload contains the names of the failed macros.
| ParseMacroDefinitionAnalysisFailed (NonEmpty Text)
deriving stock (Show, Eq, Ord, Generic)
instance PrettyForTrace ImmediateParseMsg where
prettyForTrace = \case
ParseGetMacroExpansionsMultipleFiles range -> PP.hsep [
"Could not get macro expansions"
, "(declaration spans multiple files):"
, PP.show range
]
ParseMacroExpansionNoMacroName ->
"Could not obtain macro name at macro expansion location"
ParseOfDeclarationRequiredForScopingFailed -> PP.hsep [
"Parse of declaration required for scoping failed;"
, "the failed declaration may be required when parsing"
, "other declarations containing macro replacements"
]
ParseSourceOrderPopulated -> PP.hsep [
"Source order of declarations populated"
, "(requires Clang >= 20.1)"
]
ParseSourceOrderUnavailable -> PP.hsep [
"Source order of declarations unavailable:"
, "clang_isBeforeInTranslationUnit requires Clang >= 20.1"
]
ParseMacroDefinitionAnalysisFailed names -> PP.hsep [
"Failed to analyse these macro definitions for ambiguity:"
, PP.string (show names)
]
instance IsTrace Level ImmediateParseMsg where
getDefaultLogLevel = \case
ParseGetMacroExpansionsMultipleFiles{} -> Warning
ParseMacroExpansionNoMacroName{} -> Bug
ParseOfDeclarationRequiredForScopingFailed{} -> Info
ParseSourceOrderPopulated{} -> Info
ParseSourceOrderUnavailable{} -> Info
ParseMacroDefinitionAnalysisFailed{} -> Bug
getSource = const HsBindgen
getTraceId = \case
ParseMacroExpansionNoMacroName -> "parse-immediate-macro"
ParseMacroDefinitionAnalysisFailed{} -> "parse-immediate-macro"
_otherwise -> "parse-immediate"
{-------------------------------------------------------------------------------
Delayed parse messages
-------------------------------------------------------------------------------}
-- Note to developers: We order delayed parse message constructors by
-- 1. Recursive/nested constructors come first
-- 2. Severity; debug messages come first
-- 3. Constructor name
-- | Delayed parse messages
--
-- We emit these parse messages only when we attempt to select the attached
-- declaration.
--
-- We distinguish between \"unsupported\", which refers to C features that one
-- could reasonably expect to be supported eventually, and \"unexpected\", for C
-- input we are not prepared for.
data DelayedParseMsg =
-- | Recursive case; we failed to parse the target of a @typedef@ with a
-- delayed parse message.
ParseUnderlyingTypeFailed C.PrelimDeclId DelayedParseMsg
-- | We tried to parse an implicit field for a struct or union object, but
-- it was unsuccessful
| ParseImplicitFieldFailed ParseImplicitFieldsMsg
-- | We did not attempt to parse a macro with an empty replacement list
-- (@#define FOO@)
--
-- Reported separately from 'ParseMacroErrorParse' because every include
-- guard has this shape, and not attempting to parse one is not a failure.
-- See 'HsBindgen.Frontend.Pass.Parse.IsPass.EmptyMacros'.
| ParseMacroEmpty C.PrelimDeclId [Token SourcePath TokenSpelling]
-- | We could not parse the macro (macro def sites)
| ParseMacroErrorParse MacroParseError
-- | Fully defined global variables and functions with external linkage.
--
-- Such definitions can lead to duplicate symbols (linker errors) if they
-- are included more than once in the same program. See the manual section
-- on globals for details.
--
-- Duplicate symbols can also exist across multiple shared libraries as long
-- as these symbols have public visibility. However, in such cases the
-- linker will pick one according to the rules of linker symbol
-- interposition, rather than throw a linker error. It can be surprising for
-- users if the linker picks an unexpected definition for the symbol they
-- are referencing. So, if a symbol has non-public visibility, the risk of
-- such surprises is mitigated somewhat. See the "Visibility" section in the
-- manual for more details.
| ParsePotentialDuplicateSymbol
-- | The symbol has public visibility
Bool
-- | Struct/union tag first declared inside a function prototype, e.g.
-- @void f(struct foo* arg);@
--
-- Might indicate a missing @#include@ in the header.
| ParseDeclarationNotVisible C.TagKind Text
-- | A function declaration was encountered where the type of the function
-- is typedef reference. This is not yet supported by hs-bindgen.
--
-- For example:
--
-- > typedef int int2int(int);
-- > extern int2int foo;
--
-- <https://github.com/well-typed/hs-bindgen/issues/1034>
| ParseFunctionOfTypeTypedef
| ParseInvalidLinkage
| ParseInvalidVisibility
-- | Failed to parse a declaration because some of its nested declarations
-- failed to parse
--
-- For example, to successfully parse a struct (or union), all of its
-- nested declarations have to be parsed successfully as well. If any
-- nested declaration fails to parse, then we could miss seeing some
-- (implicit) fields, and we can't generate Haskell bindings for structs
-- (or unions) with an incomplete list of fields.
| ParseNestedDeclsFailed
-- | A function declaration or global variable declaration has a problematic
-- case of non-public visibility that can lead to linker errors if the
-- symbol is defined in a shared library.
--
-- In such cases, we emit this message. Arguably declarations like these are
-- a bug in the C library, given the way that header files are @#include@d
-- in other header and body files.
--
-- Concretely, a linker error can occur for a declared symbol if it:
--
-- 1. has non-public visibility,
-- 2. has external linkage, and
-- 3. is not a definition (and there is no definition elsewhere in the
-- header).
--
-- For example:
--
-- > extern void __attribute__ ((visibility ("hidden"))) f (void);
-- > extern int __attribute__ ((visibility ("hidden"))) i;
| ParseNonPublicVisibility
-- | Declaration availability can not be determined.
--
-- That is 'Clang.LowLevel.Core.clang_getCursorAvailability' does not
-- provide a valid 'Clang.LowLevel.Core.CXAvailabilityKind'.
| ParseUnknownCursorAvailability (SimpleEnum CXAvailabilityKind)
-- | Variable declaration
| ParseUnknownStorageClass (SimpleEnum CX_StorageClass)
-- | Unexposed type
--
-- In some cases, @libclang@ does not expose information about a type. For
-- example, LLVM/Clang 22 gives the @size_t@ arguments and results of
-- library builtins such as @malloc@ and @strlen@ the predefined sugar type
-- @__size_t@, which its @libclang@ does not classify (see
-- <https://github.com/llvm/llvm-project/issues/192268>). LLVM/Clang 23
-- fixes this, by reporting these as 'CXType_PredefinedSugar' instead, which
-- we handle.
| ParseUnexposedType
-- | Unsupported unnamed declaration inside @extern@
--
-- Something like
--
-- > extern struct { .. } config;
--
-- does not make sense: this declares the existence of some externally
-- defined global variable, but it is impossible to actually define said
-- global variable; an attempt such as
--
-- > #include "config.h"
-- > struct { .. } config = ..
--
-- will result in an error: "conflicting types for @config@".
--
-- The /header/ however by itself will not result in a @clang@ warning, so
-- we detect the situation and warn the user in @hs-bindgen@.
--
-- (As of C23, the situation is different for /tagged/ structs:
-- multiple uses of a struct with the same tag are considered
-- compatible as of WG14-N3037.)
| ParseUnsupportedUnnamedInExtern
-- | Unsupported unnamed declaration inside function signature
--
-- Consider:
--
-- > void f(struct named { int x; int y; } arg);
-- > void g(struct { int x; int y; } arg);
--
-- Somewhat surprisingly, @clang@ warns about the first
--
-- > warning: declaration of 'struct named' will not be visible outside of
-- > this function
--
-- but accepts the second (untagged struct); @gcc@ warns in both cases:
--
-- > {‘struct named’, untagged struct} declared inside parameter list will
-- > not be visible outside of this definition or declaration
--
-- as does the C compiler used by vscode, which in both cases says
--
-- > type definition not allowed
--
-- It's not entirely clear if C23/WG14-N3037 affects this or not; @gcc@
-- warns about both declarations even with @-std=c2x@.
--
-- For our purposes, only the unnamed case is really problematic (we have
-- no way of assigning a tag to the struct). Since it is relatively clear
-- that the unnamed version is anyway unusable (callers would have no way
-- of constructing any values), we rule them out.
| ParseUnsupportedUnnamedInSignature
-- | Clang built-in declaration
| ParseUnsupportedBuiltin Text
-- | We do not support this floating-point type
| ParseUnsupportedFloatType UnsupportedFloatType
| ParseUnsupportedLinkage String CXLinkageKind
-- | Thread local variables
--
-- <https://github.com/well-typed/hs-bindgen/issues/828>
| ParseUnsupportedTLS
-- | We do not support variadic (varargs) functions
| ParseUnsupportedVariadicFunction
-- | We do not support SIMD vector types (yet)
| ParseUnsupportedVector
-- | Unusable unnamed declaration
--
-- When an unusable declaration appears in some outer declaration (say a
-- function signature), we will fail to \"parse\" that outer declaration
-- (actually this happens as a separate post-processing step in
-- @FillUnnamedIds@). We record the identifier of the unnamed declaration
-- here (that is, it's source location); the identifier of the outer
-- declaration is recorded in the encloding 'HsBindgen.Frontend.Pass.Parse.Result.ParseResult'.
| ParseUnusableUnnamedDecl C.UnnamedId
| ParseExpectedFunctionType String
-- | A macro definition has no name, even though macros can not be unnamed
| ParseMacroDefinitionNoMacroName
-- | Complex types can only be defined using primitive types, e.g.
-- @double complex@. @struct Point complex@ is not allowed.
| ParseUnexpectedComplexType CXType
-- | We encountered an unexpected cursor kind
--
-- Similar comments apply as for 'ParseUnexpectedTypeKind'.
| ParseUnexpectedCursorKind (Either CInt CXCursorKind)
| ParseUnexpectedLinkage (Either CInt CXLinkageKind)
-- | We encountered an unexpected type kind
--
-- This is always a bug in hs-bindgen: if this kind of type is unsupported,
-- we should explicitly check for it and throw an appropriate exception.
--
-- If this is a @Left@ value, it means that our @libclang@ bindings are
-- incomplete.
| ParseUnexpectedTypeKind (Either CInt CXTypeKind)
| ParseUnexpectedVisibility (Either CInt CXLinkageKind)
| ParseNoMainHeadersException String RealPath
deriving stock (Show, Generic)
-- | Floating-point types we do not support
--
-- @float@ and @double@ are the only floating-point types we support; all others
-- either have a platform dependent representation or lack a Haskell FFI
-- counterpart.
--
-- <https://github.com/well-typed/hs-bindgen/issues/349>
-- <https://github.com/well-typed/hs-bindgen/issues/2232>
data UnsupportedFloatType =
-- | @long double@
UnsupportedLongDouble
-- | @__float128@, @_Float128@
| UnsupportedFloat128
-- | @_Float16@
| UnsupportedFloat16
-- | @__fp16@ (half precision, storage only)
| UnsupportedHalf
-- | @__bf16@ (brain floating point)
| UnsupportedBFloat16
-- | @__ibm128@ (PowerPC double-double)
| UnsupportedIbm128
deriving stock (Show, Eq, Ord, Enum, Bounded, Generic)
instance PrettyForTrace UnsupportedFloatType where
prettyForTrace = \case
UnsupportedLongDouble -> "long double"
UnsupportedFloat128 -> "__float128"
UnsupportedFloat16 -> "_Float16"
UnsupportedHalf -> "__fp16"
UnsupportedBFloat16 -> "__bf16"
UnsupportedIbm128 -> "__ibm128"
instance Exception DelayedParseMsg where
displayException = PP.renderCtxDoc (PP.mkContext 100) . prettyForTrace
instance PrettyForTrace DelayedParseMsg where
prettyForTrace = \case
ParseUnderlyingTypeFailed name err -> PP.hcat [
"Parse failure of underlying type of typedef "
, prettyForTrace name
, ": "
, prettyForTrace err
]
ParseImplicitFieldFailed reason -> PP.hsep [
"Failed to parse an implicit struct or union field"
, "referencing an anonymous object. Reason: "
, prettyForTrace reason
]
ParseMacroEmpty name tokens -> PP.hsep [
"Not parsing macro with empty replacement list"
, prettyForTrace name >< ";"
, "use --parse-empty-macros to parse it:"
, PP.show tokens
]
ParseMacroErrorParse err -> PP.vcat [
"Could not parse macro:"
, PP.nest 2 $
PP.renderedLines $ \_maxWidth -> lines err.macroParseError
]
ParsePotentialDuplicateSymbol isPublic -> PP.hcat $ [
"Bindings may result in duplicate symbols; "
, "consider using 'static' or 'extern'"
] ++
if isPublic
then [
"; "
, "or if that is not an option, consider attributing hidden "
, "visibility to the symbol"
]
else []
ParseFunctionOfTypeTypedef ->
"Unsupported function declared with a typedef type"
ParseInvalidLinkage ->
"Invalid linkage (CXLinkage_Invalid)"
ParseInvalidVisibility ->
"Invalid visibility (CXVisibility_Invalid)"
ParseNestedDeclsFailed -> PP.hsep [
"Failed to parse a declaration because some of its nested declarations"
, "failed to parse"
]
ParseNonPublicVisibility -> PP.hsep [
"Bindings may result in linker errors"
, "because the symbol has non-public visibility"
]
ParseUnknownCursorAvailability simpleKind -> PP.hsep [
"Unknown declaration cursor availability:"
, PP.show simpleKind
]
ParseUnknownStorageClass storage -> PP.hsep [
"Unsupported storage class"
, PP.show storage
]
ParseUnexposedType ->
"Unexposed type"
ParseUnsupportedUnnamedInExtern ->
"Unexpected unnamed declaration in global variable"
ParseUnsupportedUnnamedInSignature ->
"Unexpected unnamed declaration in function signature"
ParseUnsupportedBuiltin name ->
"Unsupported built-in " >< PP.show name
ParseUnsupportedFloatType ty ->
"Unsupported floating-point type " >< prettyForTrace ty
ParseUnsupportedLinkage comment linkage -> PP.hcat [
"Unsupported linkage: "
, PP.show linkage
, " ("
, PP.string comment
, ")"
]
ParseUnsupportedTLS ->
"Unsupported thread-local variable"
ParseUnsupportedVariadicFunction ->
"Unsupported variadic (varargs) function"
ParseUnsupportedVector ->
"Unsupported SIMD vector type"
ParseUnusableUnnamedDecl unnamedId -> PP.hsep [
"Unusable unnamed declaration "
, prettyForTrace unnamedId
]
ParseDeclarationNotVisible kind name -> PP.hcat [
"Declaration of '"
, PP.text (C.tagKindPrefix kind)
, " "
, PP.text name
, "' will not be visible outside of this function"
]
ParseExpectedFunctionType ty -> PP.hsep [
"Expected function type, but got"
, PP.string ty
]
ParseMacroDefinitionNoMacroName ->
"A macro definition has no name, even though macros can not be unnamed"
ParseUnexpectedComplexType ty ->
unexpected $ "complex type " >< PP.show ty
ParseUnexpectedCursorKind x ->
unexpected $ "cursor kind " >< either PP.show PP.show x
ParseUnexpectedLinkage linkage -> PP.hcat [
"Unexpected linkage: "
, PP.show linkage
]
ParseUnexpectedTypeKind x ->
unexpected $ "type kind " >< either PP.show PP.show x
ParseUnexpectedVisibility visibility -> PP.hcat [
"Unexpected visibility: "
, PP.show visibility
]
ParseNoMainHeadersException why whr -> PP.hsep [
"Could not determine main headers:"
, PP.string why
, "at"
, PP.string $ getRealPath whr
]
where
unexpected :: PP.CtxDoc -> PP.CtxDoc
unexpected msg = PP.vcat [
"Unexpected " >< msg
, PP.string pleaseReport
]
-- | Unsupported features are warnings
instance IsTrace Level DelayedParseMsg where
getDefaultLogLevel = \case
ParseUnderlyingTypeFailed _ x -> getDefaultLogLevel x
ParseImplicitFieldFailed x -> getDefaultLogLevel x
ParseMacroEmpty{} -> Info
ParseMacroErrorParse{} -> Info
ParsePotentialDuplicateSymbol{} -> Notice
ParseDeclarationNotVisible{} -> Warning
ParseFunctionOfTypeTypedef{} -> Warning
ParseInvalidLinkage -> Warning
ParseInvalidVisibility -> Warning
ParseNestedDeclsFailed{} -> Warning
ParseNonPublicVisibility{} -> Warning
ParseUnknownCursorAvailability{} -> Warning
ParseUnknownStorageClass{} -> Warning
ParseUnexposedType -> Warning
ParseUnsupportedUnnamedInExtern{} -> Warning
ParseUnsupportedUnnamedInSignature{} -> Warning
ParseUnsupportedBuiltin{} -> Warning
ParseUnsupportedFloatType{} -> Warning
ParseUnsupportedLinkage{} -> Warning
ParseUnsupportedTLS{} -> Warning
ParseUnsupportedVariadicFunction -> Warning
ParseUnsupportedVector -> Warning
ParseUnusableUnnamedDecl{} -> Warning
ParseExpectedFunctionType{} -> Bug
ParseMacroDefinitionNoMacroName{} -> Bug
ParseUnexpectedComplexType{} -> Bug
ParseUnexpectedCursorKind{} -> Bug
ParseUnexpectedLinkage{} -> Bug
ParseUnexpectedTypeKind{} -> Bug
ParseUnexpectedVisibility{} -> Bug
ParseNoMainHeadersException{} -> Error
getSource = const HsBindgen
getTraceId = \case
ParseImplicitFieldFailed x -> "parse-" <> getTraceId x
ParseMacroDefinitionNoMacroName{} -> "parse-macro"
ParseMacroEmpty{} -> "parse-macro"
ParseMacroErrorParse{} -> "parse-macro"
_ -> "parse"
{-------------------------------------------------------------------------------
Delayed parse messages: implicit fields
-------------------------------------------------------------------------------}
data ParseImplicitFieldsMsg =
-- | Unsupported empty anonymous nested struct or union
--
-- Anonymous nested structs and unions need to have at least one named
-- member for parsing of implicit fields to succeed.
UnsupportedEmptyAnon
-- | An unexpected exception was thrown when using @clang_Type_getOffsetOf@
--
-- This is likely a bug in implicit field parsing.
| UnexpectedClangOffsetOfException Text String
deriving stock (Show, Eq, Ord, Generic)
instance PrettyForTrace ParseImplicitFieldsMsg where
prettyForTrace = \case
UnsupportedEmptyAnon -> PP.hsep [
"Unsupported empty nested anonymous union or struct:"
, "it should have at least one named field"
]
UnexpectedClangOffsetOfException field exc -> PP.hsep [
"Unexpected exception when using clang_Type_getOffsetOf"
, "with field name", PP.text field, ":", PP.string exc
]
-- | Unsupported features are warnings
instance IsTrace Level ParseImplicitFieldsMsg where
getDefaultLogLevel = \case
UnsupportedEmptyAnon{} -> Warning
UnexpectedClangOffsetOfException{} -> Bug
getSource = const HsBindgen
getTraceId = const "implicit-fields"