Skip to content

Commit c48fcf1

Browse files
committed
C++: Support concept id expressions
1 parent 554ea29 commit c48fcf1

File tree

3 files changed

+111
-2
lines changed

3 files changed

+111
-2
lines changed

cpp/ql/lib/semmle/code/cpp/Concept.qll

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,110 @@ class NestedRequirementExpr extends Expr, @nested_requirement {
153153

154154
/**
155155
* A C++ concept id expression.
156+
*
157+
* For example, if:
158+
* ```cpp
159+
* template<typename T, T X> concept C = ...;
160+
* ...
161+
* requires { C<int, 1>; };
162+
* ```
163+
* then `C<int, 1>` is a concept id expression that refers to
164+
* the concept `C`.
156165
*/
157166
class ConceptIdExpr extends RequirementExpr, @concept_id {
158-
override string toString() { result = "concept<...>" }
167+
override string toString() {
168+
exists(string name |
169+
concept_templates(this.getConcept(), name, _) and
170+
result = name + "<...>"
171+
)
172+
or
173+
// The following is for backward compatibility with databases created with
174+
// CodeQL 2.19.3, 2.19.4, and 2.20.0. Those databases include concept id
175+
// expressions, but do not include concept template information.
176+
not exists(this.getConcept()) and
177+
result = "concept<...>"
178+
}
159179

160180
override string getAPrimaryQlClass() { result = "ConceptIdExpr" }
181+
182+
/**
183+
* Holds if the concept id is used as a type constraint.
184+
*
185+
* In this case, the first template argument is implicit.
186+
*/
187+
predicate isTypeConstraint() { is_type_constraint(underlyingElement(this)) }
188+
189+
/**
190+
* Gets the concept this concept id refers to.
191+
*/
192+
Concept getConcept() { concept_instantiation(underlyingElement(this), unresolveElement(result)) }
193+
194+
/**
195+
* Gets a template argument passed to the concept.
196+
*/
197+
final Locatable getATemplateArgument() { result = this.getTemplateArgument(_) }
198+
199+
/**
200+
* Gets the kind of a non-type template argument passed to the concept.
201+
*/
202+
final Locatable getATemplateArgumentKind() { result = this.getTemplateArgumentKind(_) }
203+
204+
/**
205+
* Gets the `i`th template argument passed to the concept.
206+
*
207+
* For example, if:
208+
* ```cpp
209+
* template<typename T, T X> concept C = ...;
210+
* ...
211+
* requires { C<int, 1>; };
212+
* ```
213+
* then `getTemplateArgument(0)` yields `int`, and `getTemplateArgument(1)`
214+
* yields `1`.
215+
*
216+
* If the concept id is a type constraint, then `getTemplateArgument(0)`
217+
* will not yield a result.
218+
*/
219+
final Locatable getTemplateArgument(int index) {
220+
if exists(this.getTemplateArgumentValue(index))
221+
then result = this.getTemplateArgumentValue(index)
222+
else result = this.getTemplateArgumentType(index)
223+
}
224+
225+
/**
226+
* Gets the kind of the `i`th template argument value passed to the concept.
227+
*
228+
* For example, if:
229+
* ```cpp
230+
* template<typename T, T X> concept C = ...;
231+
* ...
232+
* requires { C<int, 1>; };
233+
* ```
234+
* then `getTemplateArgumentKind(1)` yields `int`, and there is no result for
235+
* `getTemplateArgumentKind(0)`.
236+
*/
237+
final Locatable getTemplateArgumentKind(int index) {
238+
exists(this.getTemplateArgumentValue(index)) and
239+
result = this.getTemplateArgumentType(index)
240+
}
241+
242+
/**
243+
* Gets the number of template arguments passed to the concept.
244+
*/
245+
final int getNumberOfTemplateArguments() {
246+
result = count(int i | exists(this.getTemplateArgument(i)))
247+
}
248+
249+
private Type getTemplateArgumentType(int index) {
250+
exists(int i | if this.isTypeConstraint() then i = index - 1 else i = index |
251+
concept_template_argument(underlyingElement(this), i, unresolveElement(result))
252+
)
253+
}
254+
255+
private Expr getTemplateArgumentValue(int index) {
256+
exists(int i | if this.isTypeConstraint() then i = index - 1 else i = index |
257+
concept_template_argument_value(underlyingElement(this), i, unresolveElement(result))
258+
)
259+
}
161260
}
162261

163262
/**
@@ -187,4 +286,9 @@ class Concept extends Declaration, @concept_template {
187286
* the constraint expression is `std::is_same<T, int>::value`.
188287
*/
189288
Expr getExpr() { result.getParent() = this }
289+
290+
/**
291+
* Gets a concept id expression that refers to this concept
292+
*/
293+
ConceptIdExpr getAReferringConceptIdExpr() { this = result.getConcept() }
190294
}

cpp/ql/lib/semmle/code/cpp/Declaration.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ class Declaration extends Locatable, @declaration {
235235
*
236236
* `Foo<int, 1> bar;`
237237
*
238-
* Will have `getTemplateArgument())` return `int`, and
238+
* Will have `getTemplateArgument(0)` return `int`, and
239239
* `getTemplateArgument(1)` return `1`.
240240
*/
241241
final Locatable getTemplateArgument(int index) {

cpp/ql/lib/semmlecode.cpp.dbscheme

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,11 @@ concept_templates(
883883
string name: string ref,
884884
int location: @location_default ref
885885
);
886+
concept_instantiation(
887+
unique int to: @concept_id ref,
888+
int from: @concept_template ref
889+
);
890+
is_type_constraint(int concept_id: @concept_id ref);
886891
concept_template_argument(
887892
int concept_id: @concept ref,
888893
int index: int ref,

0 commit comments

Comments
 (0)