Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 716df0c

Browse files
committed
C++: Support a richer language in MaD summaries for selecting member functions.
1 parent 901fac4 commit 716df0c

1 file changed

Lines changed: 312 additions & 20 deletions

File tree

cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll

Lines changed: 312 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -367,16 +367,154 @@ private predicate elementSpec(
367367
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
368368
}
369369

370-
private string paramsStringPart(Function c, int i) {
371-
i = -1 and result = "(" and exists(c)
370+
/** Gets the fully templated version of `f`. */
371+
private Function getFullyTemplatedMemberFunction(Function f) {
372+
not f.isFromUninstantiatedTemplate(_) and
373+
exists(Class c, Class templateClass, int i |
374+
c.isConstructedFrom(templateClass) and
375+
f = c.getAMember(i) and
376+
result = templateClass.getCanonicalMember(i)
377+
)
378+
}
379+
380+
/**
381+
* Gets the type name of the `n`'th parameter of `f` without any template
382+
* arguments.
383+
*/
384+
bindingset[f]
385+
pragma[inline_late]
386+
string getParameterTypeWithoutTemplateArguments(Function f, int n) {
387+
exists(string s, string base, string specifiers |
388+
s = f.getParameter(n).getType().getName() and
389+
parseAngles(s, base, _, specifiers) and
390+
result = base + specifiers
391+
)
392+
}
393+
394+
/**
395+
* Normalize the `n`'th parameter of `f` by replacing template names
396+
* with `func:N` (where `N` is the index of the template).
397+
*/
398+
private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remaining) {
399+
exists(Function templateFunction |
400+
templateFunction = getFullyTemplatedMemberFunction(f) and
401+
remaining = templateFunction.getNumberOfTemplateArguments() and
402+
result = getParameterTypeWithoutTemplateArguments(templateFunction, n)
403+
)
372404
or
373-
exists(int n, string p | c.getParameter(n).getType().toString() = p |
374-
i = 2 * n and result = p
405+
exists(string mid, TemplateParameter tp, Function templateFunction |
406+
mid = getTypeNameWithoutFunctionTemplates(f, n, remaining + 1) and
407+
templateFunction = getFullyTemplatedMemberFunction(f) and
408+
tp = templateFunction.getTemplateArgument(remaining) and
409+
result = mid.replaceAll(tp.getName(), "func:" + remaining.toString())
410+
)
411+
}
412+
413+
/**
414+
* Normalize the `n`'th parameter of `f` by replacing template names
415+
* with `class:N` (where `N` is the index of the template).
416+
*/
417+
private string getTypeNameWithoutClassTemplates(Function f, int n, int remaining) {
418+
exists(Class template |
419+
f.getDeclaringType().isConstructedFrom(template) and
420+
remaining = template.getNumberOfTemplateArguments() and
421+
result = getTypeNameWithoutFunctionTemplates(f, n, 0)
422+
)
423+
or
424+
exists(string mid, TemplateParameter tp, Class template |
425+
mid = getTypeNameWithoutClassTemplates(f, n, remaining + 1) and
426+
f.getDeclaringType().isConstructedFrom(template) and
427+
tp = template.getTemplateArgument(remaining) and
428+
result = mid.replaceAll(tp.getName(), "class:" + remaining.toString())
429+
)
430+
}
431+
432+
private string getParameterTypeName(Function c, int i) {
433+
result = getTypeNameWithoutClassTemplates(c, i, 0)
434+
}
435+
436+
/** Splits `s` by `,` and gets the `i`'th element. */
437+
bindingset[s]
438+
pragma[inline_late]
439+
private string getAtIndex(string s, int i) {
440+
result = s.splitAt(",", i) and
441+
// when `s` is `""` and `i` is `0` we get `result = ""` which we don't want.
442+
not (s = "" and i = 0)
443+
}
444+
445+
/**
446+
* Normalizes `partiallyNormalizedSignature` by replacing the `remaining`
447+
* number of template arguments in `partiallyNormalizedSignature` with their
448+
* index in `typeArgs`.
449+
*/
450+
private string getSignatureWithoutClassTemplateNames(
451+
string partiallyNormalizedSignature, string typeArgs, string nameArgs, int remaining
452+
) {
453+
elementSpecWithArguments0(_, _, _, partiallyNormalizedSignature, typeArgs, nameArgs) and
454+
remaining = count(partiallyNormalizedSignature.indexOf(",")) + 1 and
455+
result = partiallyNormalizedSignature
456+
or
457+
exists(string mid |
458+
mid =
459+
getSignatureWithoutClassTemplateNames(partiallyNormalizedSignature, typeArgs, nameArgs,
460+
remaining + 1)
461+
|
462+
exists(string typeArg |
463+
typeArg = getAtIndex(typeArgs, remaining) and
464+
result = mid.replaceAll(typeArg, "class:" + remaining.toString())
465+
)
375466
or
376-
i = 2 * n - 1 and result = "," and n != 0
467+
// Make sure `remaining` is properly bound
468+
remaining = [0 .. count(partiallyNormalizedSignature.indexOf(",")) + 1] and
469+
not exists(getAtIndex(typeArgs, remaining)) and
470+
result = mid
377471
)
472+
}
473+
474+
/**
475+
* Normalizes `partiallyNormalizedSignature` by replacing:
476+
* - _All_ the template arguments in `partiallyNormalizedSignature` that refer to
477+
* template parameters in `typeArgs` with their index in `typeArgs`, and
478+
* - The `remaining` number of template arguments in `partiallyNormalizedSignature`
479+
* with their index in `nameArgs`.
480+
*/
481+
private string getSignatureWithoutFunctionTemplateNames(
482+
string partiallyNormalizedSignature, string typeArgs, string nameArgs, int remaining
483+
) {
484+
remaining = count(partiallyNormalizedSignature.indexOf(",")) + 1 and
485+
result =
486+
getSignatureWithoutClassTemplateNames(partiallyNormalizedSignature, typeArgs, nameArgs, 0)
378487
or
379-
i = 2 * c.getNumberOfParameters() and result = ")"
488+
exists(string mid |
489+
mid =
490+
getSignatureWithoutFunctionTemplateNames(partiallyNormalizedSignature, typeArgs, nameArgs,
491+
remaining + 1)
492+
|
493+
exists(string nameArg |
494+
nameArg = getAtIndex(nameArgs, remaining) and
495+
result = mid.replaceAll(nameArg, "func:" + remaining.toString())
496+
)
497+
or
498+
// Make sure `remaining` is properly bound
499+
remaining = [0 .. count(partiallyNormalizedSignature.indexOf(",")) + 1] and
500+
not exists(getAtIndex(nameArgs, remaining)) and
501+
result = mid
502+
)
503+
}
504+
505+
private string paramsStringPart(Function c, int i) {
506+
not c.isFromUninstantiatedTemplate(_) and
507+
(
508+
i = -1 and result = "(" and exists(c)
509+
or
510+
exists(int n, string p | getParameterTypeName(c, n) = p |
511+
i = 2 * n and result = p
512+
or
513+
i = 2 * n - 1 and result = "," and n != 0
514+
)
515+
or
516+
i = 2 * c.getNumberOfParameters() and result = ")"
517+
)
380518
}
381519

382520
/**
@@ -396,6 +534,152 @@ private predicate matchesSignature(Function func, string signature) {
396534
paramsString(func) = signature
397535
}
398536

537+
/**
538+
* Holds if `elementSpec(_, type, _, name, signature, _)` holds and
539+
* - `typeArgs` represents the named template parameters supplied to `type`, and
540+
* - `nameArgs` represents the named template parameters supplied to `name`, and
541+
* - `normalizedSignature` is `signature`, except with
542+
* - template parameter names replaced by `func:i` if the template name is
543+
* the `i`'th entry in `nameArgs`, and
544+
* - template parameter names replaced by `class:i` if the template name is
545+
* the `i`'th entry in `typeArgs`.
546+
*
547+
* In other words, the string `normalizedSignature` represents a "normalized"
548+
* signature with no mention of any free template parameters.
549+
*
550+
* For example, consider a summary row such as:
551+
* ```
552+
* elementSpec(_, "MyClass<B, C>", _, myFunc<A>, "(const A &,int,C,B *)", _)
553+
* ```
554+
* In this case, `normalizedSignature` will be `"(const func:0 &,int,class:1,class:0 *)"`.
555+
*/
556+
private predicate elementSpecWithArguments(
557+
string signature, string type, string name, string normalizedSignature, string typeArgs,
558+
string nameArgs
559+
) {
560+
exists(string signatureWithoutParens |
561+
elementSpecWithArguments0(signature, type, name, signatureWithoutParens, typeArgs, nameArgs) and
562+
normalizedSignature =
563+
getSignatureWithoutFunctionTemplateNames(signatureWithoutParens, typeArgs, nameArgs, 0)
564+
)
565+
}
566+
567+
/** Gets the `n`'th normalized signature parameter for the function `name` in class `type`. */
568+
private string getSignatureParameterName(string signature, string type, string name, int n) {
569+
exists(string normalizedSignature |
570+
elementSpecWithArguments(signature, type, name, normalizedSignature, _, _) and
571+
result = getAtIndex(normalizedSignature, n)
572+
)
573+
}
574+
575+
/**
576+
* Holds if the `i`'th name in `signature` matches the `i` name in `paramsString(func)`.
577+
*
578+
* When `paramsString(func)[i]` is `class:n` then the signature name is
579+
* compared with the `n`'th name in `type`, and when `paramsString(func)[i]`
580+
* is `func:n` then the signature name is compared with the `n`'th name
581+
* in `name`.
582+
*/
583+
private predicate signatureMatches(Function func, string signature, string type, string name, int i) {
584+
exists(string s |
585+
s = getSignatureParameterName(signature, type, name, i) and
586+
s = getParameterTypeName(func, i)
587+
) and
588+
if exists(getParameterTypeName(func, i + 1))
589+
then signatureMatches(func, signature, type, name, i + 1)
590+
else i = count(signature.indexOf(","))
591+
}
592+
593+
/**
594+
* Holds if `s` can be broken into a string of the form
595+
* `beforeAngles<betweenAngles>`,
596+
* or `s = beforeAngles` where `beforeAngles` does not have any brackets.
597+
*/
598+
bindingset[s]
599+
pragma[inline_late]
600+
private predicate parseAngles(
601+
string s, string beforeAngles, string betweenAngles, string afterAngles
602+
) {
603+
beforeAngles = s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 1) and
604+
(
605+
betweenAngles = s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 2) and
606+
afterAngles = s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 3)
607+
or
608+
not exists(s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 2)) and
609+
betweenAngles = "" and
610+
afterAngles = ""
611+
)
612+
}
613+
614+
/** Holds if `s` can be broken into a string of the form `(betweenParens)`. */
615+
bindingset[s]
616+
pragma[inline_late]
617+
private predicate parseParens(string s, string betweenParens) {
618+
betweenParens = s.regexpCapture("\\(([^\\)]+)\\)", 1)
619+
}
620+
621+
/**
622+
* Holds if `elementSpec(_, type, _, name, signature, _)` and:
623+
* - `type` introduces template parameters `typeArgs`, and
624+
* - `name` introduces template parameters `nameArgs`, and
625+
* - `signatureWithoutParens` equals `signature`, but with the surrounding
626+
* parentheses removed.
627+
*/
628+
private predicate elementSpecWithArguments0(
629+
string signature, string type, string name, string signatureWithoutParens, string typeArgs,
630+
string nameArgs
631+
) {
632+
elementSpec(_, type, _, name, signature, _) and
633+
parseAngles(name, _, nameArgs, "") and
634+
(
635+
type = "" and typeArgs = ""
636+
or
637+
parseAngles(type, _, typeArgs, "")
638+
) and
639+
parseParens(signature, signatureWithoutParens)
640+
}
641+
642+
/**
643+
* Holds if `elementSpec(namespace, type, subtypes, name, signature, _)` and
644+
* `method`'s signature matches `signature`.
645+
*
646+
* `signature` may contain template parameter names that are bound by `type` and `name`.
647+
*/
648+
pragma[nomagic]
649+
private predicate elementSpecMatchesSignature(
650+
Function method, string namespace, string type, boolean subtypes, string name, string signature
651+
) {
652+
elementSpec(namespace, pragma[only_bind_into](type), subtypes, pragma[only_bind_into](name),
653+
pragma[only_bind_into](signature), _) and
654+
signatureMatches(method, signature, type, name, 0)
655+
}
656+
657+
/**
658+
* Holds if `classWithMethod` has `method` named `name` (excluding any
659+
* template parameters).
660+
*/
661+
bindingset[name]
662+
pragma[inline_late]
663+
private predicate hasClassAndName(Class classWithMethod, Function method, string name) {
664+
exists(string nameWithoutArgs |
665+
parseAngles(name, nameWithoutArgs, _, "") and
666+
classWithMethod = method.getClassAndName(nameWithoutArgs)
667+
)
668+
}
669+
670+
/**
671+
* Holds if `nameClass` is in namespace `namespace` and has
672+
* name `type` (excluding any template parameters).
673+
*/
674+
bindingset[type, namespace]
675+
pragma[inline_late]
676+
private predicate hasQualifiedName(Class namedClass, string namespace, string type) {
677+
exists(string typeWithoutArgs |
678+
parseAngles(type, typeWithoutArgs, _, "") and
679+
namedClass.hasQualifiedName(namespace, typeWithoutArgs)
680+
)
681+
}
682+
399683
/**
400684
* Gets the element in module `namespace` that satisfies the following properties:
401685
* 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]
410694
private Element interpretElement0(
411695
string namespace, string type, boolean subtypes, string name, string signature
412696
) {
413-
elementSpec(namespace, type, subtypes, name, signature, _) and
414697
(
698+
elementSpec(namespace, type, subtypes, name, signature, _) and
415699
// Non-member functions
416700
exists(Function func |
417701
func.hasQualifiedName(namespace, name) and
@@ -423,21 +707,28 @@ private Element interpretElement0(
423707
)
424708
or
425709
// Member functions
426-
exists(Class namedClass, Class classWithMethod, Function method |
427-
classWithMethod = method.getClassAndName(name) and
428-
namedClass.hasQualifiedName(namespace, type) and
429-
matchesSignature(method, signature) and
430-
result = method
431-
|
432-
// member declared in the named type or a subtype of it
433-
subtypes = true and
434-
classWithMethod = namedClass.getADerivedClass*()
435-
or
436-
// member declared directly in the named type
437-
subtypes = false and
438-
classWithMethod = namedClass
710+
exists(Class namedClass, Class classWithMethod |
711+
(
712+
elementSpecMatchesSignature(result, namespace, type, subtypes, name, signature) and
713+
hasClassAndName(classWithMethod, result, name)
714+
or
715+
signature = "" and
716+
elementSpec(namespace, type, subtypes, name, "", _) and
717+
hasClassAndName(classWithMethod, result, name)
718+
) and
719+
hasQualifiedName(namedClass, namespace, type) and
720+
(
721+
// member declared in the named type or a subtype of it
722+
subtypes = true and
723+
classWithMethod = namedClass.getADerivedClass*()
724+
or
725+
// member declared directly in the named type
726+
subtypes = false and
727+
classWithMethod = namedClass
728+
)
439729
)
440730
or
731+
elementSpec(namespace, type, subtypes, name, signature, _) and
441732
// Member variables
442733
signature = "" and
443734
exists(Class namedClass, Class classWithMember, MemberVariable member |
@@ -456,6 +747,7 @@ private Element interpretElement0(
456747
)
457748
or
458749
// Global or namespace variables
750+
elementSpec(namespace, type, subtypes, name, signature, _) and
459751
signature = "" and
460752
type = "" and
461753
subtypes = false and

0 commit comments

Comments
 (0)