packages feed

hs-bindgen-1.0.0.0: src-internal/HsBindgen/Frontend/Pass/Select/IsPass.hs

module HsBindgen.Frontend.Pass.Select.IsPass (
    Select
    -- * Configuration
  , ProgramSlicing(..)
  , SelectConfig(..)
    -- * Trace messages
  , SelectReason(..)
  , SelectStatus(..)
  , TransitiveDependencyMissing(..)
  , SelectMsg(..)
  ) where

import Data.Default (Default (def))
import Text.SimplePrettyPrint (CtxDoc, (<+>), (><))
import Text.SimplePrettyPrint qualified as PP

import Clang.HighLevel.Types
import Clang.Paths

import HsBindgen.BindingSpec qualified as BindingSpec
import HsBindgen.Frontend.Analysis.DeclIndex (Squashed (..), UnusableEntry,
                                              UnusableReason (..))
import HsBindgen.Frontend.Analysis.DeclIndex qualified as DeclIndex
import HsBindgen.Frontend.Pass.AdjustTypes.IsPass
import HsBindgen.Frontend.Pass.MangleNames.IsPass
import HsBindgen.Frontend.Pass.Parse.Msg
import HsBindgen.Frontend.Pass.PrepareReparse.IsPass.Msg (DelayedPrepareReparseMsg)
import HsBindgen.Frontend.Pass.ReparseMacroExpansions.IsPass.Msg (DelayedReparseMacroExpansionsMsg)
import HsBindgen.Frontend.Pass.ResolveBindingSpecs.IsPass
import HsBindgen.Frontend.Pass.TranslateTypes.IsPass
import HsBindgen.Frontend.Pass.TranslateTypes.IsPass.Msg (DelayedTranslateTypesMsg)
import HsBindgen.Frontend.Pass.TypecheckMacros.IsPass
import HsBindgen.Frontend.Predicate
import HsBindgen.IR.C qualified as C
import HsBindgen.IR.Pass
import HsBindgen.IR.Pass.Types (CoercePassAnonRef (coercePassAnonRef))
import HsBindgen.IR.Translation
import HsBindgen.Util.Tracer

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

type Select :: Pass
data Select a

type family AnnSelect ix where
  AnnSelect "Decl"                 = PrescriptiveDeclSpec
  AnnSelect "Enum"                 = NewtypeNames
  AnnSelect "Flam"                 = FlamNames
  AnnSelect "IndirectField"        = IndirectFieldNames Select
  AnnSelect "Struct"               = StructNames
  AnnSelect "Typedef"              = TypedefNames
  AnnSelect "TypecheckedMacroType" = NewtypeNames
  AnnSelect "TypeFunArg"           = AdjustedFrom Select
  AnnSelect "Union"                = NewtypeNames
  AnnSelect _                      = NoAnn

instance IsPass Select

instance PassId Select where
  type Id Select = DeclIdPair

  idNameKind     _ namePair = namePair.cName.name.kind
  idSourceName   _ namePair = C.declIdSourceName namePair.cName
  idLocationInfo _ namePair = C.declIdLocationInfo namePair.cName

instance PassScopedName Select where
  type ScopedName Select = ScopedNamePair

instance PassTypes Select where
  type Types Select   = TranslatedTypes Select
  type AnonRef Select = TranslatedAnonRef Select

  cType _ translatedTypes = translatedTypes.c
  anonRefTypes _ = translatedAnonRefType

instance PassMacro Select where
  type MacroId Select = Id Select
  type MacroBody       Select = TypecheckedMacro Select
  type MacroUnderlying Select = C.Type Select

  macroIdId _ = id

instance PassExtBinding Select where
  type ExtBinding Select = BindingSpec.ResolvedExtBinding

  extBindingId _ extBinding = extDeclIdPair extBinding

instance PassCommentDecl Select where
  type CommentDecl Select = Maybe (C.Comment Select)

instance PassAnn Select where
  type Ann ix Select = AnnSelect ix

instance PassMsg Select where
  type Msg Select = C.WithLocationInfo SelectMsg

{-------------------------------------------------------------------------------
  Configuration
-------------------------------------------------------------------------------}

-- | Select transitive dependencies?
data ProgramSlicing =
    -- | Select declarations using the selection predicate /and/ their transitive
    -- dependencies.
    EnableProgramSlicing
  | DisableProgramSlicing
  deriving stock (Show, Eq)

instance Default ProgramSlicing where
  def :: ProgramSlicing
  def = DisableProgramSlicing

data SelectConfig = SelectConfig {
      programSlicing     :: ProgramSlicing
    , selectionPredicate :: Boolean SelectionPredicate
    }
  deriving stock (Show, Eq)

{-------------------------------------------------------------------------------
  Trace messages
-------------------------------------------------------------------------------}

data SelectReason =
    -- | The user actively selects the declarations.
    SelectionRoot
    -- | The user has activated program slicing, and the declaration is a
    -- transitive dependency of a selection root.
  | TransitiveDependency
  deriving stock (Show, Eq)

