diff --git a/cpg-all/build.gradle.kts b/cpg-all/build.gradle.kts index dca15330853..6818a2cd844 100644 --- a/cpg-all/build.gradle.kts +++ b/cpg-all/build.gradle.kts @@ -16,6 +16,12 @@ publishing { } } +repositories { + maven { + setUrl("https://jitpack.io") + } +} + dependencies { // this exposes all of our (published) modules as dependency api(projects.cpgConsole) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt index df8de34c7f4..73d7a20d225 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt @@ -507,7 +507,7 @@ private constructor( registerPass() registerPass() registerPass() - registerPass() + registerPass() useDefaultPasses = true return this } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt index 4709e6bece3..8745e7944d8 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt @@ -28,7 +28,6 @@ package de.fraunhofer.aisec.cpg import de.fraunhofer.aisec.cpg.frontends.CastNotPossible import de.fraunhofer.aisec.cpg.frontends.CastResult import de.fraunhofer.aisec.cpg.frontends.Language -import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.TemplateDeclaration import de.fraunhofer.aisec.cpg.graph.scopes.Scope @@ -140,8 +139,7 @@ class TypeManager { val node = scope.astNode // We need an additional check here, because of parsing or other errors, the AST node - // might - // not necessarily be a template declaration. + // might not necessarily be a template declaration. if (node is TemplateDeclaration) { val parameterizedType = getTypeParameter(node, name) if (parameterizedType != null) { @@ -224,12 +222,9 @@ class TypeManager { return t } - fun typeExists(name: String): Boolean { - return firstOrderTypes.any { type: Type -> type.root.name.toString() == name } - } - - fun typeExists(name: Name): Type? { - return firstOrderTypes.firstOrNull { type: Type -> type.root.name == name } + /** Checks, whether a [Type] with the given [name] exists. */ + fun typeExists(name: CharSequence): Boolean { + return firstOrderTypes.any { type: Type -> type.root.name == name } } fun resolvePossibleTypedef(alias: Type, scopeManager: ScopeManager): Type { @@ -243,7 +238,7 @@ class TypeManager { * is [Type.Origin.RESOLVED]. */ fun lookupResolvedType( - fqn: String, + fqn: CharSequence, generics: List? = null, language: Language<*>? = null ): Type? { @@ -254,7 +249,7 @@ class TypeManager { return firstOrderTypes.firstOrNull { (it.typeOrigin == Type.Origin.RESOLVED || it.typeOrigin == Type.Origin.GUESSED) && - it.name.toString() == fqn && + it.root.name == fqn && if (generics != null) { (it as? ObjectType)?.generics == generics } else { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt index 9a78528f348..e12386d36fd 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt @@ -120,7 +120,7 @@ abstract class Language> : Node() { * [builtInTypes] map, it returns null. The [typeString] must precisely match the key in the * map. */ - fun getSimpleTypeOf(typeString: String) = builtInTypes[typeString] + fun getSimpleTypeOf(typeString: CharSequence) = builtInTypes[typeString.toString()] /** Returns true if the [file] can be handled by the frontend of this language. */ fun handlesFile(file: File): Boolean { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt index dafc021df0c..b7a66ac6bf4 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt @@ -193,13 +193,32 @@ interface HasAnonymousIdentifier : LanguageTrait { */ interface HasGlobalVariables : LanguageTrait +/** + * A common super-class for all language traits that arise because they are an ambiguity of a + * function call, e.g., function-style casts. This means that we cannot differentiate between a + * [CallExpression] and other expressions during the frontend and we need to invoke the + * [ResolveCallExpressionAmbiguityPass] to resolve this. + */ +sealed interface HasCallExpressionAmbiguity : LanguageTrait + /** * A language trait, that specifies that the language has so-called functional style casts, meaning * that they look like regular call expressions. Since we can therefore not distinguish between a * [CallExpression] and a [CastExpression], we need to employ an additional pass - * ([ReplaceCallCastPass]) after the initial language frontends are done. + * ([ResolveCallExpressionAmbiguityPass]) after the initial language frontends are done. + */ +interface HasFunctionStyleCasts : HasCallExpressionAmbiguity + +/** + * A language trait, that specifies that the language has functional style (object) construction, + * meaning that constructor calls look like regular call expressions (usually meaning that the + * language has no dedicated `new` keyword). + * + * Since we can therefore not distinguish between a [CallExpression] and a [ConstructExpression] in + * the frontend, we need to employ an additional pass ([ResolveCallExpressionAmbiguityPass]) after + * the initial language frontends are done. */ -interface HasFunctionalCasts : LanguageTrait +interface HasFunctionStyleConstruction : HasCallExpressionAmbiguity /** * A language trait that specifies that this language allowed overloading functions, meaning that diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Name.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Name.kt index 1cf9d66218d..5109806ba22 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Name.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Name.kt @@ -70,11 +70,13 @@ class Name( override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is Name) return false + if (other is String) return this.fullName == other + if (other is Name) + return localName == other.localName && + parent == other.parent && + delimiter == other.delimiter - return localName == other.localName && - parent == other.parent && - delimiter == other.delimiter + return false } override fun get(index: Int) = fullName[index] @@ -153,9 +155,9 @@ internal fun parseName(fqn: CharSequence, delimiter: String, vararg splitDelimit } /** Returns a new [Name] based on the [localName] and the current name as parent. */ -fun Name?.fqn(localName: String) = +fun Name?.fqn(localName: String, delimiter: String = this?.delimiter ?: ".") = if (this == null) { - Name(localName) + Name(localName, null, delimiter) } else { - Name(localName, this, this.delimiter) + Name(localName, this, delimiter) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceCallCastPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ResolveCallExpressionAmbiguityPass.kt similarity index 58% rename from cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceCallCastPass.kt rename to cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ResolveCallExpressionAmbiguityPass.kt index 5786b3609ba..31ce5c5d227 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceCallCastPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ResolveCallExpressionAmbiguityPass.kt @@ -27,12 +27,16 @@ package de.fraunhofer.aisec.cpg.passes import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.Handler -import de.fraunhofer.aisec.cpg.frontends.HasFunctionalCasts +import de.fraunhofer.aisec.cpg.frontends.HasCallExpressionAmbiguity +import de.fraunhofer.aisec.cpg.frontends.HasFunctionStyleCasts +import de.fraunhofer.aisec.cpg.frontends.HasFunctionStyleConstruction import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.graph.* +import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.* +import de.fraunhofer.aisec.cpg.graph.types.ObjectType import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn @@ -40,17 +44,17 @@ import de.fraunhofer.aisec.cpg.passes.configuration.ExecuteBefore import de.fraunhofer.aisec.cpg.passes.configuration.RequiresLanguageTrait /** - * If a [Language] has the trait [HasFunctionalCasts], we cannot distinguish between a - * [CallExpression] and a [CastExpression] during the initial translation. This stems from the fact - * that we might not know all the types yet. We therefore need to handle them as regular call - * expression in a [LanguageFrontend] or [Handler] and then later replace them with a - * [CastExpression], if the [CallExpression.callee] refers to name of a [Type] rather than a - * function. + * If a [Language] has the trait [HasCallExpressionAmbiguity], we cannot distinguish between + * [CallExpression], [CastExpression] or [ConstructExpression] during the initial translation. This + * stems from the fact that we might not know all the types yet. We therefore need to handle them as + * regular call expression in a [LanguageFrontend] or [Handler] and then later replace them with a + * [CastExpression] or [ConstructExpression], if the [CallExpression.callee] refers to name of a + * [Type] / [RecordDeclaration] rather than a function. */ @ExecuteBefore(EvaluationOrderGraphPass::class) @DependsOn(TypeResolver::class) -@RequiresLanguageTrait(HasFunctionalCasts::class) -class ReplaceCallCastPass(ctx: TranslationContext) : TranslationUnitPass(ctx) { +@RequiresLanguageTrait(HasCallExpressionAmbiguity::class) +class ResolveCallExpressionAmbiguityPass(ctx: TranslationContext) : TranslationUnitPass(ctx) { private lateinit var walker: SubgraphWalker.ScopedWalker override fun accept(tu: TranslationUnitDeclaration) { @@ -71,13 +75,44 @@ class ReplaceCallCastPass(ctx: TranslationContext) : TranslationUnitPass(ctx) { return } + // We really need a parent, otherwise we cannot replace the node + if (parent == null) { + return + } + + // Some local copies for easier smart casting + var callee = call.callee + val language = callee.language + + // Check, if this is cast is really a construct expression (if the language supports + // functional-constructs) + if (language is HasFunctionStyleConstruction) { + // Make sure, we do not accidentally "construct" primitive types + if (language.builtInTypes.contains(callee.name.toString()) == true) { + return + } + + val fqn = + if (callee.name.parent == null) { + scopeManager.currentNamespace.fqn( + callee.name.localName, + delimiter = callee.name.delimiter + ) + } else { + callee.name + } + + // Check for our type. We are only interested in object types + val type = typeManager.lookupResolvedType(fqn) + if (type is ObjectType) { + walker.replaceCallWithConstruct(type, parent, call) + } + } + // We need to check, whether the "callee" refers to a type and if yes, convert it into a // cast expression. And this is only really necessary, if the function call has a single // argument. - var callee = call.callee - if (parent != null && call.arguments.size == 1) { - val language = parent.language - + if (language is HasFunctionStyleCasts && call.arguments.size == 1) { var pointer = false // If the argument is a UnaryOperator, unwrap them if (callee is UnaryOperator && callee.operatorCode == "*") { @@ -86,20 +121,25 @@ class ReplaceCallCastPass(ctx: TranslationContext) : TranslationUnitPass(ctx) { } // First, check if this is a built-in type - if (language?.builtInTypes?.contains(callee.name.toString()) == true) { - walker.replaceCallWithCast(callee.name.toString(), parent, call, false) + var builtInType = language.getSimpleTypeOf(callee.name) + if (builtInType != null) { + walker.replaceCallWithCast(builtInType, parent, call, false) } else { // If not, then this could still refer to an existing type. We need to make sure // that we take the current namespace into account val fqn = if (callee.name.parent == null) { - scopeManager.currentNamespace.fqn(callee.name.localName) + scopeManager.currentNamespace.fqn( + callee.name.localName, + delimiter = callee.name.delimiter + ) } else { callee.name } - if (typeManager.typeExists(fqn.toString())) { - walker.replaceCallWithCast(fqn, parent, call, pointer) + val type = typeManager.lookupResolvedType(fqn) + if (type != null) { + walker.replaceCallWithCast(type, parent, call, pointer) } } } @@ -112,7 +152,7 @@ class ReplaceCallCastPass(ctx: TranslationContext) : TranslationUnitPass(ctx) { context(ContextProvider) fun SubgraphWalker.ScopedWalker.replaceCallWithCast( - typeName: CharSequence, + type: Type, parent: Node, call: CallExpression, pointer: Boolean, @@ -123,9 +163,9 @@ fun SubgraphWalker.ScopedWalker.replaceCallWithCast( cast.location = call.location cast.castType = if (pointer) { - call.objectType(typeName).pointer() + type.pointer() } else { - call.objectType(typeName) + type } cast.expression = call.arguments.single() cast.name = cast.castType.name @@ -133,6 +173,24 @@ fun SubgraphWalker.ScopedWalker.replaceCallWithCast( replaceArgument(parent, call, cast) } +context(ContextProvider) +fun SubgraphWalker.ScopedWalker.replaceCallWithConstruct( + type: ObjectType, + parent: Node, + call: CallExpression +) { + val construct = newConstructExpression() + construct.code = call.code + construct.language = call.language + construct.location = call.location + construct.callee = call.callee + (construct.callee as? Reference)?.resolutionHelper = construct + construct.arguments = call.arguments + construct.type = type + + replaceArgument(parent, call, construct) +} + context(ContextProvider) fun SubgraphWalker.ScopedWalker.replaceArgument(parent: Node?, old: Expression, new: Expression) { if (parent !is ArgumentHolder) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt index cdb72aba486..cd515cd463b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt @@ -932,7 +932,7 @@ fun TranslationContext.tryRecordInference( // At this point, we need to check whether we have any type reference to our parent // name. If we have (e.g. it is used in a function parameter, variable, etc.), then we // have a high chance that this is actually a parent record and not a namespace - var parentType = typeManager.typeExists(parentName) + var parentType = typeManager.lookupResolvedType(parentName) holder = if (parentType != null) { tryRecordInference(parentType, locationHint = locationHint) diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt index 56e0e975c81..ea6bd14e303 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt @@ -50,7 +50,7 @@ open class CPPLanguage : HasStructs, HasClasses, HasUnknownType, - HasFunctionalCasts, + HasFunctionStyleCasts, HasFunctionOverloading, HasOperatorOverloading { override val fileExtensions = listOf("cpp", "cc", "cxx", "c++", "hpp", "hh") diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXExtraPass.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXExtraPass.kt index c419b1321ad..28559258db5 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXExtraPass.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXExtraPass.kt @@ -45,7 +45,7 @@ import de.fraunhofer.aisec.cpg.passes.configuration.ExecuteBefore * type information. */ @ExecuteBefore(EvaluationOrderGraphPass::class) -@ExecuteBefore(ReplaceCallCastPass::class) +@ExecuteBefore(ResolveCallExpressionAmbiguityPass::class) @DependsOn(TypeResolver::class) class CXXExtraPass(ctx: TranslationContext) : ComponentPass(ctx) { @@ -76,7 +76,7 @@ class CXXExtraPass(ctx: TranslationContext) : ComponentPass(ctx) { * the graph. */ private fun removeBracketOperators(node: UnaryOperator, parent: Node?) { - if (node.operatorCode == "()" && !typeManager.typeExists(node.input.name.toString())) { + if (node.operatorCode == "()" && !typeManager.typeExists(node.input.name)) { // It was really just parenthesis around an identifier, but we can only make this // distinction now. // @@ -92,9 +92,10 @@ class CXXExtraPass(ctx: TranslationContext) : ComponentPass(ctx) { * operator where some arguments are wrapped in parentheses. This function tries to resolve * this. * - * Note: This is done especially for the C++ frontend. [ReplaceCallCastPass.handleCall] handles - * the more general case (which also applies to C++), in which a cast and a call are - * indistinguishable and need to be resolved once all types are known. + * Note: This is done especially for the C++ frontend. + * [ResolveCallExpressionAmbiguityPass.handleCall] handles the more general case (which also + * applies to C++), in which a cast and a call are indistinguishable and need to be resolved + * once all types are known. */ private fun convertOperators(binOp: BinaryOperator, parent: Node?) { val fakeUnaryOp = binOp.lhs @@ -107,7 +108,7 @@ class CXXExtraPass(ctx: TranslationContext) : ComponentPass(ctx) { language != null && fakeUnaryOp is UnaryOperator && fakeUnaryOp.operatorCode == "()" && - typeManager.typeExists(fakeUnaryOp.input.name.toString()) + typeManager.typeExists(fakeUnaryOp.input.name) ) { // If the name (`long` in the example) is a type, then the unary operator (`(long)`) // is really a cast and our binary operator is really a unary operator `&addr`. diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt index 9dda501c575..b1f17ca3bd6 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt @@ -42,7 +42,7 @@ class GoLanguage : HasStructs, HasFirstClassFunctions, HasAnonymousIdentifier, - HasFunctionalCasts { + HasFunctionStyleCasts { override val fileExtensions = listOf("go") override val namespaceDelimiter = "." @Transient override val frontend = GoLanguageFrontend::class diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/ExpressionHandler.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/ExpressionHandler.kt index 263f2095c17..06ac7009d89 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/ExpressionHandler.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/ExpressionHandler.kt @@ -28,9 +28,7 @@ package de.fraunhofer.aisec.cpg.frontends.python import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.ImportDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration -import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression -import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression -import de.fraunhofer.aisec.cpg.graph.statements.expressions.ProblemExpression +import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import jep.python.PyObject class ExpressionHandler(frontend: PythonLanguageFrontend) : @@ -350,20 +348,7 @@ class ExpressionHandler(frontend: PythonLanguageFrontend) : if (callee is MemberExpression) { newMemberCallExpression(callee, rawNode = node) } else { - // try to resolve -> [ConstructExpression] - val currentScope = frontend.scopeManager.currentScope - val record = - currentScope?.let { frontend.scopeManager.getRecordForName(callee.name) } - - if (record != null) { - // construct expression - val constructExpr = - newConstructExpression((node.func as? Python.AST.Name)?.id, rawNode = node) - constructExpr.type = record.toType() - constructExpr - } else { - newCallExpression(callee, rawNode = node) - } + newCallExpression(callee, rawNode = node) } for (arg in node.args) { diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/Python.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/Python.kt index a33f9da7bcf..91899a97766 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/Python.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/Python.kt @@ -59,8 +59,8 @@ interface Python { interface AST { /** - * `ast.stmt` [AST.BaseStmt] and `ast.expr` [AST.BaseExpr] nodes have extra location - * properties as implemented here. + * Some nodes, such as `ast.stmt` [AST.BaseStmt] and `ast.expr` [AST.BaseExpr] nodes have + * extra location properties as implemented here. */ interface WithLocation { // TODO make the fields accessible `by lazy` val pyObject: PyObject @@ -1130,7 +1130,7 @@ interface Python { * | | MatchOr(pattern* patterns) * ``` */ - abstract class BasePattern(pyObject: PyObject) : AST(pyObject) + abstract class BasePattern(pyObject: PyObject) : AST(pyObject), WithLocation /** * ``` @@ -1266,7 +1266,7 @@ interface Python { * | alias(identifier name, identifier? asname) * ``` */ - class alias(pyObject: PyObject) : AST(pyObject) { + class alias(pyObject: PyObject) : AST(pyObject), WithLocation { val name: String by lazy { "name" of pyObject } val asname: String? by lazy { "asname" of pyObject } } @@ -1320,7 +1320,7 @@ interface Python { * * TODO: excepthandler <-> ExceptHandler */ - class excepthandler(pyObject: PyObject) : AST(pyObject) { + class excepthandler(pyObject: PyObject) : AST(pyObject), WithLocation { val type: BaseExpr by lazy { "type" of pyObject } val name: String by lazy { "name" of pyObject } val body: kotlin.collections.List by lazy { "body" of pyObject } @@ -1332,7 +1332,7 @@ interface Python { * | keyword(identifier? arg, expr value) * ``` */ - class keyword(pyObject: PyObject) : AST(pyObject) { + class keyword(pyObject: PyObject) : AST(pyObject), WithLocation { val arg: String? by lazy { "arg" of pyObject } val value: BaseExpr by lazy { "value" of pyObject } } diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt index 497579aa9bd..2c79b94ee51 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt @@ -25,6 +25,7 @@ */ package de.fraunhofer.aisec.cpg.frontends.python +import de.fraunhofer.aisec.cpg.frontends.HasFunctionStyleConstruction import de.fraunhofer.aisec.cpg.frontends.HasOperatorOverloading import de.fraunhofer.aisec.cpg.frontends.HasShortCircuitOperators import de.fraunhofer.aisec.cpg.frontends.Language @@ -41,7 +42,10 @@ import org.neo4j.ogm.annotation.Transient /** The Python language. */ class PythonLanguage : - Language(), HasShortCircuitOperators, HasOperatorOverloading { + Language(), + HasShortCircuitOperators, + HasOperatorOverloading, + HasFunctionStyleConstruction { override val fileExtensions = listOf("py", "pyi") override val namespaceDelimiter = "." @Transient 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 c23865dd34f..b99f5346bbd 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 @@ -479,7 +479,7 @@ class PythonFrontendTest : BaseTest() { assertNotNull(line2) assertEquals(1, line1.declarations.size) - val fooDecl = line1.declarations[0] as? VariableDeclaration + val fooDecl = line1.declarations[0] assertNotNull(fooDecl) assertLocalName("foo", fooDecl) assertFullName("class_ctor.Foo", fooDecl.type)