diff --git a/cpg-console/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/NullPointerCheck.kt b/cpg-console/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/NullPointerCheck.kt index 1271cc6927..bacbaccbd9 100644 --- a/cpg-console/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/NullPointerCheck.kt +++ b/cpg-console/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/NullPointerCheck.kt @@ -76,7 +76,7 @@ class NullPointerCheck { fun handleHasBase(node: HasBase) { try { // check for all incoming DFG branches - node.base.prevDFG.forEach { + node.base?.prevDFG?.forEach { var resolved: Any? = CouldNotResolve() val resolver = ValueEvaluator() if (it is Expression || it is Declaration) { @@ -90,7 +90,7 @@ class NullPointerCheck { sb.append("--- FINDING: Null pointer detected in ") sb.append(node.javaClass.simpleName, DEFAULT.foreground(GREEN)) sb.append(" when accessing base ") - sb.append(node.base.name, DEFAULT.foreground(YELLOW)) + sb.append(node.base?.name, DEFAULT.foreground(YELLOW)) sb.append(" ---") val header = sb.toAnsi() @@ -104,7 +104,7 @@ class NullPointerCheck { ) println("") println( - "The following path was discovered that leads to ${AttributedString(node.base.name, DEFAULT.foreground(YELLOW)).toAnsi()} being null:" + "The following path was discovered that leads to ${AttributedString(node.base?.name, DEFAULT.foreground(YELLOW)).toAnsi()} being null:" ) for (p in resolver.path) { diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/frontends/cpp/ExpressionHandler.kt b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/frontends/cpp/ExpressionHandler.kt index 67bef85308..539ed0540f 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/frontends/cpp/ExpressionHandler.kt +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/frontends/cpp/ExpressionHandler.kt @@ -232,22 +232,22 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : } /** - * Gets all arguments a template was instantiated with. Note, that the arguments can either be - * Expressions referring to a value ot TypeExpressions referring to a type. + * Retrieves all arguments a template was instantiated with. Note, that the arguments can either + * be Expressions referring to a value ot TypeExpressions referring to a type. * * @param template * @return List of Nodes containing the all the arguments the template was instantiated with. */ - private fun getTemplateArguments(template: CPPASTTemplateId): List { - val templateArguments: MutableList = ArrayList() + private fun getTemplateArguments(template: CPPASTTemplateId): List { + val templateArguments: MutableList = ArrayList() for (argument in template.templateArguments) { if (argument is IASTTypeId) { val type = TypeParser.createFrom(argument.declSpecifier.toString(), true) templateArguments.add(NodeBuilder.newTypeExpression(type.name, type)) } else if (argument is IASTLiteralExpression) { - templateArguments.add( - lang.expressionHandler.handle(argument as IASTInitializerClause) - ) + lang.expressionHandler.handle(argument as IASTInitializerClause)?.let { + templateArguments.add(it) + } } } return templateArguments @@ -443,12 +443,11 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : as CPPASTTemplateId) .templateName.toString() callExpression.name = name - callExpression.addExplicitTemplateParameters( - getTemplateArguments( + getTemplateArguments( (ctx.functionNameExpression as IASTFieldReference).fieldName as CPPASTTemplateId ) - ) + .forEach { callExpression.addTemplateParameter(it) } } } else if (reference is BinaryOperator && reference.operatorCode == ".") { // We have a dot operator that was not classified as a member expression. This happens @@ -473,11 +472,10 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : ((ctx.functionNameExpression as IASTIdExpression).name as CPPASTTemplateId) .templateName.toString() callExpression = NodeBuilder.newCallExpression(name, name, ctx.rawSignature, true) - callExpression.addExplicitTemplateParameters( - getTemplateArguments( + getTemplateArguments( (ctx.functionNameExpression as IASTIdExpression).name as CPPASTTemplateId ) - ) + .forEach { callExpression.addTemplateParameter(it) } } else if (reference is CastExpression) { // this really is a cast expression in disguise return reference diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/HasBase.kt b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/HasBase.kt index d84adfbb3a..5bf1ec6bff 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/HasBase.kt +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/HasBase.kt @@ -30,5 +30,5 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression /** Specifies that a certain node has a base on which it executes an operation. */ interface HasBase { - val base: Expression + val base: Expression? } diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt index b5208d0f4b..d100b43159 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt @@ -70,7 +70,7 @@ object NodeBuilder { node.name = name!! node.setCodeAndRegion(lang, rawNode, code) node.fqn = fqn - node.setTemplate(template) + node.template = template log(node) return node } @@ -385,7 +385,7 @@ object NodeBuilder { ): CallExpression { val node = MemberCallExpression() node.name = name!! - node.setBase(base) + node.base = base node.member = member node.operatorCode = operatorCode node.setCodeAndRegion(lang, rawNode, code) diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/declarations/Declaration.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/declarations/Declaration.kt similarity index 60% rename from cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/declarations/Declaration.java rename to cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/declarations/Declaration.kt index 25526f89c7..d6b244fca1 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/declarations/Declaration.java +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/declarations/Declaration.kt @@ -23,20 +23,17 @@ * \______/ \__| \______/ * */ -package de.fraunhofer.aisec.cpg.graph.declarations; +package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.Node; +import de.fraunhofer.aisec.cpg.graph.Node /** - * Represents a single declaration or definition, i.e. of a variable ({@link VariableDeclaration}) - * or function ({@link FunctionDeclaration}). + * Represents a single declaration or definition, i.e. of a variable ([VariableDeclaration]) or + * function ([FunctionDeclaration]). * - *

Note: We do NOT (currently) distinguish between the definition and the declaration of a - * function. This means, that if a function is first declared and later defined with a function - * body, we will currently have two {@link FunctionDeclaration} nodes. This is very similar to the - * behaviour of clang, however clang does establish a connection between those nodes, we currently - * do not. + * Note: We do NOT (currently) distinguish between the definition and the declaration of a function. + * This means, that if a function is first declared and later defined with a function body, we will + * currently have two [FunctionDeclaration] nodes. This is very similar to the behaviour of clang, + * however clang does establish a connection between those nodes, we currently do not. */ -// TODO: expressionRefersToDeclaration definition and declaration nodes and introduce a field if its -// declaration only -public abstract class Declaration extends Node {} +abstract class Declaration : Node() diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/declarations/DeclarationSequence.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/declarations/DeclarationSequence.java deleted file mode 100644 index 2c6a33fc7a..0000000000 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/declarations/DeclarationSequence.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2020, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ -package de.fraunhofer.aisec.cpg.graph.declarations; - -import de.fraunhofer.aisec.cpg.graph.DeclarationHolder; -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import org.jetbrains.annotations.NotNull; -import org.neo4j.ogm.annotation.Relationship; - -/** - * This represents a sequence of one or more declaration(s). The purpose of this node is primarily - * to bridge between a single declaration and a list of declarations in the front-end handlers. It - * will be converted into a list-structure and all its children will be added to the parent, i.e. - * the translation unit. It should not end up in the final graph. - */ -public class DeclarationSequence extends Declaration implements DeclarationHolder { - - @Relationship(value = "CHILDREN", direction = "OUTGOING") - private final List> children = new ArrayList<>(); - - public List> getChildrenPropertyEdge() { - return this.children; - } - - public List getChildren() { - List target = new ArrayList<>(); - for (PropertyEdge propertyEdge : this.children) { - target.add(propertyEdge.getEnd()); - } - return Collections.unmodifiableList(target); - } - - public void addDeclaration(@NotNull Declaration declaration) { - if (declaration instanceof DeclarationSequence) { - for (Declaration declarationChild : ((DeclarationSequence) declaration).getChildren()) { - addIfNotContains(this.children, declarationChild); - } - } - - addIfNotContains(this.children, declaration); - } - - public List asList() { - return getChildren(); - } - - public boolean isSingle() { - return children.size() == 1; - } - - public Declaration first() { - return children.get(0).getEnd(); - } - - @NotNull - public List getDeclarations() { - return getChildren(); - } -} diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/declarations/DeclarationSequence.kt b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/declarations/DeclarationSequence.kt new file mode 100644 index 0000000000..ff36690aa9 --- /dev/null +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/declarations/DeclarationSequence.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.declarations + +import de.fraunhofer.aisec.cpg.graph.DeclarationHolder +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import org.neo4j.ogm.annotation.Relationship + +/** + * This represents a sequence of one or more declaration(s). The purpose of this node is primarily + * to bridge between a single declaration and a list of declarations in the front-end handlers. It + * will be converted into a list-structure and all its children will be added to the parent, i.e. + * the translation unit. It should NOT end up in the final graph. + */ +class DeclarationSequence : Declaration(), DeclarationHolder { + @Relationship(value = "CHILDREN", direction = Relationship.OUTGOING) + val childrenPropertyEdge: MutableList> = mutableListOf() + + val children: List by + PropertyEdgeDelegate(DeclarationSequence::childrenPropertyEdge) + + override fun addDeclaration(declaration: Declaration) { + if (declaration is DeclarationSequence) { + for (declarationChild in declaration.children) { + addIfNotContains(childrenPropertyEdge, declarationChild) + } + } + addIfNotContains(childrenPropertyEdge, declaration) + } + + fun asList(): List { + return children + } + + val isSingle: Boolean + get() = childrenPropertyEdge.size == 1 + + fun first(): Declaration { + return childrenPropertyEdge[0].end + } + + override fun getDeclarations(): List { + return children + } +} diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdge.kt b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdge.kt index 3d66c1a0e5..ca33f1e362 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdge.kt +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/edge/PropertyEdge.kt @@ -27,14 +27,31 @@ package de.fraunhofer.aisec.cpg.graph.edge import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.Persistable +import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression +import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal import java.lang.reflect.Field import java.lang.reflect.InvocationTargetException import java.lang.reflect.ParameterizedType import java.util.* +import kotlin.reflect.KMutableProperty1 +import kotlin.reflect.KProperty +import kotlin.reflect.KProperty1 import org.neo4j.ogm.annotation.* import org.neo4j.ogm.annotation.typeconversion.Convert import org.slf4j.LoggerFactory +/** + * This class represents an edge between two [Node] objects in a Neo4J graph. It can be used to + * store additional information that relate to the relationship between the two nodes that belong to + * neither of the two nodes directly. + * + * An example would be the name (in this case `a`) of an argument between a [CallExpression] (`foo`) + * and its argument (a [Literal] of `2`) in languages that support keyword arguments, such as + * Python: + * ```python + * foo("bar", a = 2) + * ``` + */ @RelationshipEntity open class PropertyEdge : Persistable { /** Required field for object graph mapping. It contains the node id. */ @@ -296,3 +313,44 @@ open class PropertyEdge : Persistable { } } } + +/** + * This class can be used to implement + * [delegated properties](https://kotlinlang.org/docs/delegated-properties.html) in [Node] classes. + * The most common use case is to have a property that is a list of [PropertyEdge] objects (for + * persistence) and a second (delegated) property that allows easy access just to the connected + * nodes of the individual edges for in-memory access. + * + * For example: + * ```kotlin + * + * class MyNode { + * @Relationship(value = "EXPRESSIONS", direction = "OUTGOING") + * @field:SubGraph("AST") + * var expressionsEdges = mutableListOf>() + * var expressions by PropertyEdgeDelegate(MyNode::expressionsEdges) + * } + * ``` + * + * This class is intentionally marked with [Transient], so that the delegated properties are not + * transferred to the Neo4J OGM. Only the property that contains the property edges should be + * persisted in the graph database. + */ +@Transient +class PropertyEdgeDelegate( + val edge: KProperty1>>, + val outgoing: Boolean = true +) { + operator fun getValue(thisRef: S, property: KProperty<*>): List { + return PropertyEdge.unwrap(edge.get(thisRef), outgoing) + } + + operator fun setValue(thisRef: S, property: KProperty<*>, value: List) { + if (edge is KMutableProperty1) { + edge.setter.call( + thisRef, + PropertyEdge.transformIntoOutgoingPropertyEdgeList(value, thisRef as Node) + ) + } + } +} diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.java deleted file mode 100644 index 0d9304b824..0000000000 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.java +++ /dev/null @@ -1,426 +0,0 @@ -/* - * Copyright (c) 2020, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ -package de.fraunhofer.aisec.cpg.graph.statements.expressions; - -import de.fraunhofer.aisec.cpg.graph.HasBase; -import de.fraunhofer.aisec.cpg.graph.HasType; -import de.fraunhofer.aisec.cpg.graph.HasType.TypeListener; -import de.fraunhofer.aisec.cpg.graph.Node; -import de.fraunhofer.aisec.cpg.graph.SubGraph; -import de.fraunhofer.aisec.cpg.graph.TypeManager; -import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration; -import de.fraunhofer.aisec.cpg.graph.declarations.TemplateDeclaration; -import de.fraunhofer.aisec.cpg.graph.edge.Properties; -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge; -import de.fraunhofer.aisec.cpg.graph.types.Type; -import de.fraunhofer.aisec.cpg.helpers.Util; -import java.util.*; -import java.util.stream.Collectors; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.neo4j.ogm.annotation.Relationship; - -/** - * An expression, which calls another function. It has a list of arguments (list of {@link - * Expression}s) and is connected via the INVOKES edge to its {@link FunctionDeclaration}. - */ -public class CallExpression extends Expression - implements TypeListener, HasBase, HasType.SecondaryTypeEdge { - - /** - * Connection to its {@link FunctionDeclaration}. This will be populated by the {@link - * de.fraunhofer.aisec.cpg.passes.CallResolver}. - */ - @Relationship(value = "INVOKES", direction = "OUTGOING") - protected List> invokes = new ArrayList<>(); - - /** The list of arguments. */ - @Relationship(value = "ARGUMENTS", direction = "OUTGOING") - @SubGraph("AST") - private List> arguments = new ArrayList<>(); - - /** - * The base object. This is marked as an AST child, because this is required for {@link - * MemberCallExpression}. Be aware that for simple calls the implicit "this" base is not part of - * the original AST, but we treat it as such for better consistency - */ - @SubGraph("AST") - private Expression base; - - private String fqn; - - @NotNull - public Expression getBase() { - return base; - } - - public void setBase(Expression base) { - if (this.base != null) { - this.base.unregisterTypeListener(this); - } - this.base = base; - if (base != null) { - base.registerTypeListener(this); - } - } - - @NotNull - public List getArguments() { - List targets = new ArrayList<>(); - for (PropertyEdge propertyEdge : this.arguments) { - targets.add(propertyEdge.getEnd()); - } - return Collections.unmodifiableList(targets); - } - - public void setArgument(int index, Expression argument) { - this.arguments.get(index).setEnd(argument); - } - - @NotNull - public List> getArgumentsPropertyEdge() { - return this.arguments; - } - - public void addArgument(Expression expression) { - addArgument(expression, null); - } - - public void addArgument(Expression expression, @Nullable String name) { - PropertyEdge propertyEdge = new PropertyEdge<>(this, expression); - propertyEdge.addProperty(Properties.INDEX, this.arguments.size()); - - if (name != null) { - propertyEdge.addProperty(Properties.NAME, name); - } - - this.arguments.add(propertyEdge); - } - - public void setArguments(List arguments) { - this.arguments = PropertyEdge.transformIntoOutgoingPropertyEdgeList(arguments, this); - } - - @NotNull - public List getInvokes() { - List targets = new ArrayList<>(); - for (PropertyEdge propertyEdge : this.invokes) { - targets.add(propertyEdge.getEnd()); - } - return Collections.unmodifiableList(targets); - } - - public List> getInvokesPropertyEdge() { - return this.invokes; - } - - public void setInvokes(List invokes) { - PropertyEdge.unwrap(this.invokes) - .forEach( - i -> { - i.unregisterTypeListener(this); - Util.detachCallParameters(i, this.getArguments()); - this.removePrevDFG(i); - }); - this.invokes = PropertyEdge.transformIntoOutgoingPropertyEdgeList(invokes, this); - invokes.forEach( - i -> { - i.registerTypeListener(this); - Util.attachCallParameters(i, this.getArguments()); - this.addPrevDFG(i); - }); - } - - public List getSignature() { - return getArguments().stream().map(Expression::getType).collect(Collectors.toList()); - } - - boolean template; - - public void setTemplate(boolean template) { - this.template = template; - if (template) { - this.templateParameters = new ArrayList<>(); - } - } - - /** If the CallExpression instantiates a Template, the call can provide template parameters */ - @Relationship(value = "TEMPLATE_PARAMETERS", direction = "OUTGOING") - @SubGraph("AST") - @Nullable - private List> templateParameters; - - /** - * If the CallExpression instantiates a Template the CallExpression is connected to the template - * which is instantiated. This is required by the expansion pass to access the Template directly. - * The invokes edge will still point to the realization of the template. - */ - @Relationship(value = "TEMPLATE_INSTANTIATION", direction = "OUTGOING") - @Nullable - private TemplateDeclaration templateInstantiation; - - @Nullable - public List> getTemplateParametersPropertyEdge() { - return templateParameters; - } - - @Nullable - public List getTemplateParameters() { - if (this.templateParameters == null) { - return null; - } - return PropertyEdge.unwrap(this.templateParameters); - } - - @Nullable - public List getTypeTemplateParameters() { - if (this.templateParameters == null) { - return null; - } - List types = new ArrayList<>(); - for (Node n : getTemplateParameters()) { - if (n instanceof Type) { - types.add((Type) n); - } - } - return types; - } - - public void addTemplateParameter( - Type typeTemplateParam, TemplateDeclaration.TemplateInitialization templateInitialization) { - if (this.templateParameters == null) { - this.templateParameters = new ArrayList<>(); - } - PropertyEdge propertyEdge = new PropertyEdge<>(this, typeTemplateParam); - propertyEdge.addProperty(Properties.INDEX, this.templateParameters.size()); - propertyEdge.addProperty(Properties.INSTANTIATION, templateInitialization); - this.templateParameters.add(propertyEdge); - this.template = true; - } - - public void replaceTypeTemplateParameter(Type oldType, Type newType) { - if (this.templateParameters == null) { - return; - } - for (int i = 0; i < this.templateParameters.size(); i++) { - PropertyEdge propertyEdge = this.templateParameters.get(i); - if (propertyEdge.getEnd().equals(oldType)) { - propertyEdge.setEnd(newType); - } - } - } - - public void addTemplateParameter( - Expression expressionTemplateParam, - TemplateDeclaration.TemplateInitialization templateInitialization) { - if (this.templateParameters == null) { - this.templateParameters = new ArrayList<>(); - } - PropertyEdge propertyEdge = new PropertyEdge<>(this, expressionTemplateParam); - propertyEdge.addProperty(Properties.INDEX, this.templateParameters.size()); - propertyEdge.addProperty(Properties.INSTANTIATION, templateInitialization); - this.templateParameters.add(propertyEdge); - this.template = true; - } - - public void addTemplateParameter( - Node templateParam, TemplateDeclaration.TemplateInitialization templateInitialization) { - if (templateParam instanceof Expression) { - addTemplateParameter((Expression) templateParam, templateInitialization); - } else if (templateParam instanceof Type) { - addTemplateParameter((Type) templateParam, templateInitialization); - } - } - - public void addExplicitTemplateParameter(Node templateParameter) { - addTemplateParameter(templateParameter, TemplateDeclaration.TemplateInitialization.EXPLICIT); - } - - public void addExplicitTemplateParameters(List templateParameters) { - for (Node node : templateParameters) { - addTemplateParameter(node, TemplateDeclaration.TemplateInitialization.EXPLICIT); - } - } - - public void removeRealization(Node templateParam) { - if (this.templateParameters == null) { - return; - } - this.templateParameters.removeIf(propertyEdge -> propertyEdge.getEnd().equals(templateParam)); - } - - public void setTemplateParameters(List> templateParameters) { - this.templateParameters = templateParameters; - template = templateParameters != null; - } - - @Nullable - public TemplateDeclaration getTemplateInstantiation() { - return templateInstantiation; - } - - public void setTemplateInstantiation(TemplateDeclaration templateInstantiation) { - this.templateInstantiation = templateInstantiation; - template = templateInstantiation != null; - } - - public void updateTemplateParameters( - Map initializationType, - List orderedInitializationSignature) { - if (this.templateParameters == null) { - return; - } - for (PropertyEdge edge : this.templateParameters) { - if (edge.getProperty(Properties.INSTANTIATION) != null - && edge.getProperty(Properties.INSTANTIATION) - .equals(TemplateDeclaration.TemplateInitialization.UNKNOWN) - && initializationType.containsKey(edge.getEnd())) { - edge.addProperty(Properties.INSTANTIATION, initializationType.get(edge.getEnd())); - } - } - - for (int i = this.templateParameters.size(); i < orderedInitializationSignature.size(); i++) { - var signature = orderedInitializationSignature.get(i); - if (signature == null) { - continue; - } - - PropertyEdge propertyEdge = - new PropertyEdge<>(this, orderedInitializationSignature.get(i)); - propertyEdge.addProperty(Properties.INDEX, this.templateParameters.size()); - propertyEdge.addProperty( - Properties.INSTANTIATION, - initializationType.getOrDefault( - orderedInitializationSignature.get(i), - TemplateDeclaration.TemplateInitialization.UNKNOWN)); - this.templateParameters.add(propertyEdge); - } - } - - public boolean instantiatesTemplate() { - return templateInstantiation != null || templateParameters != null || template; - } - - @Override - public void typeChanged(HasType src, List root, Type oldType) { - if (!TypeManager.isTypeSystemActive()) { - return; - } - if (src == base) { - setFqn(src.getType().getRoot().getTypeName() + "." + this.getName()); - } else { - Type previous = this.type; - List types = - invokes.stream() - .map(PropertyEdge::getEnd) - .map(FunctionDeclaration::getType) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - Type alternative = !types.isEmpty() ? types.get(0) : null; - Type commonType = TypeManager.getInstance().getCommonType(types).orElse(alternative); - List subTypes = new ArrayList<>(getPossibleSubTypes()); - subTypes.remove(oldType); - subTypes.addAll(types); - - setType(commonType, root); - setPossibleSubTypes(subTypes, root); - - if (!previous.equals(this.type)) { - this.type.setTypeOrigin(Type.Origin.DATAFLOW); - } - } - } - - @Override - public void possibleSubTypesChanged(HasType src, List root) { - if (!TypeManager.isTypeSystemActive()) { - return; - } - if (src != base) { - List subTypes = new ArrayList<>(getPossibleSubTypes()); - subTypes.addAll(src.getPossibleSubTypes()); - setPossibleSubTypes(subTypes, root); - } - } - - @NotNull - @Override - public String toString() { - return new ToStringBuilder(this, Node.TO_STRING_STYLE) - .appendSuper(super.toString()) - .append("base", base) - .toString(); - } - - public String getFqn() { - return fqn; - } - - public void setFqn(String fqn) { - this.fqn = fqn; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof CallExpression)) { - return false; - } - CallExpression that = (CallExpression) o; - return super.equals(that) - && Objects.equals(this.getArguments(), that.getArguments()) - && PropertyEdge.propertyEqualsList(arguments, that.arguments) - && Objects.equals(this.getInvokes(), that.getInvokes()) - && PropertyEdge.propertyEqualsList(invokes, that.invokes) - && Objects.equals(base, that.base) - && Objects.equals(getTemplateParameters(), that.getTemplateParameters()) - && PropertyEdge.propertyEqualsList(templateParameters, that.templateParameters) - && Objects.equals(templateInstantiation, that.templateInstantiation) - && template == that.template; - } - - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public void updateType(Collection typeState) { - if (this.templateParameters == null) { - return; - } - for (Type t : this.getTypeTemplateParameters()) { - for (Type t2 : typeState) { - if (t2.equals(t)) { - this.replaceTypeTemplateParameter(t, t2); - } - } - } - } -} diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt new file mode 100644 index 0000000000..b8a2203b11 --- /dev/null +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2020, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.statements.expressions + +import de.fraunhofer.aisec.cpg.graph.* +import de.fraunhofer.aisec.cpg.graph.HasType.SecondaryTypeEdge +import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.TemplateDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.TemplateDeclaration.TemplateInitialization +import de.fraunhofer.aisec.cpg.graph.edge.Properties +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.transformIntoOutgoingPropertyEdgeList +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.unwrap +import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.helpers.Util +import de.fraunhofer.aisec.cpg.passes.CallResolver +import java.util.* +import org.apache.commons.lang3.builder.ToStringBuilder +import org.neo4j.ogm.annotation.Relationship + +/** + * An expression, which calls another function. It has a list of arguments (list of [Expression]s) + * and is connected via the INVOKES edge to its [FunctionDeclaration]. + */ +open class CallExpression : Expression(), HasType.TypeListener, HasBase, SecondaryTypeEdge { + /** Connection to its [FunctionDeclaration]. This will be populated by the [CallResolver]. */ + @Relationship(value = "INVOKES", direction = Relationship.OUTGOING) + @PopulatedByPass(CallResolver::class) + var invokesRelationship = mutableListOf>() + protected set + + /** + * A virtual property to quickly access the list of declarations that this call invokes without + * property edges. + */ + var invokes: List + get(): List { + val targets: MutableList = ArrayList() + for (propertyEdge in invokesRelationship) { + targets.add(propertyEdge.end) + } + return Collections.unmodifiableList(targets) + } + set(value) { + unwrap(invokesRelationship).forEach { + it.unregisterTypeListener(this) + Util.detachCallParameters(it, arguments) + removePrevDFG(it) + } + invokesRelationship = transformIntoOutgoingPropertyEdgeList(value, this) + value.forEach { + it.registerTypeListener(this) + Util.attachCallParameters(it, arguments) + addPrevDFG(it) + } + } + + /** + * The list of arguments of this call expression, backed by a list of [PropertyEdge] objects. + */ + @Relationship(value = "ARGUMENTS", direction = Relationship.OUTGOING) + @field:SubGraph("AST") + var argumentsEdges = mutableListOf>() + + /** + * The list of arguments as a simple list. This is a delegated property delegated to + * [argumentsEdges]. + */ + var arguments by PropertyEdgeDelegate(CallExpression::argumentsEdges) + + /** + * The base object. This is marked as an AST child, because this is required for a + * [MemberCallExpression]. Be aware that for simple calls the implicit "this" base is not part + * of the original AST, but we treat it as such for better consistency + */ + @field:SubGraph("AST") + override var base: Expression? = null + set(value) { + field?.unregisterTypeListener(this) + field = value + value?.registerTypeListener(this) + } + + var fqn: String? = null + + fun setArgument(index: Int, argument: Expression) { + argumentsEdges[index].end = argument + } + + /** Adds the specified [expression] with an optional [name] to this call. */ + @JvmOverloads + fun addArgument(expression: Expression, name: String? = null) { + val edge = PropertyEdge(this, expression) + edge.addProperty(Properties.INDEX, argumentsEdges.size) + + if (name != null) { + edge.addProperty(Properties.NAME, name) + } + + argumentsEdges.add(edge) + } + + /** Returns the function signature as list of types of the call arguments. */ + val signature: List + get() = argumentsEdges.map { it.end.type } + + /** Specifies, whether this call has any template arguments. */ + var template = false + + /** If the CallExpression instantiates a template, the call can provide template parameters. */ + @Relationship(value = "TEMPLATE_PARAMETERS", direction = Relationship.OUTGOING) + @field:SubGraph("AST") + var templateParametersEdges: MutableList>? = null + set(value) { + field = value + template = value != null + } + + val templateParameters: List + get(): List { + return unwrap(templateParametersEdges ?: listOf()) + } + + /** + * If the CallExpression instantiates a Template the CallExpression is connected to the template + * which is instantiated. This is required by the expansion pass to access the Template + * directly. The invokes edge will still point to the realization of the template. + */ + @Relationship(value = "TEMPLATE_INSTANTIATION", direction = Relationship.OUTGOING) + var templateInstantiation: TemplateDeclaration? = null + set(value) { + field = value + template = value != null + } + + private val typeTemplateParameters: List + get() { + val types: MutableList = ArrayList() + for (n in templateParameters) { + if (n is Type) { + types.add(n) + } + } + return types + } + + private fun replaceTypeTemplateParameter(oldType: Type?, newType: Type) { + for (i in templateParametersEdges?.indices ?: listOf()) { + val propertyEdge = templateParametersEdges!![i] + if (propertyEdge.end == oldType) { + propertyEdge.end = newType + } + } + } + + /** + * Adds a template parameter to this call expression. A parameter can either be an [Expression] + * (usually a [Literal]) or a [Type]. + */ + @JvmOverloads + fun addTemplateParameter( + templateParam: Node, + templateInitialization: TemplateInitialization? = TemplateInitialization.EXPLICIT + ) { + if (templateParam is Expression || templateParam is Type) { + if (templateParametersEdges == null) { + templateParametersEdges = mutableListOf() + } + + val propertyEdge = PropertyEdge(this, templateParam) + propertyEdge.addProperty(Properties.INDEX, templateParameters.size) + propertyEdge.addProperty(Properties.INSTANTIATION, templateInitialization) + templateParametersEdges!!.add(propertyEdge) + template = true + } + } + + fun updateTemplateParameters( + initializationType: Map, + orderedInitializationSignature: List + ) { + if (templateParametersEdges == null) { + templateParametersEdges = mutableListOf() + } + + for (edge in templateParametersEdges!!) { + if (edge.getProperty(Properties.INSTANTIATION) != null && + (edge.getProperty(Properties.INSTANTIATION) == + TemplateInitialization.UNKNOWN) && + initializationType.containsKey(edge.end) + ) { + edge.addProperty(Properties.INSTANTIATION, initializationType[edge.end]) + } + } + + for (i in templateParametersEdges!!.size until orderedInitializationSignature.size) { + val propertyEdge = PropertyEdge(this, orderedInitializationSignature[i]) + propertyEdge.addProperty(Properties.INDEX, templateParametersEdges!!.size) + propertyEdge.addProperty( + Properties.INSTANTIATION, + initializationType.getOrDefault( + orderedInitializationSignature[i], + TemplateInitialization.UNKNOWN + ) + ) + templateParametersEdges!!.add(propertyEdge) + } + } + + fun instantiatesTemplate(): Boolean { + return templateInstantiation != null || templateParametersEdges != null || template + } + + override fun typeChanged(src: HasType, root: List, oldType: Type) { + if (!TypeManager.isTypeSystemActive()) { + return + } + if (src === base) { + fqn = src.getType().root.typeName + "." + name + } else { + val previous = type + val types = + invokesRelationship + .map(PropertyEdge::end) + .map { it.type } + .filter(Objects::nonNull) + val alternative = if (types.isNotEmpty()) types[0] else null + val commonType = TypeManager.getInstance().getCommonType(types).orElse(alternative) + val subTypes: MutableList = ArrayList(possibleSubTypes) + subTypes.remove(oldType) + subTypes.addAll(types) + setType(commonType, root) + setPossibleSubTypes(subTypes, root) + if (previous != type) { + type.typeOrigin = Type.Origin.DATAFLOW + } + } + } + + override fun possibleSubTypesChanged(src: HasType, root: List) { + if (!TypeManager.isTypeSystemActive()) { + return + } + if (src !== base) { + val subTypes: MutableList = ArrayList(possibleSubTypes) + subTypes.addAll(src.possibleSubTypes) + setPossibleSubTypes(subTypes, root) + } + } + + override fun toString(): String { + return ToStringBuilder(this, TO_STRING_STYLE) + .appendSuper(super.toString()) + .append("base", base) + .toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other !is CallExpression) { + return false + } + + return (((super.equals(other) && + arguments == other.arguments && + propertyEqualsList(argumentsEdges, other.argumentsEdges)) && + invokes == other.invokes && + propertyEqualsList(invokesRelationship, other.invokesRelationship)) && + base == other.base && + templateParameters == other.templateParameters && + propertyEqualsList(templateParametersEdges, other.templateParametersEdges)) && + templateInstantiation == other.templateInstantiation && + template == other.template + } + + override fun hashCode(): Int { + return super.hashCode() + } + + override fun updateType(typeState: Collection) { + for (t in typeTemplateParameters) { + for (t2 in typeState) { + if (t2 == t) { + replaceTypeTemplateParameter(t, t2) + } + } + } + } +} diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt index 126a421f33..5d4be3687c 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt @@ -32,7 +32,6 @@ import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.ConstructorDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.graph.types.TypeParser import de.fraunhofer.aisec.cpg.graph.types.UnknownType @@ -58,7 +57,7 @@ class ConstructExpression : CallExpression(), HasType.TypeListener { // Forward to CallExpression. This will also take care of DFG edges. if (value != null) { - setInvokes(listOf(value as FunctionDeclaration)) + invokes = listOf(value as FunctionDeclaration) } } @@ -101,10 +100,7 @@ class ConstructExpression : CallExpression(), HasType.TypeListener { return false } - return super.equals(other) && - constructor == other.constructor && - arguments == other.arguments && - PropertyEdge.propertyEqualsList(argumentsPropertyEdge, other.argumentsPropertyEdge) + return super.equals(other) && constructor == other.constructor } override fun hashCode(): Int { diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/passes/CallResolver.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/passes/CallResolver.java index 842ffd0bb1..63a3d02725 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/passes/CallResolver.java +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/passes/CallResolver.java @@ -175,10 +175,10 @@ public static void addImplicitTemplateParametersToCall( if (templateParams != null) { for (Node node : templateParams) { if (node instanceof TypeExpression) { - constructExpression.addExplicitTemplateParameter( + constructExpression.addTemplateParameter( NodeBuilder.duplicateTypeExpression((TypeExpression) node, true)); } else if (node instanceof Literal) { - constructExpression.addExplicitTemplateParameter( + constructExpression.addTemplateParameter( NodeBuilder.duplicateLiteral((Literal) node, true)); } } @@ -360,7 +360,7 @@ protected boolean isInstantiated(Node callParameter, Declaration templateParamet /** * Check if we are handling an implicit template parameter, if so set instantiationSignature, - * instantiationType and orderedInitializationSignature maps accordningly + * instantiationType and orderedInitializationSignature maps accordingly * * @param functionTemplateDeclaration functionTemplate we have identified * @param index position of the templateParameter we are currently handling @@ -582,17 +582,22 @@ && checkArgumentValidity( if (applyInference) { // If we want to use an inferred functionTemplateDeclaration, this needs to be provided. - // Otherwise we could not resolve to a template and no modifications are made + // Otherwise, we could not resolve to a template and no modifications are made FunctionTemplateDeclaration functionTemplateDeclaration = createInferredFunctionTemplate(curClass, templateCall); templateCall.setTemplateInstantiation(functionTemplateDeclaration); templateCall.setInvokes(functionTemplateDeclaration.getRealization()); - // Set instantiation propertyEdges - for (PropertyEdge instantiationParameter : - templateCall.getTemplateParametersPropertyEdge()) { - instantiationParameter.addProperty( - Properties.INSTANTIATION, TemplateDeclaration.TemplateInitialization.EXPLICIT); + + var edges = templateCall.getTemplateParametersEdges(); + + if (edges != null) { + // Set instantiation propertyEdges + for (PropertyEdge instantiationParameter : edges) { + instantiationParameter.addProperty( + Properties.INSTANTIATION, TemplateDeclaration.TemplateInitialization.EXPLICIT); + } } + return true; } return false; @@ -1070,14 +1075,14 @@ protected void handleNormalCallCXX(RecordDeclaration curClass, CallExpression ca if (invocationCandidates.isEmpty()) { /* Check if the call can be resolved to a function template instantiation. If it can be resolver, we - resolve the call. Otherwise there won't be an inferred template, we will do an inferred + resolve the call. Otherwise, there won't be an inferred template, we will do an inferred FunctionDeclaration instead. */ - call.setTemplateParameters(new ArrayList<>()); + call.setTemplateParametersEdges(new ArrayList<>()); if (handleTemplateFunctionCalls(curClass, call, false)) { return; } else { - call.setTemplateParameters(null); + call.setTemplateParametersEdges(null); } } @@ -1176,7 +1181,7 @@ protected List handleCXXMethodCall( if (handleTemplateFunctionCalls(curClass, call, false)) { return call.getInvokes(); } else { - call.setTemplateParameters(null); + call.setTemplateParametersEdges(null); } } @@ -1295,7 +1300,6 @@ protected void resolveConstructExpression(ConstructExpression constructExpressio for (TemplateDeclaration template : templateList) { if (template instanceof ClassTemplateDeclaration && ((ClassTemplateDeclaration) template).getRealization().contains(recordDeclaration) - && constructExpression.getTemplateParameters() != null && constructExpression.getTemplateParameters().size() <= template.getParameters().size()) { int defaultDifference = diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManager.kt b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManager.kt index a28733b68c..40053d2b9f 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManager.kt +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManager.kt @@ -656,11 +656,13 @@ class ScopeManager { ): List { var s = scope + val fqn = call.fqn + // First, we need to check, whether we have some kind of scoping. - if (lang != null && call.fqn != null && call.fqn.contains(lang!!.namespaceDelimiter)) { + if (lang != null && fqn != null && fqn.contains(lang!!.namespaceDelimiter)) { // extract the scope name, it is usually a name space, but could probably be something // else as well in other languages - val scopeName = call.fqn.substring(0, call.fqn.lastIndexOf(lang!!.namespaceDelimiter)) + val scopeName = fqn.substring(0, fqn.lastIndexOf(lang!!.namespaceDelimiter)) // TODO: proper scope selection diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/ClassTemplateTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/ClassTemplateTest.kt index 048509df3b..3a996c5cce 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/ClassTemplateTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/ClassTemplateTest.kt @@ -104,6 +104,7 @@ internal class ClassTemplateTest : BaseTest() { point1: VariableDeclaration ) { assertEquals(pairConstructorDeclaration, constructExpression.constructor) + assertNotNull(pairConstructorDeclaration) assertTrue(constructExpression.invokes.contains(pairConstructorDeclaration)) assertEquals(pair, constructExpression.instantiates) assertEquals(template, constructExpression.templateInstantiation) @@ -116,17 +117,14 @@ internal class ClassTemplateTest : BaseTest() { assertEquals(2, instantiatedType.generics.size) assertEquals("int", instantiatedType.generics[0].name) assertEquals("int", instantiatedType.generics[1].name) - assertEquals(2, constructExpression.templateParameters!!.size) - assertEquals( - "int", - (constructExpression.templateParameters!![0] as TypeExpression).type.name - ) - assertEquals( - "int", - (constructExpression.templateParameters!![1] as TypeExpression).type.name - ) - assertTrue(constructExpression.templateParameters!![0].isImplicit) - assertTrue(constructExpression.templateParameters!![1].isImplicit) + + val templateParameters = constructExpression.templateParameters + assertNotNull(templateParameters) + assertEquals(2, templateParameters.size) + assertEquals("int", (templateParameters[0] as TypeExpression).type.name) + assertEquals("int", (templateParameters[1] as TypeExpression).type.name) + assertTrue(templateParameters[0].isImplicit) + assertTrue(templateParameters[1].isImplicit) assertEquals(2, point1.templateParameters.size) assertEquals("int", (point1.templateParameters[0] as TypeExpression).type.name) assertEquals("int", (point1.templateParameters[1] as TypeExpression).type.name) @@ -242,11 +240,13 @@ internal class ClassTemplateTest : BaseTest() { assertEquals(literal3, point1.templateParameters[2]) // Test Invocation - assertEquals(3, constructExpression.templateParameters!!.size) - assertEquals(literal3Implicit, constructExpression.templateParameters!![2]) + val templateParameters = constructExpression.templateParameters + assertNotNull(templateParameters) + assertEquals(3, templateParameters.size) + assertEquals(literal3Implicit, templateParameters[2]) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - constructExpression.templateParametersPropertyEdge + constructExpression.templateParametersEdges ?.get(2) ?.getProperty(Properties.INSTANTIATION) ) @@ -264,18 +264,18 @@ internal class ClassTemplateTest : BaseTest() { assertEquals(pair, constructExpression.instantiates) assertEquals(template, constructExpression.templateInstantiation) assertEquals(pairConstructorDeclaration, constructExpression.constructor) - assertEquals(2, constructExpression.templateParameters!!.size) - assertEquals("int", constructExpression.templateParameters!![0].name) + assertEquals(2, constructExpression.templateParameters.size) + assertEquals("int", constructExpression.templateParameters[0].name) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - constructExpression.templateParametersPropertyEdge + constructExpression.templateParametersEdges ?.get(0) ?.getProperty(Properties.INSTANTIATION) ) - assertEquals("int", constructExpression.templateParameters!![1].name) + assertEquals("int", constructExpression.templateParameters[1].name) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - constructExpression.templateParametersPropertyEdge + constructExpression.templateParametersEdges ?.get(1) ?.getProperty(Properties.INSTANTIATION) ) @@ -371,38 +371,39 @@ internal class ClassTemplateTest : BaseTest() { findByUniquePredicate(flattenListIsInstance>(result)) { it.value == 2 && !it.isImplicit } + assertNotNull(literal2) val literal2Implicit = findByUniquePredicate(flattenListIsInstance>(result)) { it.value == 2 && it.isImplicit } assertEquals(pair, constructExpression.instantiates) assertEquals(template, constructExpression.templateInstantiation) - assertEquals(4, constructExpression.templateParameters!!.size) - assertEquals("int", constructExpression.templateParameters!![0].name) + assertEquals(4, constructExpression.templateParameters.size) + assertEquals("int", constructExpression.templateParameters[0].name) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - constructExpression.templateParametersPropertyEdge + constructExpression.templateParametersEdges ?.get(0) ?.getProperty(Properties.INSTANTIATION) ) - assertEquals("int", constructExpression.templateParameters!![1].name) + assertEquals("int", constructExpression.templateParameters[1].name) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - constructExpression.templateParametersPropertyEdge + constructExpression.templateParametersEdges ?.get(1) ?.getProperty(Properties.INSTANTIATION) ) - assertEquals(literal2Implicit, constructExpression.templateParameters!![2]) + assertEquals(literal2Implicit, constructExpression.templateParameters[2]) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - constructExpression.templateParametersPropertyEdge + constructExpression.templateParametersEdges ?.get(2) ?.getProperty(Properties.INSTANTIATION) ) - assertEquals(literal2Implicit, constructExpression.templateParameters!![3]) + assertEquals(literal2Implicit, constructExpression.templateParameters[3]) assertEquals( TemplateDeclaration.TemplateInitialization.DEFAULT, - constructExpression.templateParametersPropertyEdge + constructExpression.templateParametersEdges ?.get(3) ?.getProperty(Properties.INSTANTIATION) ) @@ -432,13 +433,10 @@ internal class ClassTemplateTest : BaseTest() { val paramB = findByUniqueName(flattenListIsInstance(result), "B") val constructExpression = findByUniquePredicate(flattenListIsInstance(result)) { - c: ConstructExpression -> - c.code == "()" + it.code == "()" } val literal1 = - findByUniquePredicate>(flattenListIsInstance(result)) { l: Literal<*> -> - l.value == 1 - } + findByUniquePredicate>(flattenListIsInstance(result)) { it.value == 1 } assertEquals(4, template.parameters.size) assertEquals(paramA, template.parameters[2]) assertEquals(literal1, paramA.default) @@ -446,64 +444,50 @@ internal class ClassTemplateTest : BaseTest() { assertEquals(paramA, (paramB.default as DeclaredReferenceExpression).refersTo) assertEquals(pair, constructExpression.instantiates) assertEquals(template, constructExpression.templateInstantiation) - assertEquals(4, constructExpression.templateParameters!!.size) - assertEquals( - "int", - (constructExpression.templateParameters!![0] as TypeExpression).type.name - ) + assertEquals(4, constructExpression.templateParameters.size) + assertEquals("int", (constructExpression.templateParameters[0] as TypeExpression).type.name) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - constructExpression.templateParametersPropertyEdge + constructExpression.templateParametersEdges ?.get(0) ?.getProperty(Properties.INSTANTIATION) ) assertEquals( 0, - constructExpression.templateParametersPropertyEdge - ?.get(0) - ?.getProperty(Properties.INDEX) - ) - assertEquals( - "int", - (constructExpression.templateParameters!![1] as TypeExpression).type.name + constructExpression.templateParametersEdges?.get(0)?.getProperty(Properties.INDEX) ) + assertEquals("int", (constructExpression.templateParameters[1] as TypeExpression).type.name) assertEquals( TemplateDeclaration.TemplateInitialization.DEFAULT, - constructExpression.templateParametersPropertyEdge + constructExpression.templateParametersEdges ?.get(1) ?.getProperty(Properties.INSTANTIATION) ) assertEquals( 1, - constructExpression.templateParametersPropertyEdge - ?.get(1) - ?.getProperty(Properties.INDEX) + constructExpression.templateParametersEdges?.get(1)?.getProperty(Properties.INDEX) ) - assertEquals(literal1, constructExpression.templateParameters!![2]) + assertEquals(literal1, constructExpression.templateParameters[2]) assertEquals( TemplateDeclaration.TemplateInitialization.DEFAULT, - constructExpression.templateParametersPropertyEdge + constructExpression.templateParametersEdges ?.get(2) ?.getProperty(Properties.INSTANTIATION) ) assertEquals( 2, - constructExpression.templateParametersPropertyEdge - ?.get(2) - ?.getProperty(Properties.INDEX) + constructExpression.templateParametersEdges?.get(2)?.getProperty(Properties.INDEX) ) - assertEquals(literal1, constructExpression.templateParameters!![3]) + assertEquals(literal1, constructExpression.templateParameters[3]) assertEquals( TemplateDeclaration.TemplateInitialization.DEFAULT, - constructExpression.templateParametersPropertyEdge + constructExpression.templateParametersEdges ?.get(3) ?.getProperty(Properties.INSTANTIATION) ) assertEquals( 3, - constructExpression.templateParametersPropertyEdge - ?.get(3) - ?.getProperty(Properties.INDEX) + constructExpression.templateParametersEdges?.get(3)?.getProperty(Properties.INDEX) ) // Test Type @@ -531,9 +515,7 @@ internal class ClassTemplateTest : BaseTest() { val paramT = findByUniqueName(flattenListIsInstance(result), "typename T") val literal10 = - findByUniquePredicate(flattenListIsInstance>(result)) { l: Literal<*> -> - l.value == 10 - } + findByUniquePredicate(flattenListIsInstance>(result)) { it.value == 10 } val thisField = findByUniqueName(flattenListIsInstance(result), "this") val mArray = findByUniqueName(flattenListIsInstance(result), "m_Array") assertEquals(2, template.parameters.size) @@ -561,8 +543,8 @@ internal class ClassTemplateTest : BaseTest() { } assertEquals(template, constructExpression.templateInstantiation) assertEquals(array, constructExpression.instantiates) - assertEquals("int", constructExpression.templateParameters!![0].name) - assertEquals(literal10, constructExpression.templateParameters!![1]) + assertEquals("int", constructExpression.templateParameters[0].name) + assertEquals(literal10, constructExpression.templateParameters[1]) assertEquals("Array", constructExpression.type.name) val instantiatedType = constructExpression.type as ObjectType @@ -585,14 +567,14 @@ internal class ClassTemplateTest : BaseTest() { ) val array = findByUniqueName(flattenListIsInstance(result), "Array") val constructExpression = - findByUniquePredicate(flattenListIsInstance(result)) { - c: ConstructExpression -> + findByUniquePredicate(flattenListIsInstance(result)) { c: ConstructExpression -> c.code == "()" } val literal5 = findByUniquePredicate(flattenListIsInstance>(result)) { it.value == 5 && it.location!!.region.endColumn == 41 && !it.isImplicit } + assertNotNull(literal5) val literal5Declaration = findByUniquePredicate(flattenListIsInstance>(result)) { it.value == 5 && it.location!!.region.endColumn == 14 && !it.isImplicit @@ -606,13 +588,10 @@ internal class ClassTemplateTest : BaseTest() { val newExpression = findByUniqueName(flattenListIsInstance(result), "") assertEquals(array, constructExpression.instantiates) assertEquals(template, constructExpression.templateInstantiation) - assertEquals(2, constructExpression.templateParameters!!.size) - assertEquals( - "int", - (constructExpression.templateParameters!![0] as TypeExpression).type.name - ) - assertTrue(constructExpression.templateParameters!![0].isImplicit) - assertEquals(literal5Implicit, constructExpression.templateParameters!![1]) + assertEquals(2, constructExpression.templateParameters.size) + assertEquals("int", (constructExpression.templateParameters[0] as TypeExpression).type.name) + assertTrue(constructExpression.templateParameters[0].isImplicit) + assertEquals(literal5Implicit, constructExpression.templateParameters[1]) assertEquals(2, arrayVariable.templateParameters.size) assertEquals("int", (arrayVariable.templateParameters[0] as TypeExpression).type.name) assertFalse(arrayVariable.templateParameters[0].isImplicit) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt index eab76e52ea..e3fb108502 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt @@ -76,22 +76,16 @@ internal class FunctionTemplateTest : BaseTest() { ) { assertEquals(2, callFloat3.templateParameters!!.size) assertEquals(floatType, (callFloat3.templateParameters!![0] as TypeExpression).type) - assertEquals( - 0, - callFloat3.templateParametersPropertyEdge!![0].getProperty(Properties.INDEX) - ) + assertEquals(0, callFloat3.templateParametersEdges!![0].getProperty(Properties.INDEX)) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - callFloat3.templateParametersPropertyEdge!![0].getProperty(Properties.INSTANTIATION) + callFloat3.templateParametersEdges!![0].getProperty(Properties.INSTANTIATION) ) assertEquals(int3, callFloat3.templateParameters!![1]) - assertEquals( - 1, - callFloat3.templateParametersPropertyEdge!![1].getProperty(Properties.INDEX) - ) + assertEquals(1, callFloat3.templateParametersEdges!![1].getProperty(Properties.INDEX)) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - callFloat3.templateParametersPropertyEdge!![1].getProperty(Properties.INSTANTIATION) + callFloat3.templateParametersEdges!![1].getProperty(Properties.INSTANTIATION) ) } @@ -556,33 +550,23 @@ internal class FunctionTemplateTest : BaseTest() { assertEquals(1, callExpression.invokes.size) assertEquals(methodDeclaration, callExpression.invokes[0]) assertEquals(templateDeclaration, callExpression.templateInstantiation) - assertEquals(2, callExpression.templateParameters!!.size) - assertEquals("int", callExpression.templateParameters!![0].name) + assertEquals(2, callExpression.templateParameters.size) + assertEquals("int", callExpression.templateParameters[0].name) assertEquals( TemplateDeclaration.TemplateInitialization.EXPLICIT, - callExpression.templateParametersPropertyEdge - ?.get(0) - ?.getProperty(Properties.INSTANTIATION) - ) - assertEquals( - 0, - callExpression.templateParametersPropertyEdge!![0].getProperty(Properties.INDEX) + callExpression.templateParametersEdges?.get(0)?.getProperty(Properties.INSTANTIATION) ) + assertEquals(0, callExpression.templateParametersEdges!![0].getProperty(Properties.INDEX)) val int5: Literal = findByUniquePredicate( flattenListIsInstance(result), Predicate { l: Literal<*> -> l.value == 5 } ) assertEquals(int5, callExpression.templateParameters!![1]) - assertEquals( - 1, - callExpression.templateParametersPropertyEdge!![1].getProperty(Properties.INDEX) - ) + assertEquals(1, callExpression.templateParametersEdges!![1].getProperty(Properties.INDEX)) assertEquals( TemplateDeclaration.TemplateInitialization.DEFAULT, - callExpression.templateParametersPropertyEdge!! - .get(1) - .getProperty(Properties.INSTANTIATION) + callExpression.templateParametersEdges!!.get(1).getProperty(Properties.INSTANTIATION) ) } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXCompilationDatabaseTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXCompilationDatabaseTest.kt index 1461d15b4b..415a22436b 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXCompilationDatabaseTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXCompilationDatabaseTest.kt @@ -72,15 +72,15 @@ class CXXCompilationDatabaseTest { val s1 = mainFunc.getBodyStatementAs(1, CallExpression::class.java) assertNotNull(s1) - assertEquals(s1.invokes.iterator().next(), func1) + assertEquals(func1, s1.invokes.iterator().next()) val s2 = mainFunc.getBodyStatementAs(2, CallExpression::class.java) assertNotNull(s2) - assertEquals(s2.invokes.iterator().next(), func2) + assertEquals(func2, s2.invokes.iterator().next()) val s3 = mainFunc.getBodyStatementAs(3, CallExpression::class.java) assertNotNull(s3) - assertEquals(s3.invokes.iterator().next(), sysFunc) + assertEquals(sysFunc, s3.invokes.iterator().next()) val s4 = mainFunc.getBodyStatementAs(4, Literal::class.java) assertNotNull(s4) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.kt index e700fee2eb..988c966f13 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.kt @@ -371,7 +371,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { // 4th statement is not yet parsed correctly val memberCallExpression = statements[4] as MemberCallExpression - assertEquals("test", memberCallExpression.base.name) + assertEquals("test", memberCallExpression.base?.name) assertEquals("c_str", memberCallExpression.name) } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXResolveTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXResolveTest.kt index 3c84b16bd4..b297b04a2d 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXResolveTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXResolveTest.kt @@ -55,7 +55,7 @@ class CXXResolveTest { val aFoo = main.bodyOrNull(0) assertNotNull(aFoo) assertEquals("foo", aFoo.name) - assertEquals("a", aFoo.base.name) + assertEquals("a", aFoo.base?.name) // a.foo should connect to A::foo assertEquals( "A", @@ -65,7 +65,7 @@ class CXXResolveTest { val bFoo = main.bodyOrNull(1) assertNotNull(bFoo) assertEquals("foo", bFoo.name) - assertEquals("b", bFoo.base.name) + assertEquals("b", bFoo.base?.name) // b.foo should connect to B::foo assertEquals( "B", diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypescriptLanguageFrontendTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypescriptLanguageFrontendTest.kt index fc42b4cc80..0f640d3a19 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypescriptLanguageFrontendTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypescriptLanguageFrontendTest.kt @@ -187,7 +187,7 @@ class TypescriptLanguageFrontendTest { assertNotNull(preventDefault) assertEquals("preventDefault", preventDefault.name) - assertEquals("event", preventDefault.base.name) + assertEquals("event", preventDefault.base?.name) val apiUrl = function.getBodyStatementAs(1, DeclarationStatement::class.java)?.singleDeclaration diff --git a/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt b/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt index 57f7bc6da5..60a5a609b0 100644 --- a/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt +++ b/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt @@ -139,8 +139,8 @@ class PythonFrontendTest : BaseTest() { assertEquals("bar", callExpression.name) assertEquals(bar, callExpression.invokes.iterator().next()) - val edge = callExpression.argumentsPropertyEdge[1] - + val edge = callExpression.argumentsEdges[1] + assertNotNull(edge) assertEquals("s2", edge.getProperty(Properties.NAME)) val s = bar.parameters.first() @@ -498,7 +498,7 @@ class PythonFrontendTest : BaseTest() { assertEquals("Foo.bar", fooMemCall.fqn) assertEquals(1, fooMemCall.invokes.size) assertEquals(bar, fooMemCall.invokes[0]) - assertEquals("self", fooMemCall.base.name) + assertEquals("self", fooMemCall.base?.name) } @Test