Skip to content

Commit 166cfab

Browse files
[c2cpg] C++17 and C++20 features (#5210)
1 parent bf1cda8 commit 166cfab

File tree

12 files changed

+1168
-84
lines changed

12 files changed

+1168
-84
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Support For New Language Features
2+
3+
- For an explanation for each feature you may want to look at https://github.com/AnthonyCalandra/modern-cpp-features/tree/master.
4+
- Table legend:
5+
- `[?]` not yet checked
6+
- `[ ]` not supported at all / can not even be parsed
7+
- `[~]` can be parsed but is not fully represented in the CPG
8+
- `[x]` full support including the CPG representation
9+
10+
## C++17 Language Features
11+
12+
| Feature | Supported |
13+
|-------------------------------------------------------------------------|-----------|
14+
| template argument deduction for class templates | [~] |
15+
| declaring non-type template parameters with auto | [~] |
16+
| folding expressions | [x] |
17+
| new rules for auto deduction from braced-init-list | [x] |
18+
| constexpr lambda | [~] |
19+
| lambda capture this by value | [~] |
20+
| inline variables | [x] |
21+
| nested namespaces | [x] |
22+
| structured bindings | [x] |
23+
| selection statements with initializer | [x] |
24+
| constexpr if | [x] |
25+
| utf-8 character literals | [ ] |
26+
| direct-list-initialization of enums | [x] |
27+
| \[\[fallthrough\]\], \[\[nodiscard\]\], \[\[maybe_unused\]\] attributes | [~] |
28+
| \_\_has_include | [~] |
29+
| class template argument deduction | [~] |
30+
31+
## C++20 Language Features
32+
33+
| Feature | Supported |
34+
|------------------------------------------------|-----------|
35+
| coroutines | [?] |
36+
| concepts | [?] |
37+
| three-way comparison | [?] |
38+
| designated initializers | [?] |
39+
| template syntax for lambdas | [?] |
40+
| range-based for loop with initializer | [?] |
41+
| \[\[likely\]\] and \[\[unlikely\]\] attributes | [?] |
42+
| deprecate implicit capture of this | [?] |
43+
| class types in non-type template parameters | [?] |
44+
| constexpr virtual functions | [?] |
45+
| explicit(bool) | [?] |
46+
| immediate functions | [?] |
47+
| using enum | [?] |
48+
| lambda capture of parameter pack | [?] |
49+
| char8_t | [?] |
50+
| constinit | [?] |
51+
| \_\_VA_OPT\_\_ | [?] |

joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstCreatorHelper.scala

Lines changed: 75 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPMethod
2727
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation
2828
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalBinding
2929
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalMemberAccess
30+
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFoldExpression
31+
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalBinary
32+
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFoldExpression
33+
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTEqualsInitializer
34+
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPFunction
35+
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPVariable
3036
import org.eclipse.cdt.internal.core.model.ASTStringUtil
3137

3238
import java.nio.file.Path
@@ -148,8 +154,9 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As
148154
} else {
149155
rawType
150156
}
151-
StringUtils.normalizeSpace(tpe) match {
157+
StringUtils.normalizeSpace(tpe.stripSuffix(" ()")) match {
152158
case "" => Defines.Any
159+
case t if t.startsWith("[*this]") || t.startsWith("[this]") => t
153160
case t if t.startsWith("[") && t.endsWith("]") => Defines.Array
154161
case t if t.contains("->") => Defines.Function
155162
case t if t.contains("?") => Defines.Any
@@ -186,6 +193,13 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As
186193
Try(expr.getEvaluation).toOption
187194
}
188195

196+
protected def safeGetBinding(spec: IASTNamedTypeSpecifier): Option[IBinding] = {
197+
// In case of unresolved includes etc. this may fail throwing an unrecoverable exception
198+
Try(spec.getName.resolveBinding()).toOption.collect {
199+
case binding: IBinding if !binding.isInstanceOf[IProblemBinding] => binding
200+
}
201+
}
202+
189203
protected def safeGetType(tpe: IType): String = {
190204
// In case of unresolved includes etc. this may fail throwing an unrecoverable exception
191205
Try(ASTTypeUtil.getType(tpe)).getOrElse(Defines.Any)
@@ -203,6 +217,22 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As
203217
}
204218
}
205219

