зеркало из https://github.com/github/codeql.git
Merge pull request #2153 from matt-gretton-dann/cpp-447-support-non-type-template-parameters
RFC: C++ Support non type template parameter values
This commit is contained in:
Коммит
5b00b21713
|
@ -54,3 +54,8 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
|
|||
lead to regressions (or improvements) in how queries are optimized because
|
||||
optimization in QL relies on static size estimates, and the control-flow edge
|
||||
relations will now have different size estimates than before.
|
||||
* Support has been added for non-type template arguments. This means that the
|
||||
return type of `Declaration::getTemplateArgument()` and
|
||||
`Declaration::getATemplateArgument` have changed to `Locatable`. See the
|
||||
documentation for `Declaration::getTemplateArgument()` and
|
||||
`Declaration::getTemplateArgumentKind()` for details.
|
||||
|
|
|
@ -605,15 +605,6 @@ class Class extends UserType {
|
|||
class_instantiation(underlyingElement(this), unresolveElement(c))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this class from a
|
||||
* class template. When called on a class template, this will return the
|
||||
* `i`th template parameter.
|
||||
*/
|
||||
override Type getTemplateArgument(int i) {
|
||||
class_template_argument(underlyingElement(this), i, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this class/struct is polymorphic (has a virtual function, or
|
||||
* inherits one).
|
||||
|
@ -623,7 +614,7 @@ class Class extends UserType {
|
|||
}
|
||||
|
||||
override predicate involvesTemplateParameter() {
|
||||
getATemplateArgument().involvesTemplateParameter()
|
||||
getATemplateArgument().(Type).involvesTemplateParameter()
|
||||
}
|
||||
|
||||
/** Holds if this class, struct or union was declared 'final'. */
|
||||
|
|
|
@ -193,20 +193,83 @@ abstract class Declaration extends Locatable, @declaration {
|
|||
|
||||
/**
|
||||
* Gets a template argument used to instantiate this declaration from a template.
|
||||
* When called on a template, this will return a template parameter.
|
||||
* When called on a template, this will return a template parameter type for
|
||||
* both typed and non-typed parameters.
|
||||
*/
|
||||
final Type getATemplateArgument() { result = getTemplateArgument(_) }
|
||||
final Locatable getATemplateArgument() { result = getTemplateArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets a template argument used to instantiate this declaration from a template.
|
||||
* When called on a template, this will return a non-typed template
|
||||
* parameter value.
|
||||
*/
|
||||
final Locatable getATemplateArgumentKind() { result = getTemplateArgumentKind(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this declaration from a
|
||||
* template. When called on a template, this will return the `i`th template parameter.
|
||||
* template.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* `template<typename T, T X> class Foo;`
|
||||
*
|
||||
* Will have `getTemplateArgument(0)` return `T`, and
|
||||
* `getTemplateArgument(1)` return `X`.
|
||||
*
|
||||
* `Foo<int, 1> bar;
|
||||
*
|
||||
* Will have `getTemplateArgument())` return `int`, and
|
||||
* `getTemplateArgument(1)` return `1`.
|
||||
*/
|
||||
Type getTemplateArgument(int index) { none() }
|
||||
final Locatable getTemplateArgument(int index) {
|
||||
if exists(getTemplateArgumentValue(index))
|
||||
then result = getTemplateArgumentValue(index)
|
||||
else result = getTemplateArgumentType(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument value used to instantiate this declaration
|
||||
* from a template. When called on a template, this will return the `i`th template
|
||||
* parameter value if it exists.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* `template<typename T, T X> class Foo;`
|
||||
*
|
||||
* Will have `getTemplateArgumentKind(1)` return `T`, and no result for
|
||||
* `getTemplateArgumentKind(0)`.
|
||||
*
|
||||
* `Foo<int, 10> bar;
|
||||
*
|
||||
* Will have `getTemplateArgumentKind(1)` return `int`, and no result for
|
||||
* `getTemplateArgumentKind(0)`.
|
||||
*/
|
||||
final Locatable getTemplateArgumentKind(int index) {
|
||||
if exists(getTemplateArgumentValue(index))
|
||||
then result = getTemplateArgumentType(index)
|
||||
else none()
|
||||
}
|
||||
|
||||
/** Gets the number of template arguments for this declaration. */
|
||||
final int getNumberOfTemplateArguments() {
|
||||
result = count(int i | exists(getTemplateArgument(i)))
|
||||
}
|
||||
|
||||
private Type getTemplateArgumentType(int index) {
|
||||
class_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
function_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
variable_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
|
||||
private Expr getTemplateArgumentValue(int index) {
|
||||
class_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
function_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
variable_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -343,15 +343,6 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
|||
function_instantiation(underlyingElement(this), unresolveElement(f))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this function from a
|
||||
* function template. When called on a function template, this will return the
|
||||
* `i`th template parameter.
|
||||
*/
|
||||
override Type getTemplateArgument(int index) {
|
||||
function_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function is defined in several files. This is illegal in
|
||||
* C (though possible in some C++ compilers), and likely indicates that
|
||||
|
|
|
@ -35,6 +35,14 @@ private string getParameterTypeString(Type parameterType) {
|
|||
else result = parameterType.(DumpType).getTypeIdentityString()
|
||||
}
|
||||
|
||||
private string getTemplateArgumentString(Declaration d, int i) {
|
||||
if exists(d.getTemplateArgumentKind(i))
|
||||
then
|
||||
result = d.getTemplateArgumentKind(i).(DumpType).getTypeIdentityString() + " " +
|
||||
d.getTemplateArgument(i)
|
||||
else result = d.getTemplateArgument(i).(DumpType).getTypeIdentityString()
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Declaration` extended to add methods for generating strings useful only for dumps and debugging.
|
||||
*/
|
||||
|
@ -56,7 +64,7 @@ abstract private class DumpDeclaration extends Declaration {
|
|||
strictconcat(int i |
|
||||
exists(this.getTemplateArgument(i))
|
||||
|
|
||||
this.getTemplateArgument(i).(DumpType).getTypeIdentityString(), ", " order by i
|
||||
getTemplateArgumentString(this, i), ", " order by i
|
||||
) + ">"
|
||||
else result = ""
|
||||
}
|
||||
|
|
|
@ -210,7 +210,7 @@ class Type extends Locatable, @type {
|
|||
// A function call that provides an explicit template argument that refers to T uses T.
|
||||
// We exclude calls within instantiations, since they do not appear directly in the source.
|
||||
exists(FunctionCall c |
|
||||
c.getAnExplicitTemplateArgument().refersTo(this) and
|
||||
c.getAnExplicitTemplateArgument().(Type).refersTo(this) and
|
||||
result = c and
|
||||
not c.getEnclosingFunction().isConstructedFrom(_)
|
||||
)
|
||||
|
|
|
@ -155,15 +155,6 @@ class Variable extends Declaration, @variable {
|
|||
variable_instantiation(underlyingElement(this), unresolveElement(v))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this variable from a
|
||||
* variable template. When called on a variable template, this will return the
|
||||
* `i`th template parameter.
|
||||
*/
|
||||
override Type getTemplateArgument(int index) {
|
||||
variable_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is a compiler-generated variable. For example, a
|
||||
* [range-based for loop](http://en.cppreference.com/w/cpp/language/range-for)
|
||||
|
|
|
@ -139,17 +139,29 @@ class FunctionCall extends Call, @funbindexpr {
|
|||
override string getCanonicalQLClass() { result = "FunctionCall" }
|
||||
|
||||
/** Gets an explicit template argument for this call. */
|
||||
Type getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) }
|
||||
Locatable getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) }
|
||||
|
||||
/** Gets an explicit template argument value for this call. */
|
||||
Locatable getAnExplicitTemplateArgumentKind() { result = getExplicitTemplateArgumentKind(_) }
|
||||
|
||||
/** Gets a template argument for this call. */
|
||||
Type getATemplateArgument() { result = getTarget().getATemplateArgument() }
|
||||
Locatable getATemplateArgument() { result = getTarget().getATemplateArgument() }
|
||||
|
||||
/** Gets a template argument value for this call. */
|
||||
Locatable getATemplateArgumentKind() { result = getTarget().getATemplateArgumentKind() }
|
||||
|
||||
/** Gets the nth explicit template argument for this call. */
|
||||
Type getExplicitTemplateArgument(int n) {
|
||||
Locatable getExplicitTemplateArgument(int n) {
|
||||
n < getNumberOfExplicitTemplateArguments() and
|
||||
result = getTemplateArgument(n)
|
||||
}
|
||||
|
||||
/** Gets the nth explicit template argument value for this call. */
|
||||
Locatable getExplicitTemplateArgumentKind(int n) {
|
||||
n < getNumberOfExplicitTemplateArguments() and
|
||||
result = getTemplateArgumentKind(n)
|
||||
}
|
||||
|
||||
/** Gets the number of explicit template arguments for this call. */
|
||||
int getNumberOfExplicitTemplateArguments() {
|
||||
if numtemplatearguments(underlyingElement(this), _)
|
||||
|
@ -161,7 +173,10 @@ class FunctionCall extends Call, @funbindexpr {
|
|||
int getNumberOfTemplateArguments() { result = count(int i | exists(getTemplateArgument(i))) }
|
||||
|
||||
/** Gets the nth template argument for this call (indexed from 0). */
|
||||
Type getTemplateArgument(int n) { result = getTarget().getTemplateArgument(n) }
|
||||
Locatable getTemplateArgument(int n) { result = getTarget().getTemplateArgument(n) }
|
||||
|
||||
/** Gets the nth template argument value for this call (indexed from 0). */
|
||||
Locatable getTemplateArgumentKind(int n) { result = getTarget().getTemplateArgumentKind(n) }
|
||||
|
||||
/** Holds if any template arguments for this call are implicit / deduced. */
|
||||
predicate hasImplicitTemplateArguments() {
|
||||
|
|
|
@ -731,6 +731,11 @@ class_template_argument(
|
|||
int index: int ref,
|
||||
int arg_type: @type ref
|
||||
);
|
||||
class_template_argument_value(
|
||||
int type_id: @usertype ref,
|
||||
int index: int ref,
|
||||
int arg_value: @expr ref
|
||||
);
|
||||
|
||||
is_proxy_class_for(
|
||||
unique int id: @usertype ref,
|
||||
|
@ -755,6 +760,11 @@ function_template_argument(
|
|||
int index: int ref,
|
||||
int arg_type: @type ref
|
||||
);
|
||||
function_template_argument_value(
|
||||
int function_id: @function ref,
|
||||
int index: int ref,
|
||||
int arg_value: @expr ref
|
||||
);
|
||||
|
||||
is_variable_template(unique int id: @variable ref);
|
||||
variable_instantiation(
|
||||
|
@ -766,6 +776,11 @@ variable_template_argument(
|
|||
int index: int ref,
|
||||
int arg_type: @type ref
|
||||
);
|
||||
variable_template_argument_value(
|
||||
int variable_id: @variable ref,
|
||||
int index: int ref,
|
||||
int arg_value: @expr ref
|
||||
);
|
||||
|
||||
/*
|
||||
Fixed point types
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -67,31 +67,7 @@ bad_asts.cpp:
|
|||
# 5| params:
|
||||
#-----| 0: [Parameter] p#0
|
||||
#-----| Type = [RValueReferenceType] S &&
|
||||
# 9| [MemberFunction] int Bad::S::MemberFunction(int)
|
||||
# 9| params:
|
||||
# 9| 0: [Parameter] y
|
||||
# 9| Type = [IntType] int
|
||||
# 9| body: [Block] { ... }
|
||||
# 10| 0: [ReturnStmt] return ...
|
||||
# 10| 0: [AddExpr] ... + ...
|
||||
# 10| Type = [IntType] int
|
||||
# 10| ValueCategory = prvalue
|
||||
# 10| 0: [AddExpr] ... + ...
|
||||
# 10| Type = [IntType] int
|
||||
# 10| ValueCategory = prvalue
|
||||
# 10| 0: [Literal] Unknown literal
|
||||
# 10| Type = [IntType] int
|
||||
# 10| ValueCategory = prvalue
|
||||
# 10| 1: [PointerFieldAccess] x
|
||||
# 10| Type = [IntType] int
|
||||
# 10| ValueCategory = prvalue(load)
|
||||
#-----| -1: [ThisExpr] this
|
||||
#-----| Type = [PointerType] S *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 10| 1: [VariableAccess] y
|
||||
# 10| Type = [IntType] int
|
||||
# 10| ValueCategory = prvalue(load)
|
||||
# 9| [TopLevelFunction] int MemberFunction(int)
|
||||
# 9| [FunctionTemplateInstantiation,MemberFunction] int Bad::S::MemberFunction<int 6>(int)
|
||||
# 9| params:
|
||||
# 9| 0: [Parameter] y
|
||||
# 9| Type = [IntType] int
|
||||
|
@ -116,6 +92,31 @@ bad_asts.cpp:
|
|||
# 10| 1: [VariableAccess] y
|
||||
# 10| Type = [IntType] int
|
||||
# 10| ValueCategory = prvalue(load)
|
||||
# 9| [MemberFunction,TemplateFunction] int Bad::S::MemberFunction<int t>(int)
|
||||
# 9| params:
|
||||
# 9| 0: [Parameter] y
|
||||
# 9| Type = [IntType] int
|
||||
# 9| body: [Block] { ... }
|
||||
# 10| 0: [ReturnStmt] return ...
|
||||
# 10| 0: [AddExpr] ... + ...
|
||||
# 10| Type = [IntType] int
|
||||
# 10| ValueCategory = prvalue
|
||||
# 10| 0: [AddExpr] ... + ...
|
||||
# 10| Type = [IntType] int
|
||||
# 10| ValueCategory = prvalue
|
||||
# 10| 0: [Literal] t
|
||||
# 10| Type = [IntType] int
|
||||
# 10| Value = [Literal] t
|
||||
# 10| ValueCategory = prvalue
|
||||
# 10| 1: [PointerFieldAccess] x
|
||||
# 10| Type = [IntType] int
|
||||
# 10| ValueCategory = prvalue(load)
|
||||
#-----| -1: [ThisExpr] this
|
||||
#-----| Type = [PointerType] S *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 10| 1: [VariableAccess] y
|
||||
# 10| Type = [IntType] int
|
||||
# 10| ValueCategory = prvalue(load)
|
||||
# 14| [TopLevelFunction] void Bad::CallBadMemberFunction()
|
||||
# 14| params:
|
||||
# 14| body: [Block] { ... }
|
||||
|
|
|
@ -1,4 +1,28 @@
|
|||
bad_asts.cpp:
|
||||
# 9| int Bad::S::MemberFunction<int 6>(int)
|
||||
# 9| Block 0
|
||||
# 9| v0_0(void) = EnterFunction :
|
||||
# 9| mu0_1(unknown) = AliasedDefinition :
|
||||
# 9| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 9| r0_3(glval<S>) = InitializeThis :
|
||||
# 9| r0_4(glval<int>) = VariableAddress[y] :
|
||||
# 9| mu0_5(int) = InitializeParameter[y] : &:r0_4
|
||||
# 10| r0_6(glval<int>) = VariableAddress[#return] :
|
||||
# 10| r0_7(int) = Constant[6] :
|
||||
#-----| r0_8(S *) = CopyValue : r0_3
|
||||
# 10| r0_9(glval<int>) = FieldAddress[x] : r0_8
|
||||
# 10| r0_10(int) = Load : &:r0_9, ~mu0_2
|
||||
# 10| r0_11(int) = Add : r0_7, r0_10
|
||||
# 10| r0_12(glval<int>) = VariableAddress[y] :
|
||||
# 10| r0_13(int) = Load : &:r0_12, ~mu0_2
|
||||
# 10| r0_14(int) = Add : r0_11, r0_13
|
||||
# 10| mu0_15(int) = Store : &:r0_6, r0_14
|
||||
# 9| r0_16(glval<int>) = VariableAddress[#return] :
|
||||
# 9| v0_17(void) = ReturnValue : &:r0_16, ~mu0_2
|
||||
# 9| v0_18(void) = UnmodeledUse : mu*
|
||||
# 9| v0_19(void) = AliasedUse : ~mu0_2
|
||||
# 9| v0_20(void) = ExitFunction :
|
||||
|
||||
# 14| void Bad::CallBadMemberFunction()
|
||||
# 14| Block 0
|
||||
# 14| v0_0(void) = EnterFunction :
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
| file://:0:0:0:0 | __i | file://:0:0:0:0 | unsigned long |
|
||||
| file://:0:0:0:0 | uls | file://:0:0:0:0 | unsigned long |
|
||||
| file://:0:0:0:0 | uls | file://:0:0:0:0 | unsigned long |
|
||||
| file://:0:0:0:0 | uls | file://:0:0:0:0 | unsigned long |
|
||||
| segfault.cpp:25:46:25:65 | call to S | file://:0:0:0:0 | void |
|
||||
| segfault.cpp:25:46:25:65 | call to S | file://:0:0:0:0 | void |
|
||||
| segfault.cpp:25:48:25:55 | __second | segfault.cpp:15:7:15:11 | tuple |
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
| file://:0:0:0:0 | __va_list_tag | <none> |
|
||||
| test.cpp:3:8:3:9 | s1<<expression>> | <none> |
|
||||
| test.cpp:3:8:3:9 | s1<<unnamed>> | <none> |
|
||||
| test.cpp:3:8:3:9 | s1<<expression>> | {...} |
|
||||
| test.cpp:3:8:3:9 | s1<<unnamed>> | (null) |
|
||||
| test.cpp:5:8:5:9 | s2<T> | T |
|
||||
| test.cpp:5:8:5:9 | s2<T> | T |
|
||||
| test.cpp:7:8:7:9 | s3<T, <unnamed>> | (unnamed) |
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
| file://:0:0:0:0 | |
|
||||
| file://:0:0:0:0 | 0 |
|
||||
| file://:0:0:0:0 | (global namespace) |
|
||||
| file://:0:0:0:0 | B |
|
||||
| file://:0:0:0:0 | X |
|
||||
| file://:0:0:0:0 | X |
|
||||
| file://:0:0:0:0 | X |
|
||||
| file://:0:0:0:0 | X |
|
||||
| file://:0:0:0:0 | Y |
|
||||
| file://:0:0:0:0 | __va_list_tag |
|
||||
| file://:0:0:0:0 | __va_list_tag & |
|
||||
| file://:0:0:0:0 | __va_list_tag && |
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
template <int i>
|
||||
class Int { };
|
||||
|
||||
Int<10> i;
|
|
@ -0,0 +1,2 @@
|
|||
| test.cpp:2:7:2:9 | Int<10> | file://:0:0:0:0 | int | test.cpp:4:5:4:6 | 10 |
|
||||
| test.cpp:2:7:2:9 | Int<i> | file://:0:0:0:0 | int | file://:0:0:0:0 | i |
|
|
@ -0,0 +1,4 @@
|
|||
import cpp
|
||||
|
||||
from Class c
|
||||
select c, c.getATemplateArgumentKind(), c.getATemplateArgument()
|
|
@ -0,0 +1,5 @@
|
|||
// semmle-extractor-options: --edg --trap_container=folder --edg --trap-compression=none
|
||||
template <int i>
|
||||
int addToSelf() { return i + i; };
|
||||
|
||||
int bar() { return addToSelf<10>(); }
|
|
@ -0,0 +1,2 @@
|
|||
| test.cpp:3:5:3:5 | addToSelf | file://:0:0:0:0 | int | test.cpp:5:30:5:31 | 10 |
|
||||
| test.cpp:3:5:3:13 | addToSelf | file://:0:0:0:0 | int | file://:0:0:0:0 | i |
|
|
@ -0,0 +1,4 @@
|
|||
import cpp
|
||||
|
||||
from Function f
|
||||
select f, f.getATemplateArgumentKind(), f.getATemplateArgument()
|
|
@ -0,0 +1,19 @@
|
|||
// semmle-extractor-options: --edg --trap_container=folder --edg --trap-compression=none
|
||||
template<int x>
|
||||
struct C { };
|
||||
|
||||
static const int one1 = 1, one2 = 1;
|
||||
C<one1> c = C<one2>();
|
||||
C<one1 + one2> e;
|
||||
|
||||
template<typename T, T X>
|
||||
struct D { };
|
||||
|
||||
D<int, 2> a;
|
||||
D<long, 2> b;
|
||||
|
||||
template<typename T, T* X>
|
||||
struct E { };
|
||||
|
||||
E<int, nullptr> z;
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
| test.cpp:3:8:3:8 | C<1> | 0 | int | test.cpp:5:25:5:25 | 1 |
|
||||
| test.cpp:3:8:3:8 | C<2> | 0 | int | file://:0:0:0:0 | 2 |
|
||||
| test.cpp:3:8:3:8 | C<x> | 0 | int | file://:0:0:0:0 | x |
|
||||
| test.cpp:10:8:10:8 | D<T, X> | 0 | <none> | test.cpp:9:19:9:19 | T |
|
||||
| test.cpp:10:8:10:8 | D<T, X> | 1 | T | file://:0:0:0:0 | X |
|
||||
| test.cpp:10:8:10:8 | D<int, 2> | 0 | <none> | file://:0:0:0:0 | int |
|
||||
| test.cpp:10:8:10:8 | D<int, 2> | 1 | int | test.cpp:12:8:12:8 | 2 |
|
||||
| test.cpp:10:8:10:8 | D<long, 2L> | 0 | <none> | file://:0:0:0:0 | long |
|
||||
| test.cpp:10:8:10:8 | D<long, 2L> | 1 | long | file://:0:0:0:0 | 2 |
|
||||
| test.cpp:16:8:16:8 | E<T, X> | 0 | <none> | test.cpp:15:19:15:19 | T |
|
||||
| test.cpp:16:8:16:8 | E<T, X> | 1 | T * | file://:0:0:0:0 | X |
|
||||
| test.cpp:16:8:16:8 | E<int, (int *)nullptr> | 0 | <none> | file://:0:0:0:0 | int |
|
||||
| test.cpp:16:8:16:8 | E<int, (int *)nullptr> | 1 | int * | file://:0:0:0:0 | 0 |
|
|
@ -0,0 +1,14 @@
|
|||
import cpp
|
||||
|
||||
string maybeGetTemplateArgumentKind(Declaration d, int i) {
|
||||
(
|
||||
if exists(d.getTemplateArgumentKind(i))
|
||||
then result = d.getTemplateArgumentKind(i).toString()
|
||||
else result = "<none>"
|
||||
) and
|
||||
i = [0 .. d.getNumberOfTemplateArguments()]
|
||||
}
|
||||
|
||||
from Declaration d, int i
|
||||
where i >= 0 and i < d.getNumberOfTemplateArguments()
|
||||
select d, i, maybeGetTemplateArgumentKind(d, i), d.getTemplateArgument(i)
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,2 @@
|
|||
description: Add support for value template parameters.
|
||||
compatibility: partial
|
Загрузка…
Ссылка в новой задаче