Files
codeql/cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll
Jeroen Ketema b7d1da8741 C++: Introduce a new base class for template parameters
This will enable us to support non-type template parameters, which we
currently do not support, and error template parameters, which might
become relevant in the `build-mode: none` context.
2024-12-17 20:25:41 +01:00

503 lines
17 KiB
Plaintext

import semmle.code.cpp.Element
import semmle.code.cpp.Declaration
import semmle.code.cpp.Function
import semmle.code.cpp.Variable
/**
* Options that control the dependencies generated by
* this library.
*/
class DependencyOptions extends string {
DependencyOptions() { this = "DependencyOptions" }
/**
* Holds if dependencies should only be generated in templates rather than
* in both templates and instantiations, where possible. This is expensive
* to compute, but tends to produce dependencies that are easier to read.
*/
cached
predicate preferTemplateDeps() { any() }
}
/**
* Gets the `DependencyOptions`.
*/
DependencyOptions getDependencyOptions() { any() }
/**
* An Element that can be the source of a transitive dependency. This is any
* Element that is not in a template instantiation, plus declarations of template
* specializations (even though they are technically in an instantiation) because
* we need to generate (at least) a dependency from them to the general declaration.
*/
class DependsSource extends Element {
DependsSource() {
// not inside a template instantiation
not this.isFromTemplateInstantiation(_) or
// allow DeclarationEntrys of template specializations
this.(DeclarationEntry).getDeclaration().(Function).isConstructedFrom(_) or
this.(DeclarationEntry).getDeclaration().(Class).isConstructedFrom(_)
}
}
/**
* A program element which can be the target of inter-function or inter-file dependencies.
*
* This is the union of Declaration, DeclarationEntry and Macro, minus various kinds of declaration:
* * FriendDecl is not included, as a friend declaration cannot be the target of a dependency (nor, as it happens, can they be a source).
* * TemplateParameter and related UserTypes are not included, as they are intrinsic sub-components of their associated template.
* * Template instantiations are excluded, as the template itself is more useful as a dependency target.
* * Stack variables and local types are excluded, as they are lexically tied to their enclosing function, and intra-function dependencies
* can only be inter-file dependencies in pathological cases.
* * Builtin functions and macros are excluded, as dependencies on them do not translate to inter-file dependencies (note that static functions
* and declarations within anonymous namespaces cannot be excluded for this reason, as the declaration can be in a header).
* * DeclarationEntrys are only needed if they're not definitions, for the definition to declaration dependency.
*/
class Symbol extends DependsSource {
Symbol() {
(
exists(EnumConstant ec | this = ec and not ec.getDeclaringEnum() instanceof LocalEnum)
or
this instanceof Macro and this.getFile().getAbsolutePath() != ""
or
this instanceof DeclarationEntry and
not this.(VariableDeclarationEntry).getVariable() instanceof LocalScopeVariable and
not this.(FunctionDeclarationEntry).getFunction() instanceof BuiltInFunction and
not this.(TypeDeclarationEntry).getType() instanceof LocalEnum and
not this.(TypeDeclarationEntry).getType() instanceof LocalClass and
not this.(TypeDeclarationEntry).getType() instanceof LocalTypedefType and
not this.(TypeDeclarationEntry).getType() instanceof TypeTemplateParameter
or
this instanceof NamespaceDeclarationEntry
)
}
/**
* Gets an element which depends upon this symbol.
*
* To a first approximation, dependent elements can be thought of as occurrences of the symbol's name: instances of `VariableAccess`
* for `Variable` symbols, instances of `MacroInvocation` for `Macro` symbols, and so on.
*
* category:
* 1 - C/C++ compile-time dependency
* 2 - C/C++ link-time dependency (or transitive dependency with a link-time component)
*/
cached
Element getADependentElement(int category) { dependsOnFull(result, this, category) }
}
/**
* Associates a Declaration with it's DeclarationEntries, or (for a template
* instantiation) with the DeclarationEntries of its template.
*/
cached
predicate getDeclarationEntries(Declaration decl, DeclarationEntry de) {
(
decl = de.getDeclaration() or
decl.(Function).isConstructedFrom(de.getDeclaration()) or
decl.(Class).isConstructedFrom(de.getDeclaration())
) and
// ParameterDeclarationEntries are special, as (a) they can only be accessed
// from within the definition, and (b) non-definition PDEs may be commonly
// included. Thus, for PDEs, we point only to the definition.
(de instanceof ParameterDeclarationEntry implies de.isDefinition())
}
/**
* A 'simple' dependency from src to dest. This type of dependency
* does not make any special account of templates.
*
* Consider using Symbol.getADependentElement() rather than directly
* accessing this predicate.
*/
predicate dependsOnSimple(Element src, Element dest) {
dependsOnSimpleInline(src, dest) or
dependency_macroUse(src, dest)
}
/**
* A 'simple' dependency that might be inlined.
*/
private predicate dependsOnSimpleInline(Element src, Element dest) {
dependency_functionUse(src, dest) or
dependency_typeUse(src, dest) or
dependency_variableUse(src, dest) or
dependency_usingDeclaration(src, dest) or
dependency_usingNamespace(src, dest) or
dependency_enumConstantUse(src, dest) or
dependency_outOfLineDeclaration(src, dest) or
dependency_outOfLineInitializer(src, dest) or
dependency_functionSpecialization(src, dest) or
dependency_classSpecialization(src, dest)
}
/**
* Holds if a simple, non-template dependency exists between two Locations
* specified by the parameters.
*/
private predicate dependsLocation(
File f1, int sl1, int sc1, int el1, int ec1, File f2, int sl2, int sc2, int el2, int ec2
) {
exists(Element src, Element dest, Location loc1, Location loc2 |
dependsOnSimpleInline(src, dest) and
src instanceof DependsSource and
loc1 = src.getLocation() and
f1 = loc1.getFile() and
sl1 = loc1.getStartLine() and
sc1 = loc1.getStartColumn() and
el1 = loc1.getEndLine() and
ec1 = loc1.getEndColumn() and
loc2 = dest.getLocation() and
f2 = loc2.getFile() and
sl2 = loc2.getStartLine() and
sc2 = loc2.getStartColumn() and
el2 = loc2.getEndLine() and
ec2 = loc2.getEndColumn()
)
}
/**
* Holds if a simple dependency from `loc` to `loc2` in a template has a
* non-template alternative?
* (if `DependencyOptions.preferTemplateDeps()` is enabled)
*/
private predicate dependsNonTemplateAlternative(Location loc1, Location loc2) {
getDependencyOptions().preferTemplateDeps() and
exists(Element src, Element dest |
dependsOnSimpleInline(src, dest) and
src.isFromTemplateInstantiation(_) and
src.getLocation() = loc1 and
dest.getLocation() = loc2
) and
dependsLocation(loc1.getFile(), loc1.getStartLine(), loc1.getStartColumn(), loc1.getEndLine(),
loc1.getEndColumn(), loc2.getFile(), loc2.getStartLine(), loc2.getStartColumn(),
loc2.getEndLine(), loc2.getEndColumn())
}
/**
* A simple dependency from src to a declaration dest, where the definition is not
* needed at compile time.
*/
predicate dependsOnDeclOnly(Element src, Element dest) {
dependency_functionUse(src, dest) or
dependency_variableUse(src, dest) or
dependency_pointerTypeUse(src, dest)
}
/**
* A dependency from src to dest. This predicate inlines
* template dependencies.
*/
private predicate dependsOnViaTemplate(Declaration src, Element dest) {
// A template instantiation depends on everything that anything
// inside it depends upon. This effectively inlines the things
// inside at the point where the template is called or
// referenced.
exists(Element internal, Location internalLocation, Location destLocation |
// internal is an element in the template {function or class} instantiation that cannot
// itself be a transitive dependency source
internal.isFromTemplateInstantiation(src) and
// don't generate template dependencies through a member function of a template class;
// these dependencies are also generated through the class, which has to be referenced
// somewhere anyway.
not exists(Class c |
internal.isFromTemplateInstantiation(c) and
src.getDeclaringType() = c
) and
// dest is anything that the internal element depends upon
dependsOnSimpleInline(internal, dest) and
// is there something in the template (not the instantiation) that's generating
// (better) dependencies from internal anyway?
internalLocation = internal.getLocation() and
destLocation = dest.getLocation() and
not dependsNonTemplateAlternative(internalLocation, destLocation)
)
}
/**
* Holds if `src` is related to `dest` by one `dependsOnSimple` and any
* number of `dependsOnViaTemplate` steps.
*
* Consider using `Symbol.getADependentElement()` rather than directly
* accessing this predicate.
*/
predicate dependsOnTransitive(DependsSource src, Element dest) {
exists(Element mid1 |
// begin with a simple step
dependsOnSimpleInline(src, mid1) and
// any number of recursive steps
(
mid1 = dest or // mid1 is not necessarily a Declaration
dependsOnViaTemplate+(mid1, dest)
)
)
or
dependency_macroUse(src, dest)
}
/**
* A dependency that targets a TypeDeclarationEntry.
*/
private predicate dependsOnTde(Element src, Type t, TypeDeclarationEntry dest) {
dependsOnTransitive(src, t) and
getDeclarationEntries(t, dest)
}
/**
* A dependency that targets a visible TypeDeclarationEntry.
*/
pragma[noopt]
private predicate dependsOnVisibleTde(Element src, Type t, TypeDeclarationEntry dest) {
dependsOnTde(src, t, dest) and
exists(File g | g = dest.getFile() |
exists(File f | f = src.getFile() | f.getAnIncludedFile*() = g)
)
}
/**
* A dependency that targets a DeclarationEntry
*/
private predicate dependsOnDeclarationEntry(Element src, DeclarationEntry dest) {
exists(Type t |
// dependency from a Type use -> unique visible TDE
dependsOnVisibleTde(src, t, dest) and
strictcount(TypeDeclarationEntry alt | dependsOnVisibleTde(src, t, alt)) = 1
)
or
exists(TypedefType mid |
// dependency from a TypedefType use -> any (visible) TDE
dependsOnTransitive(src, mid) and
getDeclarationEntries(mid, dest.(TypeDeclarationEntry))
)
or
exists(Declaration mid |
// dependency from a Variable / Function use -> any (visible) declaration entry
dependsOnTransitive(src, mid) and
not mid instanceof Type and
not mid instanceof EnumConstant and
getDeclarationEntries(mid, dest) and
not dest instanceof TypeDeclarationEntry
)
or
exists(Declaration mid |
// dependency from a Type / Variable / Function use -> any (visible) definition
dependsOnTransitive(src, mid) and
not mid instanceof EnumConstant and
getDeclarationEntries(mid, dest) and
// must be definition
dest.isDefinition()
)
}
/**
* The full dependsOn relation, made up of dependsOnTransitive plus some logic
* to fix up the results for Declarations to most reasonable DeclarationEntrys.
*/
private predicate dependsOnFull(DependsSource src, Symbol dest, int category) {
// direct result
dependsOnTransitive(src, dest) and
category = 1
or
// result to a visible DeclarationEntry
dependsOnDeclarationEntry(src, dest) and
src.getFile().getAnIncludedFile*() = dest.getFile() and
category = 1
or
exists(Declaration mid |
// dependency from a Variable / Function use -> non-visible definition (link time)
dependsOnTransitive(src, mid) and
not mid instanceof EnumConstant and
getDeclarationEntries(mid, dest) and
not dest instanceof TypeDeclarationEntry and
// must be definition
dest.(DeclarationEntry).isDefinition() and
// must not be visible (else covered above)
not src.getFile().getAnIncludedFile*() = dest.getFile() and
// filter out FDEs that are only defined in the dummy link target
(
(
dest instanceof FunctionDeclarationEntry and
isLinkerAwareExtracted()
)
implies
exists(LinkTarget lt | not lt.isDummy() |
lt.getAFunction() = dest.(FunctionDeclarationEntry).getFunction()
)
) and
category = 2
)
}
/**
* A dependency caused by a function call / use.
*/
private predicate dependency_functionUse(Element src, Function dest) {
funbind(unresolveElement(src), unresolveElement(dest))
}
/**
* A Type which refers to a UserType.
*/
cached
private predicate refersToUserType(Type a, UserType b) { a.refersTo(b) }
/**
* A Type which refers to a type directly, without using a pointer or reference.
*/
private predicate refersToDirectlyNonPointer(Type a, Type b) {
a.refersToDirectly(b) and
not a instanceof PointerType and
not a instanceof ReferenceType
}
/**
* A Type which refers to a UserType, but only through a pointer or reference.
*/
cached
private predicate refersToUserTypePointer(Type a, UserType b) {
refersToUserType(a, b) and
not refersToDirectlyNonPointer*(a, b)
}
/**
* A dependency caused by a type use.
*/
private predicate dependency_typeUse(Element src, UserType dest) {
refersToUserType(typeUsedBy(src), dest)
}
/**
* A dependency caused by a pointer/reference type use only.
*/
predicate dependency_pointerTypeUse(Element src, UserType dest) {
refersToUserTypePointer(typeUsedBy(src), dest)
}
/**
* The Types that must be defined for a particular Element.
*/
private Type typeUsedBy(Element src) {
result = src.(VariableDeclarationEntry).getType() and
not src.(VariableDeclarationEntry).getVariable().declaredUsingAutoType()
or
result = src.(FunctionDeclarationEntry).getType()
or
result = src.(Cast).getType() and not src.(Cast).isImplicit()
or
result = src.(ClassDerivation).getBaseClass()
or
result = src.(TypeDeclarationEntry).getType().(TypedefType).getBaseType()
or
result = src.(TypeDeclarationEntry).getDeclaration().(Enum).getExplicitUnderlyingType()
or
result = src.(SizeofTypeOperator).getTypeOperand()
or
exists(Function f |
funbind(unresolveElement(src), unresolveElement(f)) and result = f.getATemplateArgument()
)
or
result = src.(NewExpr).getType() and not result.(Class).hasConstructor()
or
result = src.(NewArrayExpr).getType() and
not result.(ArrayType).getBaseType().(Class).hasConstructor()
or
result = src.(DeleteExpr).getExpr().getType() and
not result.(PointerType).getBaseType().(Class).hasDestructor()
or
result = src.(DeleteArrayExpr).getExpr().getType() and
not result.(PointerType).getBaseType().(Class).hasDestructor()
}
/**
* A dependency caused by a variable use.
*/
private predicate dependency_variableUse(VariableAccess src, Variable dest) {
src.getTarget() = dest and
not dest instanceof LocalScopeVariable
}
/**
* A dependency caused by an enum constant use.
*/
private predicate dependency_enumConstantUse(EnumConstantAccess src, EnumConstant dest) {
src.getTarget() = dest
}
/**
* A dependency caused by a macro access.
*/
private predicate dependency_macroUse(MacroAccess src, Macro dest) { src.getMacro() = dest }
/**
* A dependency caused by a 'using' declaration 'using X::Y'.
*/
private predicate dependency_usingDeclaration(UsingDeclarationEntry src, Declaration dest) {
src.getDeclaration() = dest
}
/**
* A dependency caused by a 'using' directive 'using namespace X'.
*/
private predicate dependency_usingNamespace(UsingDirectiveEntry src, NamespaceDeclarationEntry dest) {
exists(Namespace nsdecl |
nsdecl = src.getNamespace() and
dest.getNamespace() = nsdecl and
dest.getFile().getAnIncludedFile*() = src.getFile() and
(
dest.getFile() = src.getFile()
implies
dest.getLocation().getStartLine() < src.getLocation().getStartLine()
) and
none() // temporarily disabled until we have suitable UI in Architect
)
}
/**
* A dependency from the definition of a class member to a corresponding declaration. This
* ensures that an externally defined class member has a dependency on (something in) the
* class definition.
*/
private predicate dependency_outOfLineDeclaration(DeclarationEntry src, DeclarationEntry dest) {
src.getDeclaration().hasDeclaringType() and
src.isDefinition() and
(
dest.getDeclaration() = src.getDeclaration()
or
// also permit out of line declarations to jump from the declaration of a specialized
// function to it's definition in the primary template. Note that the specialization
// in this case may be on a template class parameter.
src.getDeclaration().(Function).isConstructedFrom(dest.getDeclaration())
) and
not dest.isDefinition()
}
/**
* A dependency from an initialization of a (static) class member to a corresponding
* declaration.
*/
private predicate dependency_outOfLineInitializer(Initializer src, DeclarationEntry dest) {
src.getDeclaration().hasDeclaringType() and
dest.getDeclaration() = src.getDeclaration() and
not dest.isDefinition()
}
/**
* A dependency from a template function specialization to the general one.
*/
private predicate dependency_functionSpecialization(DeclarationEntry src, DeclarationEntry dest) {
exists(FunctionTemplateSpecialization fts |
src.getDeclaration() = fts and
dest.getDeclaration() = fts.getPrimaryTemplate()
)
}
/**
* A dependency from a template class specialization to the most general one.
*/
private predicate dependency_classSpecialization(DeclarationEntry src, DeclarationEntry dest) {
exists(ClassTemplateSpecialization cts |
src.getDeclaration() = cts and
dest.getDeclaration() = cts.getPrimaryTemplate()
)
}