Skip to content

Commit d869b00

Browse files
authored
Merge pull request #20899 from MathiasVP/ignore-non-type-template-params
C++: Ignore non-type template parameters when matching signatures in MaD
2 parents adc13e3 + 295dc69 commit d869b00

File tree

7 files changed

+134
-25
lines changed

7 files changed

+134
-25
lines changed

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

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@
1515
* reading.
1616
* 1. The `namespace` column selects a namespace.
1717
* 2. The `type` column selects a type within that namespace. This column can
18-
* introduce template names that can be mentioned in the `signature` column.
18+
* introduce template type names that can be mentioned in the `signature` column.
1919
* For example, `vector<T,Allocator>` introduces the template names `T` and
20-
* `Allocator`.
20+
* `Allocator`. Non-type template parameters cannot be specified.
2121
* 3. The `subtypes` is a boolean that indicates whether to jump to an
2222
* arbitrary subtype of that type. Set this to `false` if leaving the `type`
2323
* blank (for example, a free function).
2424
* 4. The `name` column optionally selects a specific named member of the type.
25-
* Like the `type` column, this column can introduce template names that can
26-
* be mentioned in the `signature` column. For example, `insert<InputIt>`
27-
* introduces the template name `InputIt`.
25+
* Like the `type` column, this column can introduce template type names
26+
* that can be mentioned in the `signature` column. For example,
27+
* `insert<InputIt>` introduces the template name `InputIt`. Non-type
28+
* template parameters cannot be specified.
2829
* 5. The `signature` column optionally restricts the named member. If
2930
* `signature` is blank then no such filtering is done. The format of the
3031
* signature is a comma-separated list of types enclosed in parentheses. The
@@ -633,25 +634,70 @@ string getParameterTypeWithoutTemplateArguments(Function f, int n, boolean canon
633634
canonical = true
634635
}
635636

637+
/**
638+
* Gets the largest index of a template parameter of `templateFunction` that
639+
* is a type template parameter.
640+
*/
641+
private int getLastTypeTemplateFunctionParameterIndex(Function templateFunction) {
642+
result =
643+
max(int index | templateFunction.getTemplateArgument(index) instanceof TypeTemplateParameter)
644+
}
645+
646+
/** Gets the number of supported template parameters for `templateFunction`. */
647+
private int getNumberOfSupportedFunctionTemplateArguments(Function templateFunction) {
648+
result = count(int i | exists(getSupportedFunctionTemplateArgument(templateFunction, i)) | i)
649+
}
650+
651+
/** Gets the `i`'th supported template parameter for `templateFunction`. */
652+
private Locatable getSupportedFunctionTemplateArgument(Function templateFunction, int i) {
653+
result = templateFunction.getTemplateArgument(i) and
654+
// We don't yet support non-type template parameters in the middle of a
655+
// template parameter list
656+
i <= getLastTypeTemplateFunctionParameterIndex(templateFunction)
657+
}
658+
636659
/**
637660
* Normalize the `n`'th parameter of `f` by replacing template names
638661
* with `func:N` (where `N` is the index of the template).
639662
*/
640663
private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remaining) {
641664
exists(Function templateFunction |
642665
templateFunction = getFullyTemplatedFunction(f) and
643-
remaining = templateFunction.getNumberOfTemplateArguments() and
666+
remaining = getNumberOfSupportedFunctionTemplateArguments(templateFunction) and
644667
result = getParameterTypeWithoutTemplateArguments(templateFunction, n, _)
645668
)
646669
or
647670
exists(string mid, TypeTemplateParameter tp, Function templateFunction |
648671
mid = getTypeNameWithoutFunctionTemplates(f, n, remaining + 1) and
649672
templateFunction = getFullyTemplatedFunction(f) and
650-
tp = templateFunction.getTemplateArgument(remaining) and
673+
tp = getSupportedFunctionTemplateArgument(templateFunction, remaining)
674+
|
651675
result = mid.replaceAll(tp.getName(), "func:" + remaining.toString())
652676
)
653677
}
654678

