mirror of
https://github.com/github/codeql.git
synced 2026-04-26 01:05:15 +02:00
C++: Support a richer language in MaD summaries for selecting member functions.
This commit is contained in:
@@ -367,16 +367,154 @@ private predicate elementSpec(
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
|
||||
}
|
||||
|
||||
private string paramsStringPart(Function c, int i) {
|
||||
i = -1 and result = "(" and exists(c)
|
||||
or
|
||||
exists(int n, string p | c.getParameter(n).getType().toString() = p |
|
||||
i = 2 * n and result = p
|
||||
or
|
||||
i = 2 * n - 1 and result = "," and n != 0
|
||||
/** Gets the fully templated version of `f`. */
|
||||
private Function getFullyTemplatedMemberFunction(Function f) {
|
||||
not f.isFromUninstantiatedTemplate(_) and
|
||||
exists(Class c, Class templateClass, int i |
|
||||
c.isConstructedFrom(templateClass) and
|
||||
f = c.getAMember(i) and
|
||||
result = templateClass.getCanonicalMember(i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type name of the `n`'th parameter of `f` without any template
|
||||
* arguments.
|
||||
*/
|
||||
bindingset[f]
|
||||
pragma[inline_late]
|
||||
string getParameterTypeWithoutTemplateArguments(Function f, int n) {
|
||||
exists(string s, string base, string specifiers |
|
||||
s = f.getParameter(n).getType().getName() and
|
||||
parseAngles(s, base, _, specifiers) and
|
||||
result = base + specifiers
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the `n`'th parameter of `f` by replacing template names
|
||||
* with `func:N` (where `N` is the index of the template).
|
||||
*/
|
||||
private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remaining) {
|
||||
exists(Function templateFunction |
|
||||
templateFunction = getFullyTemplatedMemberFunction(f) and
|
||||
remaining = templateFunction.getNumberOfTemplateArguments() and
|
||||
result = getParameterTypeWithoutTemplateArguments(templateFunction, n)
|
||||
)
|
||||
or
|
||||
i = 2 * c.getNumberOfParameters() and result = ")"
|
||||
exists(string mid, TemplateParameter tp, Function templateFunction |
|
||||
mid = getTypeNameWithoutFunctionTemplates(f, n, remaining + 1) and
|
||||
templateFunction = getFullyTemplatedMemberFunction(f) and
|
||||
tp = templateFunction.getTemplateArgument(remaining) and
|
||||
result = mid.replaceAll(tp.getName(), "func:" + remaining.toString())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the `n`'th parameter of `f` by replacing template names
|
||||
* with `class:N` (where `N` is the index of the template).
|
||||
*/
|
||||
private string getTypeNameWithoutClassTemplates(Function f, int n, int remaining) {
|
||||
exists(Class template |
|
||||
f.getDeclaringType().isConstructedFrom(template) and
|
||||
remaining = template.getNumberOfTemplateArguments() and
|
||||
result = getTypeNameWithoutFunctionTemplates(f, n, 0)
|
||||
)
|
||||
or
|
||||
exists(string mid, TemplateParameter tp, Class template |
|
||||
mid = getTypeNameWithoutClassTemplates(f, n, remaining + 1) and
|
||||
f.getDeclaringType().isConstructedFrom(template) and
|
||||
tp = template.getTemplateArgument(remaining) and
|
||||
result = mid.replaceAll(tp.getName(), "class:" + remaining.toString())
|
||||
)
|
||||
}
|
||||
|
||||
private string getParameterTypeName(Function c, int i) {
|
||||
result = getTypeNameWithoutClassTemplates(c, i, 0)
|
||||
}
|
||||
|
||||
/** Splits `s` by `,` and gets the `i`'th element. */
|
||||
bindingset[s]
|
||||
pragma[inline_late]
|
||||
private string getAtIndex(string s, int i) {
|
||||
result = s.splitAt(",", i) and
|
||||
// when `s` is `""` and `i` is `0` we get `result = ""` which we don't want.
|
||||
not (s = "" and i = 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes `partiallyNormalizedSignature` by replacing the `remaining`
|
||||
* number of template arguments in `partiallyNormalizedSignature` with their
|
||||
* index in `typeArgs`.
|
||||
*/
|
||||
private string getSignatureWithoutClassTemplateNames(
|
||||
string partiallyNormalizedSignature, string typeArgs, string nameArgs, int remaining
|
||||
) {
|
||||
elementSpecWithArguments0(_, _, _, partiallyNormalizedSignature, typeArgs, nameArgs) and
|
||||
remaining = count(partiallyNormalizedSignature.indexOf(",")) + 1 and
|
||||
result = partiallyNormalizedSignature
|
||||
or
|
||||
exists(string mid |
|
||||
mid =
|
||||
getSignatureWithoutClassTemplateNames(partiallyNormalizedSignature, typeArgs, nameArgs,
|
||||
remaining + 1)
|
||||
|
|
||||
exists(string typeArg |
|
||||
typeArg = getAtIndex(typeArgs, remaining) and
|
||||
result = mid.replaceAll(typeArg, "class:" + remaining.toString())
|
||||
)
|
||||
or
|
||||
// Make sure `remaining` is properly bound
|
||||
remaining = [0 .. count(partiallyNormalizedSignature.indexOf(",")) + 1] and
|
||||
not exists(getAtIndex(typeArgs, remaining)) and
|
||||
result = mid
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes `partiallyNormalizedSignature` by replacing:
|
||||
* - _All_ the template arguments in `partiallyNormalizedSignature` that refer to
|
||||
* template parameters in `typeArgs` with their index in `typeArgs`, and
|
||||
* - The `remaining` number of template arguments in `partiallyNormalizedSignature`
|
||||
* with their index in `nameArgs`.
|
||||
*/
|
||||
private string getSignatureWithoutFunctionTemplateNames(
|
||||
string partiallyNormalizedSignature, string typeArgs, string nameArgs, int remaining
|
||||
) {
|
||||
remaining = count(partiallyNormalizedSignature.indexOf(",")) + 1 and
|
||||
result =
|
||||
getSignatureWithoutClassTemplateNames(partiallyNormalizedSignature, typeArgs, nameArgs, 0)
|
||||
or
|
||||
exists(string mid |
|
||||
mid =
|
||||
getSignatureWithoutFunctionTemplateNames(partiallyNormalizedSignature, typeArgs, nameArgs,
|
||||
remaining + 1)
|
||||
|
|
||||
exists(string nameArg |
|
||||
nameArg = getAtIndex(nameArgs, remaining) and
|
||||
result = mid.replaceAll(nameArg, "func:" + remaining.toString())
|
||||
)
|
||||
or
|
||||
// Make sure `remaining` is properly bound
|
||||
remaining = [0 .. count(partiallyNormalizedSignature.indexOf(",")) + 1] and
|
||||
not exists(getAtIndex(nameArgs, remaining)) and
|
||||
result = mid
|
||||
)
|
||||
}
|
||||
|
||||
private string paramsStringPart(Function c, int i) {
|
||||
not c.isFromUninstantiatedTemplate(_) and
|
||||
(
|
||||
i = -1 and result = "(" and exists(c)
|
||||
or
|
||||
exists(int n, string p | getParameterTypeName(c, n) = p |
|
||||
i = 2 * n and result = p
|
||||
or
|
||||
i = 2 * n - 1 and result = "," and n != 0
|
||||
)
|
||||
or
|
||||
i = 2 * c.getNumberOfParameters() and result = ")"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,6 +534,152 @@ private predicate matchesSignature(Function func, string signature) {
|
||||
paramsString(func) = signature
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `elementSpec(_, type, _, name, signature, _)` holds and
|
||||
* - `typeArgs` represents the named template parameters supplied to `type`, and
|
||||
* - `nameArgs` represents the named template parameters supplied to `name`, and
|
||||
* - `normalizedSignature` is `signature`, except with
|
||||
* - template parameter names replaced by `func:i` if the template name is
|
||||
* the `i`'th entry in `nameArgs`, and
|
||||
* - template parameter names replaced by `class:i` if the template name is
|
||||
* the `i`'th entry in `typeArgs`.
|
||||
*
|
||||
* In other words, the string `normalizedSignature` represents a "normalized"
|
||||
* signature with no mention of any free template parameters.
|
||||
*
|
||||
* For example, consider a summary row such as:
|
||||
* ```
|
||||
* elementSpec(_, "MyClass<B, C>", _, myFunc<A>, "(const A &,int,C,B *)", _)
|
||||
* ```
|
||||
* In this case, `normalizedSignature` will be `"(const func:0 &,int,class:1,class:0 *)"`.
|
||||
*/
|
||||
private predicate elementSpecWithArguments(
|
||||
string signature, string type, string name, string normalizedSignature, string typeArgs,
|
||||
string nameArgs
|
||||
) {
|
||||
exists(string signatureWithoutParens |
|
||||
elementSpecWithArguments0(signature, type, name, signatureWithoutParens, typeArgs, nameArgs) and
|
||||
normalizedSignature =
|
||||
getSignatureWithoutFunctionTemplateNames(signatureWithoutParens, typeArgs, nameArgs, 0)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the `n`'th normalized signature parameter for the function `name` in class `type`. */
|
||||
private string getSignatureParameterName(string signature, string type, string name, int n) {
|
||||
exists(string normalizedSignature |
|
||||
elementSpecWithArguments(signature, type, name, normalizedSignature, _, _) and
|
||||
result = getAtIndex(normalizedSignature, n)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th name in `signature` matches the `i` name in `paramsString(func)`.
|
||||
*
|
||||
* When `paramsString(func)[i]` is `class:n` then the signature name is
|
||||
* compared with the `n`'th name in `type`, and when `paramsString(func)[i]`
|
||||
* is `func:n` then the signature name is compared with the `n`'th name
|
||||
* in `name`.
|
||||
*/
|
||||
private predicate signatureMatches(Function func, string signature, string type, string name, int i) {
|
||||
exists(string s |
|
||||
s = getSignatureParameterName(signature, type, name, i) and
|
||||
s = getParameterTypeName(func, i)
|
||||
) and
|
||||
if exists(getParameterTypeName(func, i + 1))
|
||||
then signatureMatches(func, signature, type, name, i + 1)
|
||||
else i = count(signature.indexOf(","))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `s` can be broken into a string of the form
|
||||
* `beforeAngles<betweenAngles>`,
|
||||
* or `s = beforeAngles` where `beforeAngles` does not have any brackets.
|
||||
*/
|
||||
bindingset[s]
|
||||
pragma[inline_late]
|
||||
private predicate parseAngles(
|
||||
string s, string beforeAngles, string betweenAngles, string afterAngles
|
||||
) {
|
||||
beforeAngles = s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 1) and
|
||||
(
|
||||
betweenAngles = s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 2) and
|
||||
afterAngles = s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 3)
|
||||
or
|
||||
not exists(s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 2)) and
|
||||
betweenAngles = "" and
|
||||
afterAngles = ""
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `s` can be broken into a string of the form `(betweenParens)`. */
|
||||
bindingset[s]
|
||||
pragma[inline_late]
|
||||
private predicate parseParens(string s, string betweenParens) {
|
||||
betweenParens = s.regexpCapture("\\(([^\\)]+)\\)", 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `elementSpec(_, type, _, name, signature, _)` and:
|
||||
* - `type` introduces template parameters `typeArgs`, and
|
||||
* - `name` introduces template parameters `nameArgs`, and
|
||||
* - `signatureWithoutParens` equals `signature`, but with the surrounding
|
||||
* parentheses removed.
|
||||
*/
|
||||
private predicate elementSpecWithArguments0(
|
||||
string signature, string type, string name, string signatureWithoutParens, string typeArgs,
|
||||
string nameArgs
|
||||
) {
|
||||
elementSpec(_, type, _, name, signature, _) and
|
||||
parseAngles(name, _, nameArgs, "") and
|
||||
(
|
||||
type = "" and typeArgs = ""
|
||||
or
|
||||
parseAngles(type, _, typeArgs, "")
|
||||
) and
|
||||
parseParens(signature, signatureWithoutParens)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `elementSpec(namespace, type, subtypes, name, signature, _)` and
|
||||
* `method`'s signature matches `signature`.
|
||||
*
|
||||
* `signature` may contain template parameter names that are bound by `type` and `name`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate elementSpecMatchesSignature(
|
||||
Function method, string namespace, string type, boolean subtypes, string name, string signature
|
||||
) {
|
||||
elementSpec(namespace, pragma[only_bind_into](type), subtypes, pragma[only_bind_into](name),
|
||||
pragma[only_bind_into](signature), _) and
|
||||
signatureMatches(method, signature, type, name, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `classWithMethod` has `method` named `name` (excluding any
|
||||
* template parameters).
|
||||
*/
|
||||
bindingset[name]
|
||||
pragma[inline_late]
|
||||
private predicate hasClassAndName(Class classWithMethod, Function method, string name) {
|
||||
exists(string nameWithoutArgs |
|
||||
parseAngles(name, nameWithoutArgs, _, "") and
|
||||
classWithMethod = method.getClassAndName(nameWithoutArgs)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nameClass` is in namespace `namespace` and has
|
||||
* name `type` (excluding any template parameters).
|
||||
*/
|
||||
bindingset[type, namespace]
|
||||
pragma[inline_late]
|
||||
private predicate hasQualifiedName(Class namedClass, string namespace, string type) {
|
||||
exists(string typeWithoutArgs |
|
||||
parseAngles(type, typeWithoutArgs, _, "") and
|
||||
namedClass.hasQualifiedName(namespace, typeWithoutArgs)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the element in module `namespace` that satisfies the following properties:
|
||||
* 1. If the element is a member of a class-like type, then the class-like type has name `type`
|
||||
@@ -410,8 +694,8 @@ pragma[nomagic]
|
||||
private Element interpretElement0(
|
||||
string namespace, string type, boolean subtypes, string name, string signature
|
||||
) {
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
(
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
// Non-member functions
|
||||
exists(Function func |
|
||||
func.hasQualifiedName(namespace, name) and
|
||||
@@ -423,21 +707,28 @@ private Element interpretElement0(
|
||||
)
|
||||
or
|
||||
// Member functions
|
||||
exists(Class namedClass, Class classWithMethod, Function method |
|
||||
classWithMethod = method.getClassAndName(name) and
|
||||
namedClass.hasQualifiedName(namespace, type) and
|
||||
matchesSignature(method, signature) and
|
||||
result = method
|
||||
|
|
||||
// member declared in the named type or a subtype of it
|
||||
subtypes = true and
|
||||
classWithMethod = namedClass.getADerivedClass*()
|
||||
or
|
||||
// member declared directly in the named type
|
||||
subtypes = false and
|
||||
classWithMethod = namedClass
|
||||
exists(Class namedClass, Class classWithMethod |
|
||||
(
|
||||
elementSpecMatchesSignature(result, namespace, type, subtypes, name, signature) and
|
||||
hasClassAndName(classWithMethod, result, name)
|
||||
or
|
||||
signature = "" and
|
||||
elementSpec(namespace, type, subtypes, name, "", _) and
|
||||
hasClassAndName(classWithMethod, result, name)
|
||||
) and
|
||||
hasQualifiedName(namedClass, namespace, type) and
|
||||
(
|
||||
// member declared in the named type or a subtype of it
|
||||
subtypes = true and
|
||||
classWithMethod = namedClass.getADerivedClass*()
|
||||
or
|
||||
// member declared directly in the named type
|
||||
subtypes = false and
|
||||
classWithMethod = namedClass
|
||||
)
|
||||
)
|
||||
or
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
// Member variables
|
||||
signature = "" and
|
||||
exists(Class namedClass, Class classWithMember, MemberVariable member |
|
||||
@@ -456,6 +747,7 @@ private Element interpretElement0(
|
||||
)
|
||||
or
|
||||
// Global or namespace variables
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
signature = "" and
|
||||
type = "" and
|
||||
subtypes = false and
|
||||
|
||||
Reference in New Issue
Block a user