-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
C++: Support concept id expressions #18366
Changes from all commits
c48fcf1
f8458f6
033f35f
7cba263
f08d100
6325dd2
ac05bfc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
description: Support concept id expressions | ||
compatibility: full | ||
concept_instantiation.rel: delete | ||
is_type_constraint.rel: delete |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
category: feature | ||
--- | ||
* A new class `ConceptIdExpr` was introduced, which represents C++20 concept id expressions. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -153,11 +153,107 @@ class NestedRequirementExpr extends Expr, @nested_requirement { | |
|
||
/** | ||
* A C++ concept id expression. | ||
* | ||
* For example, if: | ||
* ```cpp | ||
* template<typename T, T X> concept C = ...; | ||
* ... | ||
* requires { C<int, 1>; }; | ||
* ``` | ||
* then `C<int, 1>` is a concept id expression that refers to | ||
* the concept `C`. | ||
*/ | ||
class ConceptIdExpr extends RequirementExpr, @concept_id { | ||
override string toString() { result = "concept<...>" } | ||
override string toString() { | ||
result = this.getConcept().getName() + "<...>" | ||
or | ||
// The following is for backward compatibility with databases created with | ||
// CodeQL 2.19.3, 2.19.4, and 2.20.0. Those databases include concept id | ||
// expressions, but do not include concept template information. | ||
not exists(this.getConcept()) and | ||
result = "concept<...>" | ||
} | ||
|
||
override string getAPrimaryQlClass() { result = "ConceptIdExpr" } | ||
|
||
/** | ||
* Holds if the concept id is used as a type constraint. | ||
* | ||
* In this case, the first template argument is implicit. | ||
*/ | ||
predicate isTypeConstraint() { is_type_constraint(underlyingElement(this)) } | ||
|
||
/** | ||
* Gets the concept this concept id refers to. | ||
*/ | ||
Concept getConcept() { concept_instantiation(underlyingElement(this), unresolveElement(result)) } | ||
|
||
/** | ||
* Gets a template argument passed to the concept. | ||
*/ | ||
final Locatable getATemplateArgument() { result = this.getTemplateArgument(_) } | ||
|
||
/** | ||
* Gets the kind of a non-type template argument passed to the concept. | ||
*/ | ||
final Locatable getATemplateArgumentKind() { result = this.getTemplateArgumentKind(_) } | ||
|
||
/** | ||
* Gets the `i`th template argument passed to the concept. | ||
* | ||
* For example, if: | ||
* ```cpp | ||
* template<typename T, T X> concept C = ...; | ||
* ... | ||
* requires { C<int, 1>; }; | ||
* ``` | ||
* then `getTemplateArgument(0)` yields `int`, and `getTemplateArgument(1)` | ||
* yields `1`. | ||
* | ||
* If the concept id is a type constraint, then `getTemplateArgument(0)` | ||
* will not yield a result. | ||
*/ | ||
final Locatable getTemplateArgument(int index) { | ||
|
||
if exists(this.getTemplateArgumentValue(index)) | ||
then result = this.getTemplateArgumentValue(index) | ||
else result = this.getTemplateArgumentType(index) | ||
} | ||
|
||
/** | ||
* Gets the kind of the `i`th template argument value passed to the concept. | ||
* | ||
* For example, if: | ||
* ```cpp | ||
* template<typename T, T X> concept C = ...; | ||
* ... | ||
* requires { C<int, 1>; }; | ||
* ``` | ||
* then `getTemplateArgumentKind(1)` yields `int`, and there is no result for | ||
* `getTemplateArgumentKind(0)`. | ||
*/ | ||
final Locatable getTemplateArgumentKind(int index) { | ||
|
||
exists(this.getTemplateArgumentValue(index)) and | ||
result = this.getTemplateArgumentType(index) | ||
} | ||
|
||
/** | ||
* Gets the number of template arguments passed to the concept. | ||
*/ | ||
final int getNumberOfTemplateArguments() { | ||
result = count(int i | exists(this.getTemplateArgument(i))) | ||
} | ||
|
||
private Type getTemplateArgumentType(int index) { | ||
exists(int i | if this.isTypeConstraint() then i = index - 1 else i = index | | ||
concept_template_argument(underlyingElement(this), i, unresolveElement(result)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that there's a trade-off in the design here: I follow what we do for the arguments in template instantiations, as concept ids are similar to those. Alternatively we could have added them as a kind of child expressions. The latter would have made the modifications to PrintAST somewhat simpler, but would have required custom code on the extractor-side, while in the current situation that shares code with template instantiations. |
||
) | ||
} | ||
|
||
private Expr getTemplateArgumentValue(int index) { | ||
exists(int i | if this.isTypeConstraint() then i = index - 1 else i = index | | ||
concept_template_argument_value(underlyingElement(this), i, unresolveElement(result)) | ||
) | ||
} | ||
} | ||
|
||
/** | ||
|
@@ -187,4 +283,9 @@ class Concept extends Declaration, @concept_template { | |
* the constraint expression is `std::is_same<T, int>::value`. | ||
*/ | ||
Expr getExpr() { result.getParent() = this } | ||
|
||
/** | ||
* Gets a concept id expression that refers to this concept | ||
*/ | ||
ConceptIdExpr getAReferringConceptIdExpr() { this = result.getConcept() } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Observation: It feels like we need a new return type
class TemplateArgument
here, but I see that this code follows the examples elsewhere in the library, so don't fix this now.