packages feed

glean-0.2.0.0: glean/schema/source/cxx.angle

# Copyright (c) Meta Platforms, Inc. and affiliates.

schema cxx1.5 {
import buck.4
import builtin.1
import pp1
import src
import fbthrift

# A name (identifier)
predicate Name : string

# Types are their textual representations for now
predicate Type : string

# Named parameter
type Parameter =
  {
    name : Name,
    type : Type,
  }

# Type signature of function or method (without name or scope)
predicate Signature :
  {
    returns : Type,
    parameters : [Parameter],
  }

# -----------------------------------------------------------------------------
# Scopes and qualified names

# Fully qualified C++ namespace name (or anonymous)
predicate NamespaceQName :
  {
    name : maybe Name,
    parent : maybe NamespaceQName,
  }

# Access scope for C++ declaration
type Access = enum { Public | Protected | Private }

# Scope (still somewhat incomplete)
type Scope =
  {
    global_ : builtin.Unit |
    namespace_ : NamespaceQName |
    recordWithAccess :
      {
        record : QName,
        access : Access
      } |
    local : FunctionQName
  }

# Fully qualified name
predicate QName :
  {
    name : Name,
    scope : Scope,
  }

# C++ operator name
type Operator = string

# C++ literal operator name
type LiteralOperator = string

# Describe a function or method name (without parameters or scope)
predicate FunctionName :
  {
    name : Name |                         # ordinary name
    operator_ : Operator |                # C++ operator
    literalOperator : LiteralOperator |   # C++ literator operator (\"\"_x)
    constructor : builtin.Unit |
    destructor : builtin.Unit |
    conversionOperator : Type
  }

# Fully qualified function name
predicate FunctionQName :
  {
    name : FunctionName,
    scope : Scope,
  }

# -----------------------------------------------------------------------------
# Declarations

# A namespace declaration
predicate NamespaceDeclaration :
  {
    name : NamespaceQName,
    source : src.Range,
  }

# C++ record kind - struct/class/union
type RecordKind =
   {
     struct_ : builtin.Unit |
     class_ : builtin.Unit |
     union_ : builtin.Unit
   }

# Record declaration
predicate RecordDeclaration :
  {
    name : QName,
    kind : RecordKind,
    source : src.Range,
  }

# Enum declaration
predicate EnumDeclaration :
  {
    name : QName,
    isScoped : bool,
    type : maybe Type,
    source : src.Range,
  }

# C++ method ref qualifier
type RefQualifier = enum { None_ | LValue | RValue }

# Method signature
type MethodSignature =
  {
    isVirtual : bool,
    isConst : bool,
    isVolatile : bool,
    refQualifier : RefQualifier,
  }

# Function declaration
predicate FunctionDeclaration :
  {
    name : FunctionQName,
    signature : Signature,
    method : maybe MethodSignature,
    source : src.Range,
  }

# C++ method override
predicate MethodOverrides :
  {
    derived : FunctionDeclaration,
    base : FunctionDeclaration,
  }

# Objective C category identifier
type ObjcCategoryId =
  {
    className : Name,
    categoryName : Name,
  }

# Objective C container identifier
type ObjcContainerId =
  {
    protocol : Name |
    interface_ : Name |
    categoryInterface : ObjcCategoryId |
    extensionInterface : Name |
    implementation : Name |
    categoryImplementation : ObjcCategoryId |
  }

# Objective C container (protocol, interface etc.) declaration
predicate ObjcContainerDeclaration :
  {
    id : ObjcContainerId,
    source : src.Range,
  }

# Objective C selector
#
# T166420010: This is not fully correct. Specifically, ObjC selectors
# `method` and `method:` are different, but isn't distinguished here.
predicate ObjcSelector : [string]

# Objective C method declaration
predicate ObjcMethodDeclaration :
  {
    selector : ObjcSelector,
    # Locations of the slots of the selector.
    # These correspond to the `selector` entries.
    locations : [src.FileLocation],
    container : ObjcContainerId,
    signature : Signature,
    isInstance : bool,
    isOptional : bool,
    isAccessor : bool,
    source : src.Range,
  }

predicate ObjcContainerMethods :
  {
    container : ObjcContainerId,
    method: ObjcMethodDeclaration,
  } stored { C, M } where
    M = ObjcMethodDeclaration { container = C }

# The full name of an ObjC method declaration.
#
# If the method has no parameters, the name is the method name. e.g., `foo`
# If the method has 1 parameter, the name ends with a colon. e.g., `foo:`
# If the method has multiple parameters, the parameter names are
# concatenated with a colon. e.g., `foo:bar:`
#
# Parameters may have no name, in which case they are omitted. e.g., `foo::`
#
# NOTE: Even with `ObjcSelector` not being sufficient in fully capturing
# the name, we can deduce this name for a method since we have the `signature`.
# That is, `parameters = []` is `method`, and `parameters = [_]` is `method:`.
# However, Angle currently isn't powerful enough for us to perform string join
# operations. As such, we produce these in the indexer and store them for now.
predicate ObjcMethodDeclarationName :
  {
    decl : ObjcMethodDeclaration,
    name : Name,
  }

# Objective C property declaration
predicate ObjcPropertyDeclaration :
  {
    name : Name,
    container : ObjcContainerId,
    type : Type,
    isInstance : bool,
    isOptional : bool,
    isReadOnly : bool,
    isAtomic : bool,
    source : src.Range,
  }

predicate ObjcContainerProperties :
  {
    container : ObjcContainerId,
    property : ObjcPropertyDeclaration,
  } stored { C, P } where
    P = ObjcPropertyDeclaration { container = C }

# Objective C property implementation kind
type ObjcPropertyKind = enum { Synthesize | Dynamic }

# Objective C property implementation
predicate ObjcPropertyImplementation :
  {
    declaration : ObjcPropertyDeclaration,
    kind : ObjcPropertyKind,
    ivar : maybe Name,
    source : src.Range,
  }

# C++ record base
type RecordBase =
  {
    base : RecordDeclaration,
    access : Access,
    isVirtual : bool,
  }

# Enumerator declaration (constant in an enum)
predicate Enumerator :
  {
    name : Name,
    enumeration : EnumDeclaration,   # TODO: qname?
    source : src.Range,
  }

# Enum definition
predicate EnumDefinition :
  {
    declaration : EnumDeclaration,
    enumerators : [Enumerator]
  }

# C++ function definition
# Will also have a body eventually
predicate FunctionDefinition :
  {
    declaration : FunctionDeclaration,
    isInline : bool,
  }

# Unified Symbol Resolution (USR) hash for function
type USR = string
predicate USRToDeclaration:
  {
    hash : USR,
    declaration : Declaration,
  }


# For quicker lookup of Entity -> USR
predicate DeclarationToUSR :
  {
    declaration : Declaration,
    usr : USR
  }
  stored { D, U } where USRToDeclaration { U, D }


# Hash of the function's mangled name
type MangledNameHash = string
predicate MangledNameHashToDeclaration:
  {
    hash : MangledNameHash,
    declaration : Declaration,
  }

# Objective C interface-implementation relationship
predicate ObjcImplements :
  {
    implementation : ObjcContainerDeclaration,
    interface_ : ObjcContainerDeclaration,
  }

# Reverse of ObjcImplements; efficiently look up implementations
predicate ObjcInterfaceToImplementation :
  {
    interface_ : ObjcContainerDeclaration,
    implementation : ObjcContainerDeclaration,
  }
  stored { If, Im } where ObjcImplements { Im, If }

# Objective C method definition
# Will also have a body eventually
predicate ObjcMethodDefinition : ObjcMethodDeclaration

predicate Declarations : [Declaration]

# Two declarations refer to the same entity
predicate Same :
  {
    declaration1 : Declaration,
    declaration2 : Declaration,
  }

# Definitions

# Part of a namespace definition
predicate NamespaceDefinition :
  {
    declaration : NamespaceDeclaration,
    members : Declarations,
  }

predicate RecordDefinition :
  {
    declaration : RecordDeclaration,
    bases : [RecordBase],
    members : Declarations,
  }

# Objective C interface definition
predicate ObjcContainerDefinition :
  {
    declaration : ObjcContainerDeclaration,
    protocols : [ObjcContainerDeclaration],
    members : Declarations,
  }

# C++ preprocessor include trace
type IncludeTrace =
  {
    include_ : pp1.Include,
    trace : maybe Trace,  # deprecated; always nothing. See IncludeTree.
  }

# A C++ preprocessor event
type PPEvent =
  {
    include_ : IncludeTrace |
    define : pp1.Define |
    undef : pp1.Undef |
    use : pp1.Use |
  }

# A trace of preprocessor events in a C++ file
predicate PPTrace :
  {
    file : src.File,
    events : [PPEvent]
  }

# A trace of all interesting things in a C++ file
predicate Trace :
  {
    file : src.File,
    declarations : Declarations,
    preprocessor : PPTrace,
  }

type MaybeIncludeTree =
  {
    tree : maybe IncludeTree
  }

predicate IncludeTree :
  {
    trace : Trace,
    # The elements in this list map 1:1 to the `IncludeTrace` in `PPTrace`.
    children : [MaybeIncludeTree],
  }

predicate IncludeTreeParent :
  {
    tree : IncludeTree,
    parent : IncludeTree
  }
  stored { Child , Parent } where
    Parent = IncludeTree { _, Children };
    { tree = { just = Child }} = Children[..]

# C++ type alias kind
type TypeAliasKind = enum { Typedef | Using }

# Type alias declaration (typedef or using)
predicate TypeAliasDeclaration :
  {
    name : QName,
    type : Type,
    kind : TypeAliasKind,
    source : src.Range,
  }

type NamespaceTarget =
  {
    namespace_ : NamespaceDeclaration |
    namespaceAlias : NamespaceAliasDeclaration |
  }

predicate NamespaceAliasDeclaration :
  {
    name : NamespaceQName,
    target : NamespaceTarget,
    source : src.Range,
  }

# Using declaration
predicate UsingDeclaration :
  {
    name : FunctionQName,
    source : src.Range,
  }

# Using directive
predicate UsingDirective :
  {
    name : QName,
    source : src.Range,
  }

# Kind of global variable
type GlobalVariableKind =
  enum {
    SimpleVariable |
    StaticVariable |
    StaticMember
  }

# Kind of local variable
type LocalVariableKind =
  enum {
    SimpleVariable |
    StaticVariable |
    Parameter
  }

# Global variable attributes
type GlobalVariableAttribute =
  enum {
    Plain |
    Inline |
    Constexpr
  }

# Local variable attributes
type LocalVariableAttribute =
  enum {
    Plain |
    Constexpr
  }

# Global variable description
type GlobalVariable =
  {
    kind : GlobalVariableKind,
    attribute : GlobalVariableAttribute,
    definition : bool,  # is this a declaration or a definition
  }

# Local variable description
type LocalVariable =
  {
    kind : LocalVariableKind,
    attribute : LocalVariableAttribute,
  }

# Attributes of non-static member variables
type Field =
  {
    mutable_ : bool,
    bitsize : maybe nat,
  }

# Attributes of Objective C ivars
type ObjcIVar =
  {
    synthesize : bool,
    bitsize : maybe nat,
  }

# Kinds of variables
type VariableKind =
  {
    global_ : GlobalVariable |
    local : LocalVariable |
    field : Field |
    ivar : ObjcIVar |
  }

# Variable declaration
predicate VariableDeclaration :
  {
    name : QName,
    type : Type,
    kind : VariableKind,
    source : src.Range,
  }

# Sum type of all declarations
type Declaration =
  {
    namespace_ : NamespaceDeclaration |
    usingDeclaration : UsingDeclaration |
    usingDirective : UsingDirective |
    record_ : RecordDeclaration |
    enum_ : EnumDeclaration |
    function_ : FunctionDeclaration |
    variable : VariableDeclaration |
    objcContainer : ObjcContainerDeclaration |
    objcMethod : ObjcMethodDeclaration |
    objcProperty : ObjcPropertyDeclaration |
    typeAlias : TypeAliasDeclaration |
    namespaceAlias : NamespaceAliasDeclaration |
  }

# A specific slot of an ObjC selector. The `index` field is the position
# within the `selector` and `locations` field of `ObjCMethodDeclaration`.
#
# This construction of declaration accompanied by an index enables
# representing the relationship of (decl <-> defn) + index.
type ObjcSelectorSlot =
  {
    objcMethod : ObjcMethodDeclaration,
    index : nat,
  }

# What indirect xrefs can go through
type XRefVia =
  {
    usingDeclaration : UsingDeclaration |
    usingDirective : UsingDirective |
    macro : pp1.Use |
  }

# C++ cross-reference target
type XRefTarget =
  {
    declaration : Declaration |
    enumerator : Enumerator |
    objcSelector : ObjcSelector |
    objcSelectorSlot : ObjcSelectorSlot |
    unknown : src.Loc |
    indirect : XRefIndirectTarget |
  }

# Cross-reference that goes through something (macro or using)
predicate XRefIndirectTarget :
  {
    via : XRefVia,
    target : XRefTarget,
  }

predicate XRefTargets : [XRefTarget]

# Uses of XRefs
#
# There are 3 different sources of uses: file, expansion, and spelling.
# Here's an example for an overview:
#
#   void foo() {}
#
#   #define FOO foo
#               ^^^ spelling (1)
#
#   #define REF(x) x
#
#   void f() {
#     foo();
#     ^^^ file (2)
#
#     FOO();
#     ^^^ expansion (3)
#
#     REF(foo)();
#         ^^^  spelling (4)
#     ^^^^^^^^ expansion (5)
#   }
#
# The "file" uses are non-macro uses such as (2). "expansion" uses are xrefs
# formed within macro expansions such as (3) and (5). The "spelling" uses are
# the actual spelled out source location of the "expansion" uses. There are two
# cases of this, one where the spelling is inside the macro body such as (1),
# and where the spelling is the macro argument such as (4).
#
# An extra wrinkle here is that this predicate captures uses from a single file.
# Refer to the `SpellingXRef` predicate for further details.
type From =
  {
    spans : src.PackedByteSpans,
    expansions : src.PackedByteSpans,
    spellings : src.PackedByteSpans,
  }

# Cross-references to a known target
type FixedXRef =
  {
    target : XRefTarget,
    from : From,
  }

# Note that "froms" corresponds to many "targets" in FileXRefs
# Cross-references in a file
predicate FileXRefMap :
  {
    file : src.File,
    fixed : [FixedXRef],
    froms : [From],
  }

# Note that "targets" entries correspond to "froms" entries (in the "xmap").
# Instantiation of a FileXRefMap with set of external references
predicate FileXRefs :
  {
    xmap : FileXRefMap,
    targets : [XRefTargets],
  }

# Captures the spelling xrefs discovered from different files.
#
# In general, we discover xrefs within a file while we're indexing that file.
# Macros are special in that we can discover xrefs for a file from another.
#
# Consider the following silly example:
#
#   // foo.h
#   void foo(int) {}
#   #define FOO foo(42)
#               ^^^ spelling (1)
#
#   // a.cpp
#   #include "foo.h"
#   void f() { FOO; }
#              ^^^ expansion (2)
#
# The AST of `foo.h` does not contain any xrefs, since macro definitions do not
# carry any semantics. We discover the spelling xref from (1) at the use at (2)
# during the indexing of `a.cpp`.
#
# This predicate is used for this type of "external spelling xref" cases.
#
# NOTE: We don't group these by file because if we did, we can easily run into
#       a combinatorial explosion of these facts. For example, consider if
#       the `foo.h` above contained N macro definitions. Different files can
#       include `foo.h` and each use an arbitrary subset of the macros. We can
#       then end up with 2^N of these facts. Indexing per file location should
#       help to deduplicate the spelling xrefs to absolute targets.
predicate SpellingXRef :
  {
    source : src.FileLocation,
    target : XRefTarget,
  }

# Uses

# Note that ("target", "file") makes a unique key for these facts
# All uses of a declaration in a file
predicate TargetUses :
  {
    target : XRefTarget,
    file : src.File,
    from : From,
  }

# These are all disjoint equivalence classes.  Constructors within an array
# guaranteed identical. Array length is at least 2.
# All the declarations for a given entity.
predicate DeclFamily : [Declaration]

# These "decl" is a unique key here.  "decl" are only present iff they are
# in a cxx1.Same, otherwise they are implicitly in a singleton family.
# Map a declaration to its family.
predicate DeclToFamily :
  {
    decl : Declaration,
    family : DeclFamily,
  }

# Location of the name of a declaration
#
# The corresponding declaration may be declared within normal code, or it may
# be within a macro expansion. If it's a normal declaration, the name span
# refers to the name portion of the declaration. Otherwise, the name span
# refers to the spelling location of the name.
#
# Example of declaration in normal code
#
#   void foo() {}
#        ^^^ name span
#   ^^^^^^^^^^^^^ decl range
#
# Example of declaration in macro expansion
#
#   #define FOO void foo() {}
#                    ^^^ name span
#
#   FOO
#   ^^^ decl range
#
# The name span is commonly the destination for Code Navigation.
predicate DeclarationNameSpan :
  {
    decl : Declaration,
    file : src.File,
    span : src.ByteSpan
  }

# Generalization of Function call facts, which handles references between
# any kinds of declarations.

# Declarations referenced from a given declaration. This can be used
# for constructing call graphs, for example.
#
# Notes:
#   - source is limited to RecordDeclaration, FunctionDeclaration,
#     ObjcContainerDeclaration, ObjcPropertyDeclaration, ObjcMethodDeclaration
#
#   - Each reference in the source is attributed to exactly one
#     declaration. So for example, a reference inside a method is
#     attributed to the method, but not the enclosing class declaration.
#
#   - The order of the declarations in the list is non-deterministic,
#     but there are no duplicates (it should be a set, really)
#
predicate DeclarationTargets :
  {
    source : Declaration,
    targets : [Declaration],
  }

# Declarations referring to a given declaration. The inverse of DeclarationTargets.
predicate DeclarationSources :
  {
    target : Declaration,
    sources : [Declaration],
  }

# Comment referring to a given declaration
predicate DeclarationComment :
  {
    declaration : Declaration,
    file : src.File,
    span : src.ByteSpan,
  }

# A trace of a translation unit
predicate TranslationUnitTrace :
  {
    tunit : buck.TranslationUnit,
    trace : Trace,
  }

# An include tree of a translation unit
predicate TranslationUnitIncludeTree :
  {
    tunit : buck.TranslationUnit,
    tree : IncludeTree,
  }

predicate IncludeTreeTranslationUnit :
  {
    tree : IncludeTree,
    tunit : buck.TranslationUnit,
  }
  stored { T, U } where TranslationUnitIncludeTree { U, T }

# The fileXRefs corresponding to a particular translation unit
predicate TranslationUnitXRefs :  # 2 is already used in cxx1
  {
    tunit : buck.TranslationUnit,
    xrefs : [FileXRefs]
  }

# The backing ivar of a property
predicate ObjcPropertyIVar :
  {
    property : ObjcPropertyDeclaration,
    ivar : VariableDeclaration,
  }

# Derived Predicates

# Lots of entities have a Scope-indexed parent
# This just abstracts out the lookup of the Scope component
predicate DeclarationScope :
  {
    decl : cxx1.Declaration,
    scope : cxx1.Scope
  }
  { Decl, Scope } where
    # these share FunctionQName scopes
    ( FQN = Decl.function_?.name | Decl.usingDeclaration?.name;
      Scope = FQN.scope
    # vanilla QName scopes
    ) | (
      QN =
         Decl.record_?.name |
         Decl.variable?.name |
         Decl.typeAlias?.name |
         Decl.enum_?.name |
         Decl.usingDirective?.name;
      Scope = QN.scope
    # NamespaceQName scopes
    ) | (
      NQN = Decl.namespace_?.name | Decl.namespaceAlias?.name;
      Scope.namespace_? = NQN
    )

# src.Range of an arbitrary Declaration.
predicate DeclarationSrcRange :
  {
    decl : Declaration,
    source : src.Range,
  }
  { D, R } where
     ( { namespace_ = X } = D;
       X = NamespaceDeclaration { source = R }) |
     ( { usingDeclaration = X } = D;
       X = UsingDeclaration { source = R }) |
     ( { usingDirective = X } = D;
       X = UsingDirective { source = R }) |
     ( { record_ = X } = D;
       X = RecordDeclaration { source = R }) |
     ( { enum_ = X } = D;
       X = EnumDeclaration { source = R }) |
     ( { function_ = X } = D;
       X = FunctionDeclaration { source = R }) |
     ( { variable = X } = D;
       X = VariableDeclaration { source = R }) |
     ( { objcContainer = X } = D;
       X = ObjcContainerDeclaration { source = R }) |
     ( { objcMethod = X } = D;
       X = ObjcMethodDeclaration { source = R }) |
     ( { objcProperty = X } = D;
       X = ObjcPropertyDeclaration { source = R }) |
     ( { typeAlias = X } = D;
       X = TypeAliasDeclaration { source = R }) |
     ( { namespaceAlias = X } = D;
       X = NamespaceAliasDeclaration { source = R })

# cxx1.RecordDefinition can lookup from Child to Parent. Reverse this
# here to lookup from Parent to Child.
predicate RecordDerived :
  {
    base : RecordDeclaration,
    derived : RecordDeclaration,
  }
  stored
  {Base, Derived}
  where
   cxx1.RecordDefinition{ declaration = Derived, bases = BS };
   cxx1.RecordBase { base = Base } = BS[..]

# cxx1.MethodsOverrides can lookup from Derived to Base. Reverse this
# here to lookup from Base to Derived.
predicate MethodOverridden :
  {
    base : FunctionDeclaration,
    derived : FunctionDeclaration,
  }
  stored
  {Base, Derived}
  where
    cxx1.MethodOverrides{derived = Derived, base = Base}

#
# Searching for declarations by name. One per kind
#

# For search, index just those with non-empty namespace local names
predicate NamespaceDeclarationByName :
  {
    name : string,
    parent: maybe NamespaceQName,
    decl : NamespaceDeclaration
  } stored { NameStr, Parent, Decl } where
    Name = cxx1.Name NameStr;
    NamespaceDeclaration { name = { { just = Name }, Parent } } = Decl

predicate NamespaceLowerCase :
  {
    name_lowercase: string,
    name: string
  } stored { prim.toLower NameStr, NameStr } where
    NamespaceQName { name = { just = Name NameStr }}

# Derived predicates to distinguish records by kind, to avoid enumerating all
# records when searching. We flatten the QName so that the Name key is left most,
# avoiding the need to enumerate QNames to find things.
predicate RecordDeclarationStruct :
  {
    name : string,
    scope: Scope,
    decl : RecordDeclaration
  } stored { NameStr, Scope, Decl } where
    Name = cxx1.Name NameStr;
    RecordDeclaration { name = { Name, Scope }, kind = { struct_ = _ } } = Decl

predicate RecordStructLowerCase :
  {
    name_lowercase: string,
    name: string
  } stored { prim.toLower NameStr, NameStr } where
    RecordDeclaration { name = { name = Name NameStr }, kind = { struct_ = _ } };

# Derived
predicate RecordDeclarationClass :
  {
    name : string,
    scope: Scope,
    decl : RecordDeclaration
  } stored { NameStr, Scope, Decl } where
    Name = cxx1.Name NameStr;
    RecordDeclaration { name = { Name, Scope }, kind = { class_ = _ } } = Decl

predicate RecordClassLowerCase :
  {
    name_lowercase: string,
    name: string
  } stored { prim.toLower NameStr, NameStr } where
    RecordDeclaration { name = { name = Name NameStr }, kind = { class_ = _ } };

# Derived
predicate RecordDeclarationUnion :
  {
    name : string,
    scope: Scope,
    decl : RecordDeclaration
  } stored { NameStr, Scope, Decl } where
    Name = cxx1.Name NameStr;
    RecordDeclaration { name = { Name, Scope }, kind = { union_ = _ } } = Decl

predicate RecordUnionLowerCase :
  {
    name_lowercase: string,
    name: string
  } stored { prim.toLower NameStr, NameStr } where
    RecordDeclaration { name = { name = Name NameStr }, kind = { union_ = _ } };

# Re-index left-keyed by cxx1.Name fact to avoid QName scans
predicate EnumDeclarationByName :
  { name : string,
    scope : Scope,
    decl : EnumDeclaration
  } stored { NameStr, Scope, Decl } where
    EnumDeclaration { name = { Name, Scope } } = Decl;
    Name = cxx1.Name NameStr;

predicate EnumLowerCase :
  {
    name_lowercase: string,
    name: string
  } stored { prim.toLower NameStr, NameStr } where
    EnumDeclaration { name = { name = Name NameStr } };

# Search: functions with real names only, to restrict to scope use the FQName
predicate FunctionDeclarationByNameScope :
  {
    name: string,
    scope: Scope,
    decl: FunctionDeclaration
  } stored { NameStr, Scope, Decl } where
    FName = FunctionName { name = Name };
    FQName = FunctionQName { name = FName, scope = Scope };
    cxx1.Name NameStr = Name;
    cxx1.FunctionDeclaration { name = FQName } = Decl;

predicate FunctionLowerCase :
  {
    name_lowercase: string,
    name: string
  } stored { prim.toLower NameStr, NameStr } where
    FunctionDeclaration { name = FQName };
    FQName = FunctionQName { name = FName };
    FName = FunctionName { name = Name NameStr }

# searching for ObjcContainers. We need to split these up by kind
predicate ObjcContainerDeclarationInterface :
  {
    name: string,
    decl: ObjcContainerDeclaration
  } stored { NameStr, Decl } where
    cxx1.ObjcContainerDeclaration { id = ObjcId } = Decl;
    ObjcId =
      { interface_ = Name } |
      { categoryInterface = { className = Name } } |
      { extensionInterface = Name };
    cxx1.Name NameStr = Name

predicate ObjcContainerInterfaceLowerCase :
  {
    name_lowercase: string,
    name: string
  } stored { prim.toLower NameStr, NameStr } where
    cxx1.ObjcContainerDeclaration { id = ObjcId };
    ObjcId =
      { interface_ = Name } |
      { categoryInterface = { className = Name } } |
      { extensionInterface = Name };
    cxx1.Name NameStr = Name

# Search enumerators by name
predicate EnumeratorByName :
  {
    name: string,
    decl: cxx1.Enumerator
  } stored { NameStr, Decl } where
    cxx1.Enumerator { name = cxx1.Name NameStr } = Decl

predicate EnumeratorLowerCase :
  {
    name_lowercase: string,
    name: string
  } stored { prim.toLower NameStr, NameStr } where
    Enumerator { name = cxx1.Name NameStr }

# type alias search by name
predicate TypeAliasDeclarationByName :
  {
    name : string,
    scope : Scope,
    decl : cxx1.TypeAliasDeclaration
  } stored { NameStr, Scope, Decl } where
    cxx1.TypeAliasDeclaration { name = { cxx1.Name NameStr, Scope } } = Decl

predicate TypeAliasLowerCase :
  {
    name_lowercase: string,
    name: string
  } stored { prim.toLower NameStr, NameStr } where
    TypeAliasDeclaration { name = { name = Name NameStr } }

# Variable search by name. Let's not include locals since they're too numerous
predicate VariableDeclarationNonLocalByName :
  {
    name : string,
    scope: Scope,
    decl : VariableDeclaration
  } stored { NameStr, Decl.name.scope, Decl } where
    Decl.name.name = cxx1.Name NameStr;
    Decl.kind.global_? | Decl.kind.field? | Decl.kind.ivar?;

predicate VariableLowerCase :
  {
    name_lowercase: string,
    name: string
  } stored { prim.toLower NameStr, NameStr } where
    VariableDeclaration { name = QName, kind = Kind };
    Kind.global_? | Kind.field? | Kind.ivar?;
    QName.name = Name NameStr;

#
# Searching for declarations by case-insensitive name or prefix
# We have tried a few things here:
#   lowercase name to decl
#   lowercase name to kind and upper name
#
# We want to use kind-partitioned tables so that kind-filtered searches work well,
# leading to the current design with one predicate per kind

# Use this derived predicate to handle declarations without a family in
# a uniform way as declarations with a family.
predicate DeclFamilyOf :
  {
    decl : Declaration,
    family : Declaration,
  }
  {Decl, FamDecl}
  where
    FamDecl = Decl |
    ( D where
      cxx1.DeclToFamily{decl=Decl, family=DF};
      DF=cxx1.DeclFamily F;
      D=F[..]
    )

# Maps from a declaration to a RecordDefinition that has
# (a family member of) the declaration as a member.

# With no care for the space use we could make a stored derived predicated from
# reversing the RecordDefinitions members, but since we care about space
# let's perform this search on the fly.
#
# This is quite an elaborate search, for the following reasons:
#
# - We have a Declaration R::F
# - A corresponding RecordDefinition can be found by searching for
#   RecordDefinition { declaration = { name = R }}
# - But there might be multiple of these (due to CPP, templates etc.),
#   and some of them might not even contain R::F, so we have to find a
#   correct one by searching for our R::F amongst the members.
# - Furthermore, since the members might not contain our R::F but another
#   decl in the same DeclFamily, we have to look up the family of R::F too.
#
# See T68843402 for progress on each declaration type.
#
# It might be worth swapping the order of the DeclFamilyOf and
# RecordDefinition queries, but to do that we would need to prove
# that all decl D in a family share an identical scope.

predicate DeclInRecord :
  {
    decl : Declaration,
    record : RecordDefinition,
  }
  { DeclIn, Record } where
    # Declaration{namespace} cannot be in a record
    # Declaration{objcContainer} cannot be in a record
    # Declaration{ObjcMethodDeclaration} cannot be in a record
    # Declaration{ObjcPropertyDeclaration} cannot be in a record
    DeclFamilyOf { DeclIn, Decl };
    cxx1.DeclarationScope { Decl, Scope };
    { recordWithAccess = { record = QName } } = Scope;
    DeclOut = cxx1.RecordDeclaration { name = QName }; # find the parent record
    Record = cxx1.RecordDefinition{ declaration = DeclOut, members = Decls };
    Decls = cxx1.Declarations DS;
    DeclIn = DS[..]; # verify in decl is a member

# Like DeclInRecord but exploit the fact we already have the base decl/defn
predicate DefnInRecord :
  {
    defn : DefinitionEntity,
    record : RecordDefinition
  }
  { DefnIn, Record} where
    cxx1.DefToBaseDecl { DefnIn, DeclIn };
    cxx1.DeclarationScope { DeclIn, Scope };
    { recordWithAccess = { record = QName } } = Scope;
    DeclOut = cxx1.RecordDeclaration { name = QName }; # find the parent record
    Record = cxx1.RecordDefinition{ declaration = DeclOut, members = Decls };
    Decls = cxx1.Declarations DS;
    DeclIn = DS[..]; # verify in decl is a member

# This allows going from a objc method or property declaration
# to a definition that contains a family member of the declaration.
#
# If we could prove all decl D in a family share an
# identical container id then we could change the order
# of the queries for the DeclFamilyOf and ObjcContainerDefinition.
#
#  With no care for the space use we could make a stored derived predicated from
# reversing the RecordDefinitions members.  Since we care about space we can
# narrow the search space and then check the members.
predicate DeclInObjcContainer :
  {
    decl : Declaration,
    record : ObjcContainerDefinition,
  }
  {DeclIn, Container}
  where
  DeclFamilyOf{decl=DeclIn, family=Decl};

  CId =
    (
      CId where { objcMethod = X } = Decl;
      ObjcMethodDeclaration{container = CId} = X;
    ) | (
      CId where { objcProperty = X } = Decl;
      ObjcPropertyDeclaration{container = CId} = X;
    );
  Container = ObjcContainerDefinition{declaration={id = CId}, members=Decls};
  Decls = Declarations DS;
  Decl = DS[..];

# This allows going from an enumerator to the enum definition(s) that
# contain the enumerator.
#
#  With no care for the space use we could make a stored derived predicated from
# reversing the RecordDefinitions members.  Since we care about space we can
# narrow the search space and then check the enmerators list.
predicate EnumeratorInEnum :
  {
    enumerator : Enumerator,
    enum_ : EnumDefinition
  }
  {EnumeratorIn, EnumDefOut}
  where
    Enumerator{enumeration = Decl} = EnumeratorIn;
    EnumDefOut = EnumDefinition{declaration = Decl, enumerators = Enums};
    EnumeratorIn = Enums[..]

# Given a Declaration (in some file), find the Trace(s) (in that file) that
# contain the Declaration. Very useful for looking up members of a namespace.
predicate DeclarationInTrace :
  {
    decl : Declaration,
    trace : Trace
  }
  {Decl, T}
  where
  DeclarationSrcRange{decl = Decl, source = {file = F}};
  T = Trace{file = F, declarations = Decls};
  Decls = Declarations DS;
  Decl = DS[..]

predicate ObjcContainerBase :
    {
        declaration : ObjcContainerDeclaration,
        base : ObjcContainerDeclaration,
    }

predicate ObjcContainerInheritance :
    {
        base : ObjcContainerDeclaration,
        declaration : ObjcContainerDeclaration,
    }
    stored
    { Base, Decl }
    where
      ObjcContainerBase { Decl, Base }

predicate Attribute : string

# Lookup functions with a particular attribute
predicate FunctionAttribute :
  {
    attr : Attribute,
    declaration : FunctionDeclaration,
  }

# Inverse of FunctionAttribute. Lookup attributes using function decls as keys
predicate FunctionDeclAttribute :
  {
    decl : FunctionDeclaration,
    attr : Attribute,
  } stored
  { Decl, Attr } where cxx1.FunctionAttribute { Attr, Decl }

# ObjCContainerId to Name
predicate ObjContainerIdName :
  {
    id : ObjcContainerId,
    name : Name,
  }
  { Id, Name } where
    ( { protocol = Name } = Id) |
    ( { interface_ = Name } = Id) |
    ( { categoryInterface = { categoryName = Name } } = Id) |
    ( { extensionInterface = Name } = Id) |
    ( { implementation = Name } = Id) |
    ( { categoryImplementation = { categoryName = Name  } } = Id)

# General purpose Declaration to shortname Name
predicate DeclarationLocationName :
  {
    decl : Declaration,
    source : src.Range,
    name : string,
  }
  { Decl, Range, NameStr } where
    (
      ( { namespace_ = X } = Decl;
        X = NamespaceDeclaration { source = Range, name = QName };
        NamespaceDeclarationName { QName, Name }) |
      ( { usingDeclaration = X } = Decl;
        X = UsingDeclaration { source = Range, name = QName };
        QName = FunctionQName { name = { name = Name }}) |
      ( { usingDirective = X } = Decl;
        X = UsingDirective { source = Range, name = { name = Name }}) |
      ( { record_ = X } = Decl;
        X = RecordDeclaration { source = Range, name = { name = Name }}) |
      ( { enum_ = X } = Decl;
        X = EnumDeclaration { source = Range, name = { name = Name }}) |
      ( { variable = X } = Decl;
        X = VariableDeclaration {
          source = Range,
          name = { name = Name}}) |
      ( { objcContainer = X } = Decl;
        X = ObjcContainerDeclaration { source = Range, id = Id };
        ObjContainerIdName { Id, Name }) |
      ( { objcMethod = X } = Decl;
        X = ObjcMethodDeclaration { source = Range };
        ObjcMethodDeclarationName { X, Name }) |
      ( { objcProperty = X } = Decl;
        X = ObjcPropertyDeclaration { source = Range, name = Name }) |
      ( { typeAlias = X } = Decl;
        X = TypeAliasDeclaration { source = Range, name = { name = Name }}) |
      ( { namespaceAlias = X } = Decl;
        X = NamespaceAliasDeclaration { source = Range, name = QName };
        NamespaceDeclarationName { QName, Name });
      Name = Name NameStr
    ) | (
      { function_ = X } = Decl;
      FunctionDeclaration { source = Range, name = { FName, _ } } = X;
      FunctionDeclarationNameString { FName, NameStr }
    )

# short name for function declarations
predicate FunctionDeclarationName:
  { fname : FunctionName
  , name : Name
  }
  { FName, Name } where
     # common case, ordinary names
    ( { name = Name } = FName ) |
    ( { operator_ = OpNameStr } = FName;
      Name = Name OpNameStr; ) |
    ( { literalOperator = OpNameStr } = FName;
      Name = Name OpNameStr; ) |
    ( { constructor = _ } = FName;
      Name = Name "constructor"; ) |
    ( { destructor = _ } = FName;
      Name = Name "destructor"; ) |
    ( { conversionOperator = Type TypeStr } = FName;
      Name = Name TypeStr; )

predicate FunctionDeclarationNameString:
  { fname : FunctionName
  , name : string
  }
  { FName, Name } where
     # common case, ordinary names
    ( { name = Name : string } = FName ) |
    ( { operator_ = Name } = FName; ) |
    ( { literalOperator = Name } = FName; ) |
    ( { constructor = _ } = FName;
      Name = "constructor"; ) |
    ( { destructor = _ } = FName;
      Name = "destructor"; ) |
    ( { conversionOperator = Type Name } = FName; )

# short name for namespace declarations
predicate NamespaceDeclarationName:
  { qname : NamespaceQName
  , name : Name
  }
  { QName, Name } where
    ( NamespaceQName { name = { just = Name }} = QName ) |
    ( NamespaceQName { name = nothing } = QName; Name = Name "anonymous" )

predicate DeclarationLocationNameSpan :
  {
    decl : Declaration,
    source : src.Range,
    name : string,
    file: src.File,
    span : src.ByteSpan,
  }
  { Decl, Range, NameStr, File, Span } where
    (
      ( { namespace_ = X } = Decl;
        X = NamespaceDeclaration { name = QName, source = Range };
        NamespaceDeclarationName { QName, Name }) |
      ( { usingDeclaration = X } = Decl;
        X = UsingDeclaration { name = QName, source = Range };
        QName = FunctionQName { name = { name = Name }}) |
      ( { usingDirective = X } = Decl;
        X = UsingDirective { name = { name = Name }, source = Range}) |
      ( { record_ = X } = Decl;
        X = RecordDeclaration { name = { name = Name }, source = Range }) |
      ( { enum_ = X } = Decl;
        X = EnumDeclaration { name = { name = Name }, source = Range }) |
      ( { variable = X } = Decl;
        X = VariableDeclaration { name = { name = Name }, source = Range }) |
      ( { objcContainer = X } = Decl;
        X = ObjcContainerDeclaration { id = Id, source = Range };
        ObjContainerIdName { Id, Name }) |
      ( { objcMethod = X } = Decl;
        X = ObjcMethodDeclaration { source = Range };
        ObjcMethodDeclarationName { X, Name }) |
      ( { objcProperty = X } = Decl;
        X = ObjcPropertyDeclaration { name = Name, source = Range }) |
      ( { typeAlias = X } = Decl;
        X = TypeAliasDeclaration { name = { name = Name }, source = Range }) |
      ( { namespaceAlias = X } = Decl;
        X = NamespaceAliasDeclaration { name = QName, source = Range };
        NamespaceDeclarationName { QName, Name });
      Name = Name NameStr
    ) | (
      { function_ = X } = Decl;
      FunctionDeclaration { name = { FName, _ }, source = Range } = X;
      FunctionDeclarationNameString { FName, NameStr }
    );
    DeclarationNameSpan { Decl, File, Span }

# Mirror of type in code.cxx.angle
type DefinitionEntity =
  {
    record_ : cxx1.RecordDefinition |
    function_ : cxx1.FunctionDefinition |
    enum_ : cxx1.EnumDefinition |
    objcMethod : cxx1.ObjcMethodDefinition |
    objcContainer : cxx1.ObjcContainerDefinition |
    variable : cxx1.VariableDeclaration |
    namespace_ : cxx1.NamespaceDefinition |
  }

# Short name and location of a definition (via its embedded declaration)
# N.B. This isn't the inverse of DeclToDef (use DeclFamily for that), this
# just unwraps a Definition to get at its root declaration value
predicate DefToBaseDecl:
  {
    defn : DefinitionEntity,
    decl : Declaration,
  }
  { Defn, Decl } where
    ( { record_ = { declaration = X } } = Defn;
      Decl = Declaration { record_ = X } ) |
    ( { function_ = { declaration = X } } = Defn;
      Decl = Declaration { function_ = X } ) |
    ( { enum_ = { declaration = X } } = Defn;
      Decl = Declaration { enum_ = X } ) |
    ( { objcMethod = ObjcMethodDefinition X } = Defn;
      Decl = Declaration { objcMethod = X } ) |
    ( { objcContainer = { declaration = X } } = Defn;
      Decl = Declaration { objcContainer = X } ) |
    ( { variable = X } = Defn;
      Decl = Declaration { variable = X } ) |
    ( { namespace_ = { declaration = X } } = Defn;
      Decl = Declaration { namespace_ = X } )

# All uses of preprocessor #defines in a file, as xrefs
predicate FilePPUseXRefs:
  {
    file: src.File,
    source: src.Range,
    define: pp1.Define,
  }
  { File, SrcRange, Define } where
    Trace = cxx1.Trace { file = File };
    FilePPUseTraceXRefs { File, Trace, SrcRange, Define };

# All uses of preprocessor #defines in a file, as xrefs, for a specific trace
# (deprecated)
predicate FilePPUseTraceXRefs:
  {
    file: src.File,
    trace: cxx1.Trace,
    source: src.Range,
    define: pp1.Define,
  }
  { File, Trace, SrcRange, Define } where
    Trace = cxx1.Trace { file = File, preprocessor = { events = PPEvents } };
    PPEvent = PPEvents[..];
    { use = Use } = PPEvent;
    { macro = Macro, definition = { just = { file = TargetFile }}, source = SrcRange } = Use;

    # Search for the macro define, since 'definition' isn't a range xref
    Define = pp1.Define { macro = Macro, source = { file = TargetFile } };

# Definition location for a pp1.Define
# (deprecated)
predicate PPDefineLocation :
  {
    define : pp1.Define,
    name : string,
    file : src.File,
    range : src.Range,
  }
  { Define, Name, File, Range } where
    { pp1.Macro Name , Range } = Define;
    { file = File } = Range;

# Type of targets of pp xrefs (shape of code.pp.angle:Entity)
type PpEntity =
  {
    define : pp1.Define |
    undef : pp1.Undef |
    include_ : src.File |
  }

# C preprocessor entities xrefs in this file
predicate FilePPTraceXRefs:
  {
    file: src.File,
    trace: cxx1.Trace,
    source: src.Range,
    ppEntity: PpEntity,
    span: src.ByteSpan,
  }
  { File, Trace, SrcRange, Entity, SrcSpan } where
    Trace = cxx1.Trace { file = File, preprocessor = { events = PPEvents } };
    PPEvent = PPEvents[..];
    # Uses are xrefs to #define entities
    ( { SrcRange, Entity, SrcSpan } where
      { use = Use } = PPEvent;
      { Macro, { just = { file = TargetFile }}, _, SrcRange, SrcSpan } = Use;
      Define = pp1.Define { Macro, { file = TargetFile } };
      { define = Define } = Entity ) |

    # #includes are xrefs to src.Files
    ( { SrcRange, Entity, SrcSpan } where
      { include_ = { include_ = Include } } = PPEvent;
      { TargetFile, SrcRange, SrcSpan } = Include;
      { include_ = TargetFile } = Entity; )

# This maps generated Cxx entities
# to their defining Thrift declaration
# currently limited to functions
predicate CxxToThrift:
  {
    from: cxx1.XRefTarget,
    to: fbthrift.Declaration
  }

predicate ThriftToCxx:
  {
    to: fbthrift.Declaration,
    from: cxx1.XRefTarget
  }
  stored { To, From }
    where cxx1.CxxToThrift { From, To }

}