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 }
}