679+
/**
680+
* Gets the largest index of a template parameter of `templateClass` that
681+
* is a type template parameter.
682+
*/
683+
private int getLastTypeTemplateClassParameterIndex(Class templateClass) {
684+
result =
685+
max(int index | templateClass.getTemplateArgument(index) instanceof TypeTemplateParameter)
686+
}
687+
688+
/** Gets the `i`'th supported template parameter for `templateClass`. */
689+
private Locatable getSupportedClassTemplateArgument(Class templateClass, int i) {
690+
result = templateClass.getTemplateArgument(i) and
691+
// We don't yet support non-type template parameters in the middle of a
692+
// template parameter list
693+
i <= getLastTypeTemplateClassParameterIndex(templateClass)
694+
}
695+
696+
/** Gets the number of supported template parameters for `templateClass`. */
697+
private int getNumberOfSupportedClassTemplateArguments(Class templateClass) {
698+
result = count(int i | exists(getSupportedClassTemplateArgument(templateClass, i)) | i)
699+
}
700+
655701
/**
656702
* Normalize the `n`'th parameter of `f` by replacing template names
657703
* with `class:N` (where `N` is the index of the template).
@@ -661,7 +707,7 @@ private string getTypeNameWithoutClassTemplates(Function f, int n, int remaining
661707
// If there is a declaring type then we start by expanding the function templates
662708
exists(Class template |
663709
isClassConstructedFrom(f.getDeclaringType(), template) and
664-
remaining = template.getNumberOfTemplateArguments() and
710+
remaining = getNumberOfSupportedClassTemplateArguments(template) and
665711
result = getTypeNameWithoutFunctionTemplates(f, n, 0)
666712
)
667713
or
@@ -673,7 +719,8 @@ private string getTypeNameWithoutClassTemplates(Function f, int n, int remaining
673719
exists(string mid, TypeTemplateParameter tp, Class template |
674720
mid = getTypeNameWithoutClassTemplates(f, n, remaining + 1) and
675721
isClassConstructedFrom(f.getDeclaringType(), template) and
676-
tp = template.getTemplateArgument(remaining) and
722+
tp = getSupportedClassTemplateArgument(template, remaining)
723+
|
677724
result = mid.replaceAll(tp.getName(), "class:" + remaining.toString())
678725
)
679726
}

cpp/ql/test/library-tests/dataflow/external-models/flow.expected

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,14 @@ models
3030
| 29 | Summary: ; ; false; RtlMoveMemory; ; ; Argument[*@1]; Argument[*@0]; value; manual |
3131
| 30 | Summary: ; ; false; RtlMoveVolatileMemory; ; ; Argument[*@1]; Argument[*@0]; value; manual |
3232
| 31 | Summary: ; ; false; callWithArgument; ; ; Argument[1]; Argument[0].Parameter[0]; value; manual |
33-
| 32 | Summary: ; ; false; pthread_create; ; ; Argument[@3]; Argument[2].Parameter[@0]; value; manual |
34-
| 33 | Summary: ; ; false; ymlStepGenerated; ; ; Argument[0]; ReturnValue; taint; df-generated |
35-
| 34 | Summary: ; ; false; ymlStepManual; ; ; Argument[0]; ReturnValue; taint; manual |
36-
| 35 | Summary: ; ; false; ymlStepManual_with_body; ; ; Argument[0]; ReturnValue; taint; manual |
37-
| 36 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual |
33+
| 32 | Summary: ; ; false; callWithNonTypeTemplate<T>; (const T &); ; Argument[*0]; ReturnValue; value; manual |
34+
| 33 | Summary: ; ; false; pthread_create; ; ; Argument[@3]; Argument[2].Parameter[@0]; value; manual |
35+
| 34 | Summary: ; ; false; ymlStepGenerated; ; ; Argument[0]; ReturnValue; taint; df-generated |
36+
| 35 | Summary: ; ; false; ymlStepManual; ; ; Argument[0]; ReturnValue; taint; manual |
37+
| 36 | Summary: ; ; false; ymlStepManual_with_body; ; ; Argument[0]; ReturnValue; taint; manual |
38+
| 37 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual |
3839
edges
39-
| asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | asio_streams.cpp:56:18:56:23 | [summary] to write: ReturnValue in buffer | provenance | MaD:36 |
40+
| asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | asio_streams.cpp:56:18:56:23 | [summary] to write: ReturnValue in buffer | provenance | MaD:37 |
4041
| asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:91:7:91:17 | recv_buffer | provenance | Src:MaD:17 |
4142
| asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:93:29:93:39 | *recv_buffer | provenance | Src:MaD:17 Sink:MaD:2 |
4243
| asio_streams.cpp:97:37:97:44 | call to source | asio_streams.cpp:98:7:98:14 | send_str | provenance | TaintFunction |
@@ -45,10 +46,10 @@ edges
4546
| asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:101:7:101:17 | send_buffer | provenance | |
4647
| asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:103:29:103:39 | *send_buffer | provenance | Sink:MaD:2 |
4748
| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | provenance | |
48-
| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:36 |
49-
| test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | test.cpp:4:5:4:17 | [summary] to write: ReturnValue in ymlStepManual | provenance | MaD:34 |
50-
| test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | test.cpp:5:5:5:20 | [summary] to write: ReturnValue in ymlStepGenerated | provenance | MaD:33 |
51-
| test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | test.cpp:6:5:6:27 | [summary] to write: ReturnValue in ymlStepManual_with_body | provenance | MaD:35 |
49+
| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:37 |
50+
| test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | test.cpp:4:5:4:17 | [summary] to write: ReturnValue in ymlStepManual | provenance | MaD:35 |
51+
| test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | test.cpp:5:5:5:20 | [summary] to write: ReturnValue in ymlStepGenerated | provenance | MaD:34 |
52+
| test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | test.cpp:6:5:6:27 | [summary] to write: ReturnValue in ymlStepManual_with_body | provenance | MaD:36 |
5253
| test.cpp:7:47:7:52 | value2 | test.cpp:7:64:7:69 | value2 | provenance | |
5354
| test.cpp:7:64:7:69 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | provenance | |
5455
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:10:10:10:18 | call to ymlSource | provenance | Src:MaD:16 |
@@ -60,23 +61,23 @@ edges
6061
| test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | |
6162
| test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:18:10:18:10 | y | provenance | Sink:MaD:1 |
6263
| test.cpp:17:24:17:24 | x | test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | provenance | |
63-
| test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | MaD:34 |
64+
| test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | MaD:35 |
6465
| test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | |
6566
| test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:22:10:22:10 | z | provenance | Sink:MaD:1 |
6667
| test.cpp:21:27:21:27 | x | test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | provenance | |
67-
| test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | MaD:33 |
68+
| test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | MaD:34 |
6869
| test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | |
6970
| test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:26:10:26:11 | y2 | provenance | Sink:MaD:1 |
7071
| test.cpp:25:35:25:35 | x | test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | provenance | |
71-
| test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | MaD:35 |
72+
| test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | MaD:36 |
7273
| test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | provenance | |
7374
| test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:33:10:33:11 | z2 | provenance | Sink:MaD:1 |
7475
| test.cpp:32:41:32:41 | x | test.cpp:7:47:7:52 | value2 | provenance | |
7576
| test.cpp:32:41:32:41 | x | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | provenance | |
7677
| test.cpp:46:30:46:32 | *arg [x] | test.cpp:47:12:47:19 | *arg [x] | provenance | |
7778
| test.cpp:47:12:47:19 | *arg [x] | test.cpp:48:13:48:13 | *s [x] | provenance | |
7879
| test.cpp:48:13:48:13 | *s [x] | test.cpp:48:16:48:16 | x | provenance | Sink:MaD:1 |
79-
| test.cpp:52:5:52:18 | [summary param] *3 in pthread_create [x] | test.cpp:52:5:52:18 | [summary] to write: Argument[2].Parameter[*0] in pthread_create [x] | provenance | MaD:32 |
80+
| test.cpp:52:5:52:18 | [summary param] *3 in pthread_create [x] | test.cpp:52:5:52:18 | [summary] to write: Argument[2].Parameter[*0] in pthread_create [x] | provenance | MaD:33 |
8081
| test.cpp:52:5:52:18 | [summary] to write: Argument[2].Parameter[*0] in pthread_create [x] | test.cpp:46:30:46:32 | *arg [x] | provenance | |
8182
| test.cpp:56:2:56:2 | *s [post update] [x] | test.cpp:59:55:59:64 | *& ... [x] | provenance | |
8283
| test.cpp:56:2:56:18 | ... = ... | test.cpp:56:2:56:2 | *s [post update] [x] | provenance | |
@@ -103,6 +104,13 @@ edges
103104
| test.cpp:101:26:101:26 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | |
104105
| test.cpp:103:63:103:63 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | |
105106
| test.cpp:104:62:104:62 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | |
107+
| test.cpp:111:3:111:25 | [summary param] *0 in callWithNonTypeTemplate | test.cpp:111:3:111:25 | [summary] to write: ReturnValue in callWithNonTypeTemplate | provenance | MaD:32 |
108+
| test.cpp:114:10:114:18 | call to ymlSource | test.cpp:114:10:114:18 | call to ymlSource | provenance | Src:MaD:16 |
109+
| test.cpp:114:10:114:18 | call to ymlSource | test.cpp:118:44:118:44 | *x | provenance | |
110+
| test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | provenance | |
111+
| test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | test.cpp:119:10:119:11 | y2 | provenance | Sink:MaD:1 |
112+
| test.cpp:118:44:118:44 | *x | test.cpp:111:3:111:25 | [summary param] *0 in callWithNonTypeTemplate | provenance | |
113+
| test.cpp:118:44:118:44 | *x | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | provenance | MaD:32 |
106114
| windows.cpp:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | windows.cpp:17:8:17:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | provenance | MaD:18 |
107115
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:22:15:22:29 | *call to GetCommandLineA | provenance | Src:MaD:3 |
108116
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:24:8:24:11 | * ... | provenance | |
@@ -314,6 +322,14 @@ nodes
314322
| test.cpp:101:26:101:26 | x | semmle.label | x |
315323
| test.cpp:103:63:103:63 | x | semmle.label | x |
316324
| test.cpp:104:62:104:62 | x | semmle.label | x |
325+
| test.cpp:111:3:111:25 | [summary param] *0 in callWithNonTypeTemplate | semmle.label | [summary param] *0 in callWithNonTypeTemplate |
326+
| test.cpp:111:3:111:25 | [summary] to write: ReturnValue in callWithNonTypeTemplate | semmle.label | [summary] to write: ReturnValue in callWithNonTypeTemplate |
327+
| test.cpp:114:10:114:18 | call to ymlSource | semmle.label | call to ymlSource |
328+
| test.cpp:114:10:114:18 | call to ymlSource | semmle.label | call to ymlSource |
329+
| test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | semmle.label | call to callWithNonTypeTemplate |
330+
| test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | semmle.label | call to callWithNonTypeTemplate |
331+
| test.cpp:118:44:118:44 | *x | semmle.label | *x |
332+
| test.cpp:119:10:119:11 | y2 | semmle.label | y2 |
317333
| windows.cpp:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | semmle.label | [summary param] *0 in CommandLineToArgvA |
318334
| windows.cpp:17:8:17:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | semmle.label | [summary] to write: ReturnValue[**] in CommandLineToArgvA |
319335
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | semmle.label | *call to GetCommandLineA |
@@ -472,6 +488,7 @@ subpaths
472488
| test.cpp:21:27:21:27 | x | test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | test.cpp:5:5:5:20 | [summary] to write: ReturnValue in ymlStepGenerated | test.cpp:21:10:21:25 | call to ymlStepGenerated |
473489
| test.cpp:25:35:25:35 | x | test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | test.cpp:6:5:6:27 | [summary] to write: ReturnValue in ymlStepManual_with_body | test.cpp:25:11:25:33 | call to ymlStepManual_with_body |
474490
| test.cpp:32:41:32:41 | x | test.cpp:7:47:7:52 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body |
491+
| test.cpp:118:44:118:44 | *x | test.cpp:111:3:111:25 | [summary param] *0 in callWithNonTypeTemplate | test.cpp:111:3:111:25 | [summary] to write: ReturnValue in callWithNonTypeTemplate | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate |
475492
| windows.cpp:27:36:27:38 | *cmd | windows.cpp:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | windows.cpp:17:8:17:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA |
476493
| windows.cpp:537:40:537:41 | *& ... | windows.cpp:473:17:473:37 | [summary param] *1 in RtlCopyVolatileMemory | windows.cpp:473:17:473:37 | [summary param] *0 in RtlCopyVolatileMemory [Return] | windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument |
477494
| windows.cpp:542:38:542:39 | *& ... | windows.cpp:479:17:479:35 | [summary param] *1 in RtlCopyDeviceMemory | windows.cpp:479:17:479:35 | [summary param] *0 in RtlCopyDeviceMemory [Return] | windows.cpp:542:25:542:35 | RtlCopyDeviceMemory output argument |

cpp/ql/test/library-tests/dataflow/external-models/flow.ext.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ extensions:
1717
- ["", "", False, "ymlStepGenerated", "", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
1818
- ["", "", False, "ymlStepManual_with_body", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
1919
- ["", "", False, "ymlStepGenerated_with_body", "", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
20-
- ["", "", False, "callWithArgument", "", "", "Argument[1]", "Argument[0].Parameter[0]", "value", "manual"]
20+
- ["", "", False, "callWithArgument", "", "", "Argument[1]", "Argument[0].Parameter[0]", "value", "manual"]
21+
- ["", "", False, "callWithNonTypeTemplate<T>", "(const T &)", "", "Argument[*0]", "ReturnValue", "value", "manual"]

cpp/ql/test/library-tests/dataflow/external-models/sinks.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@
1313
| test.cpp:75:11:75:11 | y | test-sink |
1414
| test.cpp:83:11:83:11 | y | test-sink |
1515
| test.cpp:89:11:89:11 | y | test-sink |
16+
| test.cpp:116:10:116:11 | y1 | test-sink |
17+
| test.cpp:119:10:119:11 | y2 | test-sink |

cpp/ql/test/library-tests/dataflow/external-models/sources.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
| test.cpp:10:10:10:18 | call to ymlSource | local |
33
| test.cpp:56:8:56:16 | call to ymlSource | local |
44
| test.cpp:94:10:94:18 | call to ymlSource | local |
5+
| test.cpp:114:10:114:18 | call to ymlSource | local |
56
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | local |
67
| windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | local |
78
| windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | local |

cpp/ql/test/library-tests/dataflow/external-models/test.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,19 @@ void test_callWithArgument() {
102102
}
103103
callWithArgument(StructWithOperatorCall_has_constructor_2(), x);
104104
callWithArgument(StructWithOperatorCall_no_constructor_2(), x);
105-
}
105+
}
106+
107+
template<int N, typename T>
108+
T callWithNonTypeTemplate(const T&);
109+
110+
template<typename T, int N>
111+
T callWithNonTypeTemplate(const T&);
112+
113+
void test_callWithNonTypeTemplate() {
114+
int x = ymlSource();
115+
int y1 = callWithNonTypeTemplate<10, int>(x);
116+
ymlSink(y1); // $ MISSING: ir
117+
118+
int y2 = callWithNonTypeTemplate<int, 10>(x);
119+
ymlSink(y2); // $ ir
120+
}

0 commit comments

Comments
 (0)