instance PrettyForTrace SelectReason where
  prettyForTrace = \case
    SelectionRoot        -> "direct selection predicate match"
    TransitiveDependency -> "transitive dependency"

data SelectStatus =
    NotSelected
  | Selected SelectReason
  deriving stock (Show)

data TransitiveDependencyMissing =
    -- | Transitive dependency is 'UnusableEntry'.
    TransitiveDependencyUnusable C.DeclId UnusableEntry
    -- | Transitive dependency is not selected.
  | TransitiveDependencyNotSelected C.DeclId [SingleLoc C.DeclPath]
  deriving stock (Show)

instance PrettyForTrace TransitiveDependencyMissing where
  prettyForTrace = \case
      TransitiveDependencyUnusable i u ->
        let intro = "Transitive dependency unusable:"
        in  PP.hang intro 2 $ prettyForTrace $ C.WithLocationInfo{
                loc = C.declIdLocationInfo i $
                  C.declLocsToList $ DeclIndex.unusableToLoc u
              , msg = u
              }
      TransitiveDependencyNotSelected i ls ->
        let intro = "Transitive dependency not selected:"
        in  PP.hang intro 2 $ PP.vcat [
                prettyForTrace $ C.declIdLocationInfo i ls
              , "Adjust the selection predicate or enable program slicing"
              ]

-- | Select trace messages
data SelectMsg =
    -- | Information about selection status; issued for all available
    -- declarations.
    SelectStatusInfo SelectStatus
    -- | The user has selected a declaration that is available but has missing
    -- transitive dependencies.
  | TransitiveDependenciesMissing SelectReason [TransitiveDependencyMissing]
    -- | The user has selected a deprecated declaration. Maybe they want to
    -- de-select the deprecated declaration?
  | SelectDeprecated SelectReason
    -- | Delayed parse message.
  | SelectDelayedParseMsg DelayedParseMsg
    -- | A directly or transitively selected declaration is unusable.
  | SelectUnusable UnusableReason
  | SelectConflict
  | SelectMangleNamesSquashed Squashed
    -- | Delayed @PrepareReparse@ message
  | SelectDelayedPrepareReparseMsg DelayedPrepareReparseMsg
    -- | Delayed @ReparseMacroExpansions@ message
  | SelectDelayedReparseMacroExpansionsMsg DelayedReparseMacroExpansionsMsg
    -- | Delayed @TranslateTypes@ message
  | SelectDelayedTranslateTypesMsg DelayedTranslateTypesMsg
    -- | Inform the user that no declarations matched the selection predicate.
  | SelectNoDeclarationsMatched
    -- | Summary of the number of selected macros that hs-bindgen failed to
    -- translate.
  | SelectMacrosDropped Int
    -- | The source of a trace message is not part of the include graph, so we
    -- do not know where to sort it.
  | SelectSourceNotInIncludeGraph RealPath
  deriving stock (Show)

instance PrettyForTrace SelectMsg where
  prettyForTrace = \case
      SelectStatusInfo NotSelected ->
        "Not selected"
      SelectStatusInfo (Selected r) ->
        "Selected (" >< prettyForTrace r >< ")"
      TransitiveDependenciesMissing s xs ->
        couldNotSelectWithReason s $
          PP.vcat $ map prettyForTrace xs
      SelectDeprecated r ->
        withSelectReason r "Selected a deprecated declaration"
      SelectDelayedParseMsg x ->
        during x $ prettyForTrace x
      SelectUnusable reason -> case reason of
        UnusableUnavailable ->
          couldNotSelect $ PP.hang "Parse not attempted: " 2
            "Declaration is 'unavailable' on this platform"
        UnusableOmitted ->
          couldNotSelect $
            "Declaration omitted by prescriptive binding specifications"
        UnusableParseFailure x ->
          couldNotSelect $ prettyForTrace x
        UnusableMangleNamesFailure x ->
          couldNotSelect $ prettyForTrace x
        UnusableMacroTypecheckFailure x ->
          couldNotSelect $ prettyForTrace x
        UnusableMacroResolutionFailure x ->
          couldNotSelect $ prettyForTrace x
      SelectConflict ->
        couldNotSelect "Conflicting declarations"
      SelectMangleNamesSquashed x -> PP.hsep [
          "Squashed typedef to"
        , prettyForTrace x.targetNameC
        ]
      SelectDelayedPrepareReparseMsg x ->
        during x $ prettyForTrace x
      SelectDelayedReparseMacroExpansionsMsg x ->
        during x $ prettyForTrace x
      SelectDelayedTranslateTypesMsg x ->
        during x $ prettyForTrace x
      SelectNoDeclarationsMatched ->
        "No declarations matched the selection predicate"
      SelectMacrosDropped n ->
        PP.string $
          show n
            <> (if n == 1 then " macro failed to translate; "
                          else " macros failed to translate; ")
            <> "use --log-enable-macro-warnings for details"
      SelectSourceNotInIncludeGraph path -> PP.hsep [
          "Source not in include graph:"
        , PP.string $ getRealPath path
        ]
    where
      during :: IsTrace l e => e -> CtxDoc -> CtxDoc
      during x = PP.hang (PP.string ("During " <> (getTraceId x).id <> ":")) 2

      couldNotSelectStr :: CtxDoc
      couldNotSelectStr = "Could not select declaration"

      couldNotSelect :: CtxDoc -> CtxDoc
      couldNotSelect x = PP.hang (couldNotSelectStr >< ":") 2 x

      withSelectReason :: SelectReason -> CtxDoc -> CtxDoc
      withSelectReason r x = x <+> "(" >< prettyForTrace r >< ")"

      couldNotSelectWithReason :: SelectReason -> CtxDoc -> CtxDoc
      couldNotSelectWithReason r x =
        let intro = withSelectReason r couldNotSelectStr >< ":"
        in  PP.hang intro 2 x