220+
private def typeForCPPASTFoldExpression(f: CPPASTFoldExpression, stripKeywords: Boolean = true): String = {
221+
safeGetEvaluation(f) match {
222+
case Some(evaluation: EvalFoldExpression) =>
223+
Try(evaluation.getValue.getEvaluation).toOption match {
224+
case Some(value: EvalBinary) =>
225+
val s = value.toString
226+
cleanType(s.substring(0, s.indexOf(": ")), stripKeywords)
227+
case Some(value: EvalBinding) if value.getType.isInstanceOf[ICPPParameterPackType] =>
228+
val s = value.getType.asInstanceOf[ICPPParameterPackType].getType.toString
229+
cleanType(s, stripKeywords)
230+
case _ => Defines.Any
231+
}
232+
case _ => Defines.Any
233+
}
234+
}
235+
206236
@nowarn
207237
private def typeForIASTArrayDeclarator(a: IASTArrayDeclarator, stripKeywords: Boolean = true): String = {
208238
import org.eclipse.cdt.core.dom.ast.ASTSignatureUtil.getNodeSignature
@@ -234,8 +264,10 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As
234264
cleanType(evaluation.getOwnerType.toString + deref, stripKeywords)
235265
case Some(evalBinding: EvalBinding) =>
236266
evalBinding.getBinding match {
237-
case m: CPPMethod => cleanType(fullName(m.getDefinition))
238-
case _ => cleanType(safeGetNodeType(s), stripKeywords)
267+
case m: CPPMethod => cleanType(safeGetNodeType(m.getPrimaryDeclaration), stripKeywords)
268+
case f: CPPFunction => cleanType(safeGetNodeType(f.getDefinition), stripKeywords)
269+
case v: CPPVariable => cleanType(v.getType.toString)
270+
case _ => cleanType(safeGetNodeType(s), stripKeywords)
239271
}
240272
case _ => cleanType(safeGetNodeType(s), stripKeywords)
241273
}
@@ -256,26 +288,46 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As
256288
}
257289
}
258290

291+
private def typeForCPPASTEqualsInitializer(c: CPPASTEqualsInitializer, stripKeywords: Boolean = true): String = {
292+
import org.eclipse.cdt.core.dom.ast.ASTSignatureUtil.getNodeSignature
293+
c.getInitializerClause match {
294+
case initializer: ICPPASTFunctionCallExpression
295+
if initializer.getFunctionNameExpression.isInstanceOf[CPPASTIdExpression] =>
296+
val name = initializer.getFunctionNameExpression.asInstanceOf[CPPASTIdExpression]
297+
typeForCPPASTIdExpression(name, stripKeywords)
298+
case _ =>
299+
cleanType(getNodeSignature(c), stripKeywords)
300+
}
301+
}
302+
259303
@nowarn
260304
protected def typeFor(node: IASTNode, stripKeywords: Boolean = true): String = {
261305
import org.eclipse.cdt.core.dom.ast.ASTSignatureUtil.getNodeSignature
262306
node match {
263-
case f: CPPASTFieldReference => typeForCPPASTFieldReference(f)
264-
case f: IASTFieldReference => cleanType(safeGetType(f.getFieldOwner.getExpressionType), stripKeywords)
265-
case a: IASTArrayDeclarator => typeForIASTArrayDeclarator(a)
266-
case s: CPPASTIdExpression => typeForCPPASTIdExpression(s)
307+
case f: CPPASTFoldExpression => typeForCPPASTFoldExpression(f, stripKeywords)
308+
case f: CPPASTFieldReference => typeForCPPASTFieldReference(f, stripKeywords)
309+
case s: CPPASTIdExpression => typeForCPPASTIdExpression(s, stripKeywords)
310+
case s: ICPPASTNamedTypeSpecifier => typeForCPPAstNamedTypeSpecifier(s, stripKeywords)
311+
case a: IASTArrayDeclarator => typeForIASTArrayDeclarator(a, stripKeywords)
312+
case c: ICPPASTConstructorInitializer => typeForICPPASTConstructorInitializer(c, stripKeywords)
313+
case c: CPPASTEqualsInitializer => typeForCPPASTEqualsInitializer(c, stripKeywords)
267314
case _: IASTIdExpression | _: IASTName | _: IASTDeclarator => cleanType(safeGetNodeType(node), stripKeywords)
268-
case s: IASTNamedTypeSpecifier => cleanType(ASTStringUtil.getReturnTypeString(s, null), stripKeywords)
269-
case s: IASTCompositeTypeSpecifier => cleanType(ASTStringUtil.getReturnTypeString(s, null), stripKeywords)
270-
case s: IASTEnumerationSpecifier => cleanType(ASTStringUtil.getReturnTypeString(s, null), stripKeywords)
271-
case s: IASTElaboratedTypeSpecifier => cleanType(ASTStringUtil.getReturnTypeString(s, null), stripKeywords)
272-
case l: IASTLiteralExpression => cleanType(safeGetType(l.getExpressionType))
273-
case e: IASTExpression => cleanType(safeGetNodeType(e), stripKeywords)
274-
case c: ICPPASTConstructorInitializer => typeForICPPASTConstructorInitializer(c)
275-
case _ => cleanType(getNodeSignature(node), stripKeywords)
315+
case f: IASTFieldReference => cleanType(safeGetType(f.getFieldOwner.getExpressionType), stripKeywords)
316+
case s: IASTNamedTypeSpecifier => cleanType(ASTStringUtil.getReturnTypeString(s, null), stripKeywords)
317+
case s: IASTCompositeTypeSpecifier => cleanType(ASTStringUtil.getReturnTypeString(s, null), stripKeywords)
318+
case s: IASTEnumerationSpecifier => cleanType(ASTStringUtil.getReturnTypeString(s, null), stripKeywords)
319+
case s: IASTElaboratedTypeSpecifier => cleanType(ASTStringUtil.getReturnTypeString(s, null), stripKeywords)
320+
case l: IASTLiteralExpression => cleanType(safeGetType(l.getExpressionType), stripKeywords)
321+
case e: IASTExpression => cleanType(safeGetNodeType(e), stripKeywords)
322+
case _ => cleanType(getNodeSignature(node), stripKeywords)
276323
}
277324
}
278325

