@@ -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]
410694private 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