instance IsTrace Level SelectMsg where
  getDefaultLogLevel = \case
    SelectStatusInfo{}                       -> Info
    TransitiveDependenciesMissing{}          -> Warning
    SelectDeprecated{}                       -> Notice
    SelectDelayedParseMsg x                  -> getDefaultLogLevel x
    SelectUnusable r -> case r of
      UnusableUnavailable              -> Warning
      UnusableOmitted                  -> Info
      UnusableParseFailure x           -> getDefaultLogLevel x
      UnusableMangleNamesFailure x     -> getDefaultLogLevel x
      UnusableMacroTypecheckFailure x  -> getDefaultLogLevel x
      UnusableMacroResolutionFailure x -> getDefaultLogLevel x
    SelectConflict{}                         -> Warning
    SelectMangleNamesSquashed{}              -> Notice
    SelectDelayedPrepareReparseMsg x         -> getDefaultLogLevel x
    SelectDelayedReparseMacroExpansionsMsg x -> getDefaultLogLevel x
    SelectDelayedTranslateTypesMsg x         -> getDefaultLogLevel x
    SelectNoDeclarationsMatched              -> Warning
    SelectMacrosDropped{}                    -> Notice
    SelectSourceNotInIncludeGraph{}          -> Bug
  getSource  = const HsBindgen
  getTraceId = \case
    SelectStatusInfo{}                       -> "select"
    TransitiveDependenciesMissing{}          -> "select"
    SelectDeprecated{}                       -> "select"
    SelectDelayedParseMsg x                  -> "select-" <> getTraceId x
    SelectUnusable r -> case r of
      UnusableUnavailable              -> "select-parse"
      UnusableOmitted                  -> "select-omitted"
      UnusableParseFailure           x -> "select-" <> getTraceId x
      UnusableMangleNamesFailure     x -> "select-" <> getTraceId x
      UnusableMacroTypecheckFailure  x -> "select-" <> getTraceId x
      UnusableMacroResolutionFailure x -> "select-" <> getTraceId x
    SelectConflict{}                         -> "select"
    SelectMangleNamesSquashed{}              -> "select-mangle-names-squashed"
    SelectDelayedPrepareReparseMsg x         -> "select-" <> getTraceId x
    SelectDelayedReparseMacroExpansionsMsg x -> "select-" <> getTraceId x
    SelectDelayedTranslateTypesMsg x         -> "select-" <> getTraceId x
    SelectNoDeclarationsMatched              -> "select"
    SelectMacrosDropped{}                    -> "select-dropped-macros"
    SelectSourceNotInIncludeGraph{}          -> "select-source-not-in-include-graph"

{-------------------------------------------------------------------------------
  CoercePass
-------------------------------------------------------------------------------}

instance CoercePassAnonRef TranslateTypes Select where
  coercePassAnonRef _ = coercePass

instance CoercePassId      TranslateTypes Select

instance CoercePassTypes TranslateTypes Select where
  coercePassTypes _ = coercePass

instance CoercePassMacroId TranslateTypes Select

instance CoercePassMacroUnderlying TranslateTypes Select where
  coercePassMacroUnderlying _ = coercePass

instance CoercePassMacroBody TranslateTypes Select where
  coercePassMacroBody _ = coercePassParam

instance CoercePassAnn "IndirectField" TranslateTypes Select where
  coercePassAnn _ = \case
      IndirectFieldNames name -> IndirectFieldNames name

instance CoercePassAnn "TypeFunArg" TranslateTypes Select where
  coercePassAnn _ = \case
      AdjustedFromArray ty    -> AdjustedFromArray (coercePass ty)
      AdjustedFromFunction ty -> AdjustedFromFunction (coercePass ty)
      NotAdjusted             -> NotAdjusted

instance CoercePassCommentDecl TranslateTypes Select where
  coercePassCommentDecl _ = fmap coercePass