326+
private def typeForCPPAstNamedTypeSpecifier(s: ICPPASTNamedTypeSpecifier, stripKeywords: Boolean): String = {
327+
val tpe = safeGetBinding(s).map(_.toString.replace(" ", "")).getOrElse(ASTStringUtil.getReturnTypeString(s, null))
328+
cleanType(tpe, stripKeywords)
329+
}
330+
279331
private def notHandledText(node: IASTNode): String =
280332
s"""Node '${node.getClass.getSimpleName}' not handled yet!
281333
| Code: '${node.getRawSignature}'
@@ -305,6 +357,9 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As
305357
r
306358
}
307359

360+
protected def nullSafeAst(node: IASTInitializer): Ast =
361+
Option(node).map(astForNode).getOrElse(Ast())
362+
308363
protected def nullSafeAst(node: IASTExpression): Ast =
309364
Option(node).map(astForNode).getOrElse(Ast())
310365

@@ -322,7 +377,10 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As
322377
}
323378

324379
private def pointersAsString(spec: IASTDeclSpecifier, parentDecl: IASTDeclarator, stripKeywords: Boolean): String = {
325-
val tpe = typeFor(spec, stripKeywords)
380+
val tpe = typeFor(spec, stripKeywords) match {
381+
case Defines.Auto => typeFor(parentDecl, stripKeywords)
382+
case t => t
383+
}
326384
val pointers = parentDecl.getPointerOperators
327385
val arr = parentDecl match {
328386
case p: IASTArrayDeclarator => p.getArrayModifiers.toList.map(_.getRawSignature).mkString
@@ -425,6 +483,7 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As
425483
case l: IASTInitializerList => astForInitializerList(l)
426484
case c: ICPPASTConstructorInitializer => astForCPPASTConstructorInitializer(c)
427485
case d: ICASTDesignatedInitializer => astForCASTDesignatedInitializer(d)
486+
case d: IASTEqualsInitializer => astForNode(d.getInitializerClause)
428487
case d: ICPPASTDesignatedInitializer => astForCPPASTDesignatedInitializer(d)
429488
case d: CASTArrayRangeDesignator => astForCASTArrayRangeDesignator(d)
430489
case d: CPPASTArrayRangeDesignator => astForCPPASTArrayRangeDesignator(d)

joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForExpressionsCreator.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTIdExpression
1717
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTQualifiedName
1818
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPClosureType
1919
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFunctionCall
20+
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFoldExpression
2021

2122
import scala.util.Try
2223

@@ -521,6 +522,26 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
521522
private def astForPackExpansionExpression(packExpansionExpression: ICPPASTPackExpansionExpression): Ast =
522523
astForExpression(packExpansionExpression.getPattern)
523524

525+
private def astForFoldExpression(foldExpression: CPPASTFoldExpression): Ast = {
526+
def valueFromField[T](obj: Any, fieldName: String): Option[T] = {
527+
// we need this hack because fields are all private at CPPASTExpression
528+
Try {
529+
val field = obj.getClass.getDeclaredField(fieldName)
530+
field.setAccessible(true)
531+
field.get(obj).asInstanceOf[T]
532+
}.toOption
533+
}
534+
535+
val op = "<operator>.fold"
536+
val tpe = typeFor(foldExpression)
537+
val callNode_ =
538+
callNode(foldExpression, code(foldExpression), op, op, DispatchTypes.STATIC_DISPATCH, None, Some(tpe))
539+
540+
val left = valueFromField[ICPPASTExpression](foldExpression, "fLhs").map(nullSafeAst).getOrElse(Ast())
541+
val right = valueFromField[ICPPASTExpression](foldExpression, "fRhs").map(nullSafeAst).getOrElse(Ast())
542+
callAst(callNode_, List(left, right))
543+
}
544+
524545
protected def astForExpression(expression: IASTExpression): Ast = {
525546
val r = expression match {
526547
case lit: IASTLiteralExpression => astForLiteral(lit)
@@ -542,6 +563,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
542563
case lambdaExpression: ICPPASTLambdaExpression => astForMethodRefForLambda(lambdaExpression)
543564
case cExpr: IGNUASTCompoundStatementExpression => astForCompoundStatementExpression(cExpr)
544565
case pExpr: ICPPASTPackExpansionExpression => astForPackExpansionExpression(pExpr)
566+
case foldExpression: CPPASTFoldExpression => astForFoldExpression(foldExpression)
545567
case _ => notHandledYet(expression)
546568
}
547569
asChildOfMacroCall(expression, r)

0 commit comments

Comments
 (0)