diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt index 4b3635a408..13828cdf83 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt @@ -26,6 +26,7 @@ package de.fraunhofer.aisec.cpg import de.fraunhofer.aisec.cpg.frontends.* +import de.fraunhofer.aisec.cpg.frontends.HasFirstClassFunctions import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.scopes.* @@ -89,8 +90,7 @@ class ScopeManager : ScopeProvider { * In reality, they can probably be defined at different scopes for other languages, but for now * we only allow it for the current file. * - * This can potentially be used to replace [addTypedef] at some point, which still relies on the - * existence of a [LanguageFrontend]. + * This can potentially be merged with [addTypedef] at some point. */ private val aliases = mutableMapOf>() @@ -574,15 +574,13 @@ class ScopeManager : ScopeProvider { } } - /** Only used by the [TypeManager], adds typedefs to the current [ValueDeclarationScope]. */ - fun addTypedef(typedef: TypedefDeclaration) { - val scope = this.firstScopeIsInstanceOrNull() - if (scope == null) { - LOGGER.error("Cannot add typedef. Not in declaration scope.") - return - } - - scope.addTypedef(typedef) + /** + * Adds typedefs to a [ValueDeclarationScope]. The language frontend needs to decide on the + * scope of the typedef. Most likely, typedefs are global. Therefore, the [GlobalScope] is set + * as default. + */ + fun addTypedef(typedef: TypedefDeclaration, scope: ValueDeclarationScope? = globalScope) { + scope?.addTypedef(typedef) } private fun getCurrentTypedefs(searchScope: Scope?): Collection { @@ -968,9 +966,43 @@ class ScopeManager : ScopeProvider { // a local definition overwrites / shadows one that was there on a higher scope. while (current != null) { if (current is ValueDeclarationScope) { - val decl = current.typedefs[alias] - if (decl != null) { - return decl.type + // This is a little bit of a hack to support partial FQN resolution at least with + // typedefs, but its not really ideal. + // And this also should be merged with the scope manager logic when resolving names. + // + // The better approach would be to harmonize the FQN of all types in one pass before + // all this happens. + // + // This process has several steps: + // First, do a quick local lookup, to see if we have a typedef our current scope + // (only do this if the name is not qualified) + if (!alias.name.isQualified() && current == currentScope) { + var decl = current.typedefs[alias] + if (decl != null) { + return decl.type + } + } + + // Next, try to look up the name either by its FQN (if it is qualified) or make it + // qualified based on the current namespace + val key = + current.typedefs.keys.firstOrNull { + var lookupName = alias.name + + // If the lookup name is already a FQN, we can use the name directly + lookupName = + if (lookupName.isQualified()) { + lookupName + } else { + // Otherwise, we want to make an FQN out of it using the current + // namespace + currentNamespace?.fqn(lookupName.localName) ?: lookupName + } + + it.name.lastPartsMatch(lookupName) + } + if (key != null) { + return current.typedefs[key]?.type } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt index 3d7636ee12..b65fb66ff2 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt @@ -54,4 +54,9 @@ class TranslationContext( * the [TranslationResult.finalCtx] this may either be null or the last component analyzed. */ var currentComponent: Component? = null, + + // TODO: move somewhere better or maybe make translation context the statistics holder instead + // of the result? + var inferredFunctions: Int = 0, + var inferredRecords: Int = 0 ) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt index 47269641e8..0bf785c897 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt @@ -31,6 +31,9 @@ import de.fraunhofer.aisec.cpg.frontends.SupportsParallelParsing import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.Component import de.fraunhofer.aisec.cpg.graph.Name +import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration +import de.fraunhofer.aisec.cpg.graph.nodes +import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.helpers.Benchmark import de.fraunhofer.aisec.cpg.passes.* import java.io.File @@ -73,7 +76,7 @@ private constructor( var executedFrontends = setOf>() // Build a new global translation context - val ctx = TranslationContext(config, ScopeManager(), TypeManager()) + val ctx = TranslationContext(config, ScopeManager(), TypeManager(), null) // Build a new translation result val result = TranslationResult(this, ctx) @@ -115,6 +118,20 @@ private constructor( } } + // Print some graph statistics + val nodes = result.nodes + val functions = nodes.filterIsInstance() + val records = nodes.filterIsInstance() + val calls = nodes.filterIsInstance() + + log.info("Total graph nodes: ${nodes.size} ") + log.info("Function nodes: ${functions.size}") + log.info("Record nodes: ${records.size}") + log.info("Call nodes: ${calls.size}") + + log.info("Inferred functions: ${ctx.inferredFunctions}") + log.info("Inferred records: ${ctx.inferredRecords}") + return result } 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 7a20358d20..38706d60cc 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,12 +28,9 @@ 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.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.graph.* -import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.TemplateDeclaration -import de.fraunhofer.aisec.cpg.graph.declarations.TypedefDeclaration import de.fraunhofer.aisec.cpg.graph.scopes.Scope import de.fraunhofer.aisec.cpg.graph.scopes.TemplateScope import de.fraunhofer.aisec.cpg.graph.types.* @@ -231,26 +228,6 @@ class TypeManager { return firstOrderTypes.stream().anyMatch { type: Type -> type.root.name.toString() == name } } - /** - * Creates a typedef / type alias in the form of a [TypedefDeclaration] to the scope manager and - * returns it. - * - * @param frontend the language frontend - * @param rawCode the raw code - * @param target the target type - * @param alias the alias type - * @return the typedef declaration - */ - fun createTypeAlias( - frontend: LanguageFrontend<*, *>, - target: Type, - alias: Type, - ): Declaration { - val typedef = frontend.newTypedefDeclaration(target, alias) - frontend.scopeManager.addTypedef(typedef) - return typedef - } - fun resolvePossibleTypedef(alias: Type, scopeManager: ScopeManager): Type { val finalToCheck = alias.root val applicable = scopeManager.typedefFor(finalToCheck) @@ -417,8 +394,7 @@ fun Type.wrap(wrapState: WrapState): Type { } if (wrapState.isReference) { - wrapState.referenceType?.elementType = type - return wrapState.referenceType!! + return ReferenceType(this) } return type 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 71d528b9af..700c879f76 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 @@ -342,11 +342,19 @@ abstract class Language> : Node() { // TODO: Move this code somewhere else once we have a proper template expansion pass // We need to check, whether this language has special handling of templates. In this - // case, we need to check, whether a template matches after we have no direct matches + // case, we need to check, whether a template matches directly after we have no direct + // matches if (this is HasTemplates) { result.call.templateParameterEdges = mutableListOf() val (ok, candidates) = - this.handleTemplateFunctionCalls(null, result.call, false, result.call.ctx!!, null) + this.handleTemplateFunctionCalls( + null, + result.call, + false, + result.call.ctx!!, + null, + needsExactMatch = true + ) if (ok) { return Pair(candidates.toSet(), CallResolutionResult.SuccessKind.SUCCESSFUL) } 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 c9e955ead6..3f85b97642 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 @@ -74,7 +74,8 @@ interface HasTemplates : HasGenerics { templateCall: CallExpression, applyInference: Boolean, ctx: TranslationContext, - currentTU: TranslationUnitDeclaration? + currentTU: TranslationUnitDeclaration?, + needsExactMatch: Boolean ): Pair> } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt index 4b44a56a8d..cf7e12e01e 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt @@ -322,8 +322,8 @@ fun T.codeAndLocationFromChildren(parentNode: AstNode): T { first, current, compareBy( - { it.location?.region?.startLine }, - { it.location?.region?.startColumn } + { it?.location?.region?.startLine }, + { it?.location?.region?.startColumn } ) ) last = diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/IncludeDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/IncludeDeclaration.kt index db66652ebf..9e2eb140bc 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/IncludeDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/IncludeDeclaration.kt @@ -94,5 +94,5 @@ class IncludeDeclaration : Declaration() { filename == other.filename) } - override fun hashCode() = Objects.hash(super.hashCode(), includes, problems, filename) + override fun hashCode() = Objects.hash(super.hashCode(), problems, filename) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ParameterDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ParameterDeclaration.kt index d515eaf588..a7ca01e17b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ParameterDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ParameterDeclaration.kt @@ -39,6 +39,8 @@ class ParameterDeclaration : ValueDeclaration(), HasDefault { @AST private var defaultValue: Expression? = null + var modifiers: List = mutableListOf() + override var default: Expression? get() = defaultValue set(value) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt index ca8b592b8f..5af9e6dd71 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt @@ -44,6 +44,7 @@ class CastExpression : Expression(), ArgumentHolder, HasType.TypeObserver { set(value) { field = value type = value + name = value.name } fun setCastOperator(operatorCode: Int) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt index c13474224f..02dfd07864 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt @@ -72,8 +72,19 @@ class ConditionalExpression : Expression(), ArgumentHolder, BranchingNode, HasTy } override fun replaceArgument(old: Expression, new: Expression): Boolean { - // Do nothing - return false + return when (old) { + thenExpression -> { + thenExpression = new + true + } + elseExpression -> { + elseExpression = new + true + } + else -> { + false + } + } } override fun typeChanged(newType: Type, src: HasType) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ObjectType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ObjectType.kt index 85571b78b1..d7d364b77c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ObjectType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ObjectType.kt @@ -101,10 +101,6 @@ open class ObjectType : Type { return unknownType() } - override fun isSimilar(t: Type?): Boolean { - return t is ObjectType && generics == t.generics && super.isSimilar(t) - } - override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is ObjectType) return false diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt index 44171e159c..2749e40ac0 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt @@ -26,7 +26,7 @@ package de.fraunhofer.aisec.cpg.helpers import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend -import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration import de.fraunhofer.aisec.cpg.graph.edge.CallingContextIn diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt index 16e6e1b576..30143ba214 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt @@ -25,11 +25,16 @@ */ package de.fraunhofer.aisec.cpg.passes +import de.fraunhofer.aisec.cpg.frontends.CastNotPossible +import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.* import de.fraunhofer.aisec.cpg.isDerivedFrom +import de.fraunhofer.aisec.cpg.tryCast +import de.fraunhofer.aisec.cpg.wrap +import de.fraunhofer.aisec.cpg.wrapState import java.util.HashMap import java.util.regex.Pattern @@ -239,13 +244,13 @@ fun getTemplateInitializationSignature( val typeExpression = templateCall.newTypeExpression(deducedType.name, deducedType) typeExpression.isImplicit = true if ( - currentArgumentType is ParameterizedType && - (signature[parameterizedTypeResolution[currentArgumentType]] == null || + currentArgumentType.root is ParameterizedType && + (signature[parameterizedTypeResolution[currentArgumentType.root]] == null || (instantiationType[ - signature[parameterizedTypeResolution[currentArgumentType]]] == + signature[parameterizedTypeResolution[currentArgumentType.root]]] == TemplateDeclaration.TemplateInitialization.DEFAULT)) ) { - signature[parameterizedTypeResolution[currentArgumentType]] = typeExpression + signature[parameterizedTypeResolution[currentArgumentType.root]] = typeExpression instantiationType[typeExpression] = TemplateDeclaration.TemplateInitialization.AUTO_DEDUCTION } @@ -392,16 +397,15 @@ fun getCallSignature( ): List { val templateCallSignature = mutableListOf() for (argument in function.parameters) { - if (argument.type is ParameterizedType) { - var type: Type = UnknownType.getUnknownType(function.language) - val typeParamDeclaration = parameterizedTypeResolution[argument.type] - if (typeParamDeclaration != null) { - val node = initializationSignature[typeParamDeclaration] - if (node is TypeExpression) { - type = node.type - } - } - templateCallSignature.add(type) + if (argument.type.root is ParameterizedType) { + templateCallSignature.add( + realizeType( + function.language, + parameterizedTypeResolution, + argument.type, + initializationSignature + ) + ) } else { templateCallSignature.add(argument.type) } @@ -409,6 +413,28 @@ fun getCallSignature( return templateCallSignature } +private fun realizeType( + language: Language<*>?, + parameterizedTypeResolution: Map, + incomingType: Type, + initializationSignature: Map +): Type { + var type: Type = UnknownType.getUnknownType(language) + + val typeParamDeclaration = parameterizedTypeResolution[incomingType.root] + if (typeParamDeclaration != null) { + val node = initializationSignature[typeParamDeclaration] + if (node is TypeExpression) { + // We might need basically exchange the root node, and we can do this using a wrap state + val wrapState = incomingType.wrapState + val newType = node.type.wrap(wrapState) + + type = newType + } + } + return type +} + /** * @param functionDeclaration FunctionDeclaration realization of the template * @param functionDeclarationSignature Signature of the realization FunctionDeclaration, but @@ -422,8 +448,13 @@ fun checkArgumentValidity( functionDeclaration: FunctionDeclaration, functionDeclarationSignature: List, templateCallExpression: CallExpression, - explicitInstantiation: List + explicitInstantiation: List, + needsExactMatch: Boolean ): Boolean { + // We need to keep track of the original (template) arguments and double-check that we are not + // casting two parameterized types into two different arguments + val convertedTypes = mutableMapOf() + if (templateCallExpression.arguments.size <= functionDeclaration.parameters.size) { val callArguments = mutableListOf( @@ -439,14 +470,32 @@ fun checkArgumentValidity( ) // Extend by defaults for (i in callArguments.indices) { val callArgument = callArguments[i] ?: return false + + val originalType = functionDeclaration.parameters.getOrNull(i)?.type + + val notMatches = + callArgument.type.tryCast( + functionDeclarationSignature[i], + hint = callArgument, + targetHint = functionDeclaration.parameters[i] + ) == CastNotPossible if ( - callArgument.type != functionDeclarationSignature[i] && + notMatches && !(callArgument.type.isPrimitive && functionDeclarationSignature[i].isPrimitive && functionDeclaration.parameters[i].type in explicitInstantiation) ) { return false } + + // Check, that we "convert" each parameterized type only into the same type once + if (originalType is ParameterizedType) { + val alreadyMatches = convertedTypes[originalType] + if (alreadyMatches != null && alreadyMatches != callArgument.type) { + return false + } + convertedTypes[originalType] = callArgument.type + } } return true } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt index 437199125b..ddc93cfa72 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt @@ -86,7 +86,7 @@ open class ControlFlowSensitiveDFGPass(ctx: TranslationContext) : EOGStarterPass return } - log.debug("Handling {} (complexity: {})", node.name, c) + log.trace("Handling {} (complexity: {})", node.name, c) clearFlowsOfVariableDeclarations(node) val startState = DFGPassState>() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt index c5db2943dc..be30069c07 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt @@ -157,6 +157,9 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa map[TypeIdExpression::class.java] = { handleDefault(it) } map[Reference::class.java] = { handleDefault(it) } map[LambdaExpression::class.java] = { handleLambdaExpression(it as LambdaExpression) } + map[TemplateDeclaration::class.java] = { + handleTemplateDeclaration(it as TemplateDeclaration) + } } protected fun doNothing() { @@ -301,6 +304,10 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa pushToEOG(node) } + private fun handleTemplateDeclaration(templateDeclaration: TemplateDeclaration) { + // Nothing to do? Should w go through the realizations? + } + protected open fun handleFunctionDeclaration(node: FunctionDeclaration) { // reset EOG currentPredecessors.clear() 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 f4e1cea3c3..316f39c246 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 @@ -638,7 +638,8 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { call, true, ctx, - currentTU + currentTU, + false ) candidates diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt index f7eda4086a..e83fa1f9ff 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt @@ -117,6 +117,8 @@ class Inference internal constructor(val start: Node, override val ctx: Translat signature.map { it?.name } ) + ctx.inferredFunctions++ + // Create parameter declarations and receiver (only for methods). if (inferred is MethodDeclaration) { createInferredReceiver(inferred, record) @@ -399,6 +401,8 @@ class Inference internal constructor(val start: Node, override val ctx: Translat "Inferred a new record declaration ${declaration.name} (${declaration.kind}) in $it" ) + ctx.inferredRecords++ + // Update the type type.recordDeclaration = declaration diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt index 0165071291..c0d55d2cd4 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt @@ -26,11 +26,19 @@ package de.fraunhofer.aisec.cpg.frontends.cxx import com.fasterxml.jackson.annotation.JsonIgnore +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.* +import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.ParameterDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration +import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.types.* import kotlin.reflect.KClass import org.neo4j.ogm.annotation.Transient +const val CONST = "const" + /** The C language. */ open class CLanguage : Language(), @@ -44,7 +52,7 @@ open class CLanguage : override val fileExtensions = listOf("c", "h") override val namespaceDelimiter = "::" @Transient override val frontend: KClass = CXXLanguageFrontend::class - override val qualifiers = listOf("const", "volatile", "restrict", "atomic") + override val qualifiers = listOf(CONST, "volatile", "restrict", "atomic") override val elaboratedTypeSpecifier = listOf("struct", "union", "enum") override val conjunctiveOperators = listOf("&&") override val disjunctiveOperators = listOf("||") @@ -148,6 +156,29 @@ open class CLanguage : return ImplicitCast } + // Another special rule is that if we have a const reference (e.g. const T&) in a function + // call, this will match the type T because this means that the parameter is given by + // reference rather than by value. + if ( + targetType is ReferenceType && + targetType.elementType == type && + targetHint is ParameterDeclaration && + CONST in targetHint.modifiers + ) { + return DirectMatch + } + return CastNotPossible } + + open fun handleTemplateFunctionCalls( + curClass: RecordDeclaration?, + templateCall: CallExpression, + applyInference: Boolean, + ctx: TranslationContext, + currentTU: TranslationUnitDeclaration?, + needsExactMatch: Boolean + ): Pair> { + TODO("Not yet implemented") + } } 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 f27703b484..b4fb0e8dcc 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 @@ -186,7 +186,8 @@ open class CPPLanguage : templateCall: CallExpression, applyInference: Boolean, ctx: TranslationContext, - currentTU: TranslationUnitDeclaration? + currentTU: TranslationUnitDeclaration?, + needsExactMatch: Boolean ): Pair> { val instantiationCandidates = ctx.scopeManager.resolveFunctionTemplateDeclaration(templateCall) @@ -222,7 +223,8 @@ open class CPPLanguage : initializationSignature ), templateCall, - explicitInstantiation + explicitInstantiation, + needsExactMatch ) ) { // Valid Target -> Apply invocation diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt index 5968163f3d..18acb52f59 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt @@ -97,6 +97,8 @@ open class CXXLanguageFrontend(language: Language, ctx: Tra */ private val includeFileContentProvider: IncludeFileContentProvider = object : InternalFileContentProvider() { + var cache = mutableMapOf() + /** * Returns the content of this path, without any cache. * @@ -121,9 +123,12 @@ open class CXXLanguageFrontend(language: Language, ctx: Tra LOGGER.debug("Include file {} not on the whitelist. Ignoring.", path) return null } - LOGGER.debug("Loading include file {}", path) - val content = FileContent.createForExternalFileLocation(path) - return content as? InternalFileContent + + return cache.computeIfAbsent(path) { + LOGGER.debug("Loading include file {}", path) + val content = FileContent.createForExternalFileLocation(path) + content + } as? InternalFileContent } private fun hasIncludeWhitelist(): Boolean { @@ -206,6 +211,9 @@ open class CXXLanguageFrontend(language: Language, ctx: Tra val symbols: HashMap = HashMap() symbols.putAll(config.symbols) + // We aim to behave like clang + symbols.put("__clang__", "") + includePaths.addAll(config.includePaths.map { it.toAbsolutePath().toString() }) config.compilationDatabase?.getIncludePaths(file)?.let { includePaths.addAll(it) } @@ -235,6 +243,7 @@ open class CXXLanguageFrontend(language: Language, ctx: Tra GCCLanguage.getDefault() } else { GPPLanguage.getDefault() + GPPLanguage() } val translationUnit = @@ -479,6 +488,14 @@ open class CXXLanguageFrontend(language: Language, ctx: Tra specifier: IASTDeclSpecifier, hint: Declaration? = null ): Type { + var type = typeOf(specifier, hint) + + type = this.adjustType(declarator, type) + + return type + } + + fun typeOf(specifier: IASTDeclSpecifier, hint: Declaration? = null): Type { // Retrieve the "name" of this type, including qualifiers. val name = ASTStringUtil.getSignatureString(specifier, null) @@ -547,8 +564,6 @@ open class CXXLanguageFrontend(language: Language, ctx: Tra } else { typeManager.registerType(type) } - type = this.adjustType(declarator, type) - return type } @@ -586,6 +601,19 @@ open class CXXLanguageFrontend(language: Language, ctx: Tra hint.name.localName == "operator#0" -> { hint.name.parent?.let { objectType(it) } ?: unknownType() } + // The type of conversion operator is also always the declaration itself + specifier.type == IASTSimpleDeclSpecifier.t_unspecified && + hint is MethodDeclaration && + hint.name.localName == "operator#0*" -> { + hint.name.parent?.let { objectType(it).pointer() } ?: unknownType() + } + // The type of destructor is unspecified, but we model it as a void type to make it + // compatible with other methods. + specifier.type == IASTSimpleDeclSpecifier.t_unspecified && + hint is MethodDeclaration && + hint.isDestructor -> { + incompleteType() + } // C (not C++) allows unspecified types in function declarations, they // default to int and usually produce a warning name == "" && language !is CPPLanguage -> { @@ -627,7 +655,7 @@ open class CXXLanguageFrontend(language: Language, ctx: Tra } } - fun typeOf(name: IASTName, prefix: String? = null): Type { + fun typeOf(name: IASTName, prefix: String? = null, doFqn: Boolean = false): Type { if (name is CPPASTQualifiedName) { val last = name.lastName if (last is CPPASTTemplateId) { @@ -656,7 +684,15 @@ open class CXXLanguageFrontend(language: Language, ctx: Tra return objectType(fqn, generics) } - return objectType(name.toString()) + + val typeName = + if (doFqn) { + scopeManager.currentNamespace?.fqn(name.toString()) ?: name.toString() + } else { + name.toString() + } + + return objectType(typeName) } /** @@ -671,9 +707,16 @@ open class CXXLanguageFrontend(language: Language, ctx: Tra // type into a pointer or similar for (op in declarator.pointerOperators) { type = - when (op) { - is IASTPointer -> type.pointer() - is ICPPASTReferenceOperator -> ReferenceType(type) + when { + op is IASTPointer -> type.pointer() + op is ICPPASTReferenceOperator && !op.isRValueReference -> ReferenceType(type) + // this is a little bit of a workaround until we re-design reference types, this + // is a && r-value reference used by move semantics in C++. This is actually + // just one level of reference (with a different operator), but for now we just + // make a double reference out of it to at least differentiate it from a & + // reference. + op is ICPPASTReferenceOperator && op.isRValueReference -> + ReferenceType(ReferenceType(type)) else -> type } } @@ -839,3 +882,12 @@ private val IASTSimpleDeclSpecifier.canonicalName: CharSequence return parts.joinToString(" ") } + +/** + * Returns whether this method is a + * [Destructor](https://en.cppreference.com/w/cpp/language/destructor). + */ +val MethodDeclaration.isDestructor: Boolean + get() { + return "~" + this.name.parent?.localName == this.name.localName + } diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt index 5a6e1048de..878752fcf1 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt @@ -28,6 +28,8 @@ package de.fraunhofer.aisec.cpg.frontends.cxx import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.scopes.NameScope +import de.fraunhofer.aisec.cpg.graph.scopes.RecordScope +import de.fraunhofer.aisec.cpg.graph.scopes.ValueDeclarationScope import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement import de.fraunhofer.aisec.cpg.graph.statements.Statement import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block @@ -567,8 +569,25 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : private fun handleTypedef(declarator: IASTDeclarator, type: Type): Declaration { val (nameDecl: IASTDeclarator, _) = declarator.realName() + // C/C++ behaves slightly different when it comes to typedefs in a function or in a record, + // such as a class or struct. A typedef in a function (or actually in any block scope) is + // scoped to the current block. A + // typedef in a record declaration is scoped to the global scope, but its alias name is + // FQN'd. + val (scope, doFqn) = + if (frontend.scopeManager.currentScope is RecordScope) { + Pair(frontend.scopeManager.globalScope, true) + } else if (frontend.scopeManager.currentScope is ValueDeclarationScope) { + Pair(frontend.scopeManager.currentScope as ValueDeclarationScope, false) + } else { + TODO() + } + // TODO(oxisto): What about namespaces? + val declaration = - frontend.typeManager.createTypeAlias(frontend, type, frontend.typeOf(nameDecl.name)) + frontend.newTypedefDeclaration(type, frontend.typeOf(nameDecl.name, doFqn = doFqn)) + + frontend.scopeManager.addTypedef(declaration, scope) // Add the declaration to the current scope frontend.scopeManager.addDeclaration(declaration) diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt index 2429978ca1..12e9c973b4 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt @@ -29,7 +29,9 @@ import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.* -import de.fraunhofer.aisec.cpg.graph.types.* +import de.fraunhofer.aisec.cpg.graph.types.FunctionType +import de.fraunhofer.aisec.cpg.graph.types.SecondOrderType +import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.helpers.Util import de.fraunhofer.aisec.cpg.passes.SymbolResolver.Companion.addImplicitTemplateParametersToCall import java.math.BigInteger @@ -40,11 +42,14 @@ import kotlin.math.pow import org.eclipse.cdt.core.dom.ast.* import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression.* import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression.* +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorInitializer import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLambdaExpression +import org.eclipse.cdt.core.dom.ast.gnu.IGNUASTCompoundStatementExpression import org.eclipse.cdt.internal.core.dom.parser.c.CASTArrayDesignator import org.eclipse.cdt.internal.core.dom.parser.c.CASTArrayRangeDesignator import org.eclipse.cdt.internal.core.dom.parser.c.CASTDesignatedInitializer import org.eclipse.cdt.internal.core.dom.parser.c.CASTFieldDesignator +import org.eclipse.cdt.internal.core.dom.parser.c.CASTTypeIdInitializerExpression import org.eclipse.cdt.internal.core.dom.parser.cpp.* import org.eclipse.cdt.internal.core.model.ASTStringUtil @@ -73,18 +78,66 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : ?: ProblemExpression("could not parse initializer list") is IASTArraySubscriptExpression -> handleArraySubscriptExpression(node) is IASTTypeIdExpression -> handleTypeIdExpression(node) + is IGNUASTCompoundStatementExpression -> handleCompoundStatementExpression(node) is CPPASTNewExpression -> handleNewExpression(node) is CPPASTDesignatedInitializer -> handleCXXDesignatedInitializer(node) is CASTDesignatedInitializer -> handleCDesignatedInitializer(node) + is CASTTypeIdInitializerExpression -> handleTypeIdInitializerExpression(node) is CPPASTDeleteExpression -> handleDeleteExpression(node) - is CPPASTCompoundStatementExpression -> handleCompoundStatementExpression(node) is CPPASTLambdaExpression -> handleLambdaExpression(node) + is CPPASTSimpleTypeConstructorExpression -> handleSimpleTypeConstructorExpression(node) else -> { return handleNotSupported(node, node.javaClass.name) } } } + /** + * This handles a [CPPASTSimpleTypeConstructorExpression], which handles all cases of + * [Explicit type conversion](https://en.cppreference.com/w/cpp/language/explicit_cast). + * Depending on the case, we either handle this as a [CastExpression] or a + * [ConstructExpression]. + */ + private fun handleSimpleTypeConstructorExpression( + node: CPPASTSimpleTypeConstructorExpression + ): Expression { + return if (node.declSpecifier is IASTSimpleDeclSpecifier) { + val cast = newCastExpression(rawNode = node) + cast.castType = frontend.typeOf(node.declSpecifier) + + // The actual expression that is casted is nested in an initializer. We could forward + // this to our initializer handler, but this would create a lot of construct expressions + // just for simple type casts, which we want to avoid, so we take a shortcut and do a + // direct unwrapping here. + val single = + (node.initializer as? ICPPASTConstructorInitializer)?.arguments?.singleOrNull() + cast.expression = + single?.let { handle(it) } ?: newProblemExpression("could not parse initializer") + cast + } else { + // Otherwise, we try to parse it as the initializer, which must be an initializer list + // expression. The same as above applies, we do not really want to + // create too many temporary object, so we just parse it here directly. + val initializer = node.initializer + if (initializer is IASTInitializerList) { + val construct = newConstructExpression(rawNode = node) + construct.arguments = + initializer.clauses.map { + handle(it) ?: newProblemExpression("could not parse initializer clause") + } + construct.type = frontend.typeOf(node.declSpecifier) + + construct + } else { + log.warn( + "Unexpected type of initializer in simple type constructor: {}", + initializer.javaClass.name + ) + newProblemExpression("Unexpected type of initializer in simple type constructor") + } + } + } + private fun handleLambdaExpression(node: CPPASTLambdaExpression): Expression { val lambda = newLambdaExpression(rawNode = node) @@ -129,7 +182,7 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : } private fun handleCompoundStatementExpression( - ctx: CPPASTCompoundStatementExpression + ctx: IGNUASTCompoundStatementExpression ): Expression { return frontend.statementHandler.handle(ctx.compoundStatement) as Expression } @@ -826,6 +879,22 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : ) } + private fun handleTypeIdInitializerExpression( + ctx: CASTTypeIdInitializerExpression + ): ConstructExpression { + val type = frontend.typeOf(ctx.typeId) + + val construct = newConstructExpression(type.name, rawNode = ctx) + + // The only supported initializer is an initializer list + (ctx.initializer as? IASTInitializerList)?.let { + construct.arguments = + it.clauses.map { handle(it) ?: newProblemExpression("could not parse argument") } + } + + return construct + } + private fun handleIntegerLiteral(ctx: IASTLiteralExpression): Expression { var (strippedValue, suffix) = ctx.valueWithSuffix val bigValue: BigInteger diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ParameterDeclarationHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ParameterDeclarationHandler.kt index 8a52fa4e6e..bc85f17f31 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ParameterDeclarationHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ParameterDeclarationHandler.kt @@ -69,6 +69,12 @@ class ParameterDeclarationHandler(lang: CXXLanguageFrontend) : val paramVariableDeclaration = newParameterDeclaration(name, type, false, rawNode = ctx) + // We cannot really model "const" as part of the type, but we can model it as part of the + // parameter, so we can use it later + if (ctx.declSpecifier.isConst) { + paramVariableDeclaration.modifiers += CONST + } + // Add default values if (ctx.declarator.initializer != null) { paramVariableDeclaration.default = 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 add744e4ce..7b57491321 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 @@ -26,9 +26,12 @@ package de.fraunhofer.aisec.cpg.passes import de.fraunhofer.aisec.cpg.TranslationContext -import de.fraunhofer.aisec.cpg.graph.* +import de.fraunhofer.aisec.cpg.graph.Component +import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration +import de.fraunhofer.aisec.cpg.graph.implicit +import de.fraunhofer.aisec.cpg.graph.newConstructExpression import de.fraunhofer.aisec.cpg.graph.scopes.GlobalScope import de.fraunhofer.aisec.cpg.graph.scopes.ValueDeclarationScope import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression @@ -48,11 +51,13 @@ import de.fraunhofer.aisec.cpg.passes.configuration.ExecuteBefore @ExecuteBefore(ReplaceCallCastPass::class) @DependsOn(TypeResolver::class) class CXXExtraPass(ctx: TranslationContext) : ComponentPass(ctx) { + override fun accept(component: Component) { val walker = SubgraphWalker.ScopedWalker(ctx.scopeManager) walker.registerHandler(::fixInitializers) walker.registerHandler(::connectDefinitions) + for (tu in component.translationUnits) { walker.iterate(tu) } diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypedefTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypedefTest.kt index e98a7c0eee..1c8b05c96e 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypedefTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypedefTest.kt @@ -26,7 +26,9 @@ package de.fraunhofer.aisec.cpg.enhancements.types import de.fraunhofer.aisec.cpg.BaseTest +import de.fraunhofer.aisec.cpg.TestUtils.analyze import de.fraunhofer.aisec.cpg.TestUtils.analyzeAndGetFirstTU +import de.fraunhofer.aisec.cpg.TestUtils.assertRefersTo import de.fraunhofer.aisec.cpg.TestUtils.findByUniqueName import de.fraunhofer.aisec.cpg.assertLocalName import de.fraunhofer.aisec.cpg.frontends.cxx.CPPLanguage @@ -206,26 +208,45 @@ internal class TypedefTest : BaseTest() { @Test @Throws(Exception::class) fun testMemberTypeDef() { - val tu = - analyzeAndGetFirstTU( - listOf(topLevel.resolve("typedefs.cpp").toFile()), - topLevel, - true - ) { + val result = + analyze(listOf(topLevel.resolve("typedefs.cpp").toFile()), topLevel, true) { it.registerLanguage() } - val addConst = tu.records["add_const"] + val addConst = result.records["add_const"] val typeMember1: ValueDeclaration = findByUniqueName(addConst.fields, "typeMember1") val typeMember2: ValueDeclaration = findByUniqueName(addConst.fields, "typeMember2") assertEquals(typeMember1.type, typeMember2.type) - val typeMemberOutside = tu.variables["typeMemberOutside"] + val typeMemberOutside = result.variables["typeMemberOutside"] assertNotEquals(typeMemberOutside?.type, typeMember2.type) - val cptr1 = tu.variables["cptr1"] - val cptr2 = tu.variables["cptr2"] + val cptr1 = result.variables["cptr1"] + val cptr2 = result.variables["cptr2"] assertEquals(cptr1?.type, cptr2?.type) assertNotEquals(typeMemberOutside?.type, cptr2?.type) } + + @Test + fun testTypedefInClass() { + val result = + analyze(listOf(topLevel.resolve("typedef_in_class.cpp").toFile()), topLevel, true) { + it.registerLanguage() + } + assertNotNull(result) + + val someDataClass = result.records["SomeDataClass"] + assertNotNull(someDataClass) + + val baseClass = result.records["BaseClass"] + assertNotNull(baseClass) + + val sizeField = baseClass.fields["size"] + assertNotNull(sizeField) + assertFalse(sizeField.isInferred) + + val size = result.memberExpressions["size"] + assertNotNull(size) + assertRefersTo(size, sizeField) + } } diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXDeclarationTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXDeclarationTest.kt new file mode 100644 index 0000000000..cec158d6c6 --- /dev/null +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXDeclarationTest.kt @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2024, 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.frontends.cxx + +import de.fraunhofer.aisec.cpg.TestUtils +import de.fraunhofer.aisec.cpg.TestUtils.analyze +import de.fraunhofer.aisec.cpg.TestUtils.assertInvokes +import de.fraunhofer.aisec.cpg.assertLocalName +import de.fraunhofer.aisec.cpg.graph.calls +import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration +import de.fraunhofer.aisec.cpg.graph.functions +import de.fraunhofer.aisec.cpg.graph.get +import de.fraunhofer.aisec.cpg.graph.invoke +import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement +import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block +import de.fraunhofer.aisec.cpg.graph.types.FunctionPointerType +import java.io.File +import kotlin.test.* + +class CXXDeclarationTest { + @Test + fun testTypedefInClass() { + val file = File("src/test/resources/cxx/typedef_in_class.cpp") + val result = + analyze(listOf(file), file.parentFile.toPath(), true) { + it.registerLanguage() + } + assertNotNull(result) + + // Reset scope manager to the global scope in a bit of an hacky way + result.finalCtx.scopeManager.resetToGlobal( + result.finalCtx.scopeManager.globalScope!!.astNode as TranslationUnitDeclaration + ) + + val typedef = + result.finalCtx.scopeManager.currentTypedefs.firstOrNull { + it.alias.name.localName == "Data" + } + assertNotNull(typedef) + } + + @Test + fun testDefinitionDeclaration() { + val file = File("src/test/resources/cxx/definition.cpp") + val result = + analyze(listOf(file), file.parentFile.toPath(), true) { + it.registerLanguage() + } + assertNotNull(result) + + val declaration = result.functions[{ it.name.localName == "function" && !it.isDefinition }] + assertNotNull(declaration) + + val definition = result.functions[{ it.name.localName == "function" && it.isDefinition }] + assertNotNull(definition) + + assertEquals(definition, declaration.definition) + } + + @Test + fun testDefinitionDeclarationWithMockStd() { + val topLevel = File("src/test/resources/c/foobar") + val result = + analyze( + listOf(topLevel.resolve("foo.c"), topLevel.resolve("bar.c")), + topLevel.toPath(), + true + ) { + it.registerLanguage() + it.includePath("src/test/resources/c/foobar/std") + } + assertNotNull(result) + + val declarations = result.functions { it.name.localName == "foo" && !it.isDefinition } + assertTrue(declarations.isNotEmpty()) + + val definition = result.functions[{ it.name.localName == "foo" && it.isDefinition }] + assertNotNull(definition) + + declarations.forEach { assertEquals(definition, it.definition) } + + // With the "std" lib, we know that size_t is a typedef for an int-type and therefore we can + // resolve all the calls + val calls = result.calls("foo") + calls.forEach { assertInvokes(it, definition) } + } + + @Test + fun testDefinitionDeclarationWithoutMockStd() { + val topLevel = File("src/test/resources/c/foobar") + val result = + analyze( + listOf(topLevel.resolve("foo.c"), topLevel.resolve("bar.c")), + topLevel.toPath(), + true + ) { + it.registerLanguage() + } + assertNotNull(result) + + val declarations = + result.functions { it.name.localName == "foo" && !it.isDefinition && !it.isInferred } + assertTrue(declarations.isNotEmpty()) + + val definition = result.functions[{ it.name.localName == "foo" && it.isDefinition }] + assertNotNull(definition) + + declarations.forEach { assertEquals(definition, it.definition) } + + // without the "std" lib, int will not match with size_t and we will infer a new function; + // and this will actually result in a problematic resolution, since C does not allow + // function overloading. + val inferredDefinition = + result.functions[{ it.name.localName == "foo" && !it.isDefinition && it.isInferred }] + assertNotNull(inferredDefinition) + } + + @Test + @Throws(Exception::class) + fun testFunctionDeclaration() { + val file = File("src/test/resources/cxx/functiondecl.cpp") + val tu = + TestUtils.analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + it.registerLanguage() + } + + // should be eight function nodes + assertEquals(8, tu.functions.size) + + var method = tu.getDeclarationAs(0, FunctionDeclaration::class.java) + assertEquals("function0(int)void", method!!.signature) + + method = tu.getDeclarationAs(1, FunctionDeclaration::class.java) + assertEquals("function1(int, std::string, SomeType*, AnotherType&)int", method!!.signature) + + val args = method.parameters.map { it.name.localName } + assertEquals(listOf("arg0", "arg1", "arg2", "arg3"), args) + + val function0 = tu.functions[{ it.name.localName == "function0" && it.isDefinition }] + assertNotNull(function0) + + val function0DeclOnly = + tu.functions[{ it.name.localName == "function0" && !it.isDefinition }] + assertNotNull(function0DeclOnly) + + // the declaration should be connected to the definition + assertEquals(function0, function0DeclOnly.definition) + + method = tu.getDeclarationAs(2, FunctionDeclaration::class.java) + assertEquals("function0(int)void", method!!.signature) + + var statements = (method.body as Block).statements + assertFalse(statements.isEmpty()) + assertEquals(2, statements.size) + + // last statement should be an implicit return + var statement = method.getBodyStatementAs(statements.size - 1, ReturnStatement::class.java) + assertNotNull(statement) + assertTrue(statement.isImplicit) + + method = tu.getDeclarationAs(3, FunctionDeclaration::class.java) + assertEquals("function2()void*", method!!.signature) + + statements = (method.body as Block).statements + assertFalse(statements.isEmpty()) + assertEquals(1, statements.size) + + // should only contain 1 explicit return statement + statement = method.getBodyStatementAs(0, ReturnStatement::class.java) + assertNotNull(statement) + assertFalse(statement.isImplicit) + + method = tu.getDeclarationAs(4, FunctionDeclaration::class.java) + assertNotNull(method) + assertEquals("function3()UnknownType*", method.signature) + + method = tu.getDeclarationAs(5, FunctionDeclaration::class.java) + assertNotNull(method) + assertEquals("function4(int)void", method.signature) + + method = tu.getDeclarationAs(6, FunctionDeclaration::class.java) + assertNotNull(method) + assertEquals(0, method.parameters.size) + assertEquals("function5()void", method.signature) + + method = tu.getDeclarationAs(7, FunctionDeclaration::class.java) + assertNotNull(method) + assertEquals(1, method.parameters.size) + + val param = method.parameters.firstOrNull() + assertNotNull(param) + + val fpType = param.type as? FunctionPointerType + assertNotNull(fpType) + assertEquals(1, fpType.parameters.size) + assertLocalName("void", fpType.returnType) + } +} diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXExpressionTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXExpressionTest.kt new file mode 100644 index 0000000000..904df0fcb1 --- /dev/null +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXExpressionTest.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024, 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.frontends.cxx + +import de.fraunhofer.aisec.cpg.TestUtils.analyzeAndGetFirstTU +import de.fraunhofer.aisec.cpg.graph.casts +import java.io.File +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class CXXExpressionTest { + @Test + fun testExplicitTypeConversion() { + val file = File("src/test/resources/cxx/explicit_type_conversion.cpp") + val tu = + analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + it.registerLanguage() + } + assertNotNull(tu) + + // We should have two calls (int and myint64) + val casts = tu.casts + assertEquals(2, casts.size) + assertEquals(listOf("int", "myint64"), casts.map { it.name.localName }) + } +} diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt index 03f8a5ebc5..d55aa8a40e 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt @@ -192,13 +192,13 @@ internal class CXXLanguageFrontendTest : BaseTest() { assertNotNull(staticCast) cast = staticCast.rhs() assertNotNull(cast) - assertLocalName("static_cast", cast) + assertLocalName("BaseClass*", cast) val reinterpretCast = main.getBodyStatementAs(3, AssignExpression::class.java) assertNotNull(reinterpretCast) cast = reinterpretCast.rhs() assertNotNull(cast) - assertLocalName("reinterpret_cast", cast) + assertLocalName("BaseClass*", cast) val d = main.variables["d"] assertNotNull(d) @@ -261,145 +261,6 @@ internal class CXXLanguageFrontendTest : BaseTest() { } } - @Test - fun testDefinitionDeclarationWithMockStd() { - val topLevel = File("src/test/resources/c/foobar") - val result = - analyze( - listOf(topLevel.resolve("foo.c"), topLevel.resolve("bar.c")), - topLevel.toPath(), - true - ) { - it.registerLanguage() - it.includePath("src/test/resources/c/foobar/std") - } - assertNotNull(result) - - val declarations = result.functions { it.name.localName == "foo" && !it.isDefinition } - assertTrue(declarations.isNotEmpty()) - - val definition = result.functions[{ it.name.localName == "foo" && it.isDefinition }] - assertNotNull(definition) - - declarations.forEach { assertEquals(definition, it.definition) } - - // With the "std" lib, we know that size_t is a typedef for an int-type and therefore we can - // resolve all the calls - val calls = result.calls("foo") - calls.forEach { assertInvokes(it, definition) } - } - - @Test - fun testDefinitionDeclarationWithoutMockStd() { - val topLevel = File("src/test/resources/c/foobar") - val result = - analyze( - listOf(topLevel.resolve("foo.c"), topLevel.resolve("bar.c")), - topLevel.toPath(), - true - ) { - it.registerLanguage() - } - assertNotNull(result) - - val declarations = - result.functions { it.name.localName == "foo" && !it.isDefinition && !it.isInferred } - assertTrue(declarations.isNotEmpty()) - - val definition = result.functions[{ it.name.localName == "foo" && it.isDefinition }] - assertNotNull(definition) - - declarations.forEach { assertEquals(definition, it.definition) } - - // without the "std" lib, int will not match with size_t and we will infer a new function; - // and this will actually result in a problematic resolution, since C does not allow - // function overloading. - val inferredDefinition = - result.functions[{ it.name.localName == "foo" && !it.isDefinition && it.isInferred }] - assertNotNull(inferredDefinition) - } - - @Test - @Throws(Exception::class) - fun testFunctionDeclaration() { - val file = File("src/test/resources/cxx/functiondecl.cpp") - val tu = - analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage() - } - - // should be eight function nodes - assertEquals(8, tu.functions.size) - - var method = tu.getDeclarationAs(0, FunctionDeclaration::class.java) - assertEquals("function0(int)void", method!!.signature) - - method = tu.getDeclarationAs(1, FunctionDeclaration::class.java) - assertEquals("function1(int, std::string, SomeType*, AnotherType&)int", method!!.signature) - - val args = method.parameters.map { it.name.localName } - assertEquals(listOf("arg0", "arg1", "arg2", "arg3"), args) - - val function0 = tu.functions[{ it.name.localName == "function0" && it.isDefinition }] - assertNotNull(function0) - - val function0DeclOnly = - tu.functions[{ it.name.localName == "function0" && !it.isDefinition }] - assertNotNull(function0DeclOnly) - - // the declaration should be connected to the definition - assertEquals(function0, function0DeclOnly.definition) - - method = tu.getDeclarationAs(2, FunctionDeclaration::class.java) - assertEquals("function0(int)void", method!!.signature) - - var statements = (method.body as Block).statements - assertFalse(statements.isEmpty()) - assertEquals(2, statements.size) - - // last statement should be an implicit return - var statement = method.getBodyStatementAs(statements.size - 1, ReturnStatement::class.java) - assertNotNull(statement) - assertTrue(statement.isImplicit) - - method = tu.getDeclarationAs(3, FunctionDeclaration::class.java) - assertEquals("function2()void*", method!!.signature) - - statements = (method.body as Block).statements - assertFalse(statements.isEmpty()) - assertEquals(1, statements.size) - - // should only contain 1 explicit return statement - statement = method.getBodyStatementAs(0, ReturnStatement::class.java) - assertNotNull(statement) - assertFalse(statement.isImplicit) - - method = tu.getDeclarationAs(4, FunctionDeclaration::class.java) - assertNotNull(method) - assertEquals("function3()UnknownType*", method.signature) - - method = tu.getDeclarationAs(5, FunctionDeclaration::class.java) - assertNotNull(method) - assertEquals("function4(int)void", method.signature) - - method = tu.getDeclarationAs(6, FunctionDeclaration::class.java) - assertNotNull(method) - assertEquals(0, method.parameters.size) - assertEquals("function5()void", method.signature) - - method = tu.getDeclarationAs(7, FunctionDeclaration::class.java) - assertNotNull(method) - assertEquals(1, method.parameters.size) - - val param = method.parameters.firstOrNull() - assertNotNull(param) - - val fpType = param.type as? FunctionPointerType - assertNotNull(fpType) - assertEquals(1, fpType.parameters.size) - assertLocalName("void", fpType.returnType) - } - @Test @Throws(Exception::class) fun testBlock() { @@ -814,7 +675,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { val constant = recordDeclaration.fields["CONSTANT"] assertNotNull(constant) assertEquals(tu.incompleteType().reference(POINTER), field.type) - assertEquals(3, recordDeclaration.methods.size) + assertEquals(4, recordDeclaration.methods.size) val method = recordDeclaration.methods[0] assertLocalName("method", method) diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXTemplateTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXTemplateTest.kt new file mode 100644 index 0000000000..7092c047ae --- /dev/null +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXTemplateTest.kt @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024, 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.frontends.cxx + +import de.fraunhofer.aisec.cpg.TestUtils.analyzeAndGetFirstTU +import de.fraunhofer.aisec.cpg.assertLocalName +import de.fraunhofer.aisec.cpg.graph.declarations.ConstructorDeclaration +import de.fraunhofer.aisec.cpg.graph.functions +import de.fraunhofer.aisec.cpg.graph.methods +import de.fraunhofer.aisec.cpg.graph.types.ParameterizedType +import java.io.File +import kotlin.test.Test +import kotlin.test.assertIs +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +/** + * This class contains several tests that test our ability to understand templates to some degree. + */ +class CXXTemplateTest { + @Test + fun testConstructor() { + val file = File("src/test/resources/cxx/template_constructor.cpp") + val tu = + analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + it.registerLanguage() + } + assertNotNull(tu) + + // We should have two constructors with a "double-reference" type + var method = + tu.methods.firstOrNull { it.signature == "MyTemplateClass(T&&)MyTemplateClass" } + assertIs(method) + assertNotNull(method) + + val paramType = method.parameters.firstOrNull()?.type?.root + assertIs(paramType) + assertLocalName("T", paramType) + + method = tu.methods.firstOrNull { it.signature == "MyClass(MyClass&&)MyClass" } + assertIs(method) + assertNotNull(method) + } + + @Test + fun testFunctionTemplateCall() { + val file = File("src/test/resources/cxx/templates/function_template.cpp") + val tu = + analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + it.registerLanguage() + } + assertNotNull(tu) + assertTrue(tu.functions.all { !it.isInferred }) + } +} diff --git a/cpg-language-cxx/src/test/resources/cxx/recordstmt.cpp b/cpg-language-cxx/src/test/resources/cxx/recordstmt.cpp index 4c2b2c3e45..5ba7a1c424 100644 --- a/cpg-language-cxx/src/test/resources/cxx/recordstmt.cpp +++ b/cpg-language-cxx/src/test/resources/cxx/recordstmt.cpp @@ -17,6 +17,7 @@ class SomeClass { } SomeClass(int a); + ~SomeClass(); }; void* SomeClass::method() { diff --git a/cpg-language-cxx/src/test/resources/templates/functiontemplates/functionTemplateInvocation7.cpp b/cpg-language-cxx/src/test/resources/templates/functiontemplates/functionTemplateInvocation7.cpp index c497d4cda5..1e0e2ae5fd 100644 --- a/cpg-language-cxx/src/test/resources/templates/functiontemplates/functionTemplateInvocation7.cpp +++ b/cpg-language-cxx/src/test/resources/templates/functiontemplates/functionTemplateInvocation7.cpp @@ -9,5 +9,5 @@ int main() { f( 1 , 2 ); // non-template f('a', 'b'); // template f( 1 , 'b'); // non-template - f<>( 1 , 'b'); // error + f<>( 1 , 'b'); // error; we fall back to non-template for now } \ No newline at end of file diff --git a/cpg-language-cxx/src/test/resources/typedefs/typedefs.cpp b/cpg-language-cxx/src/test/resources/typedefs/typedefs.cpp index f118811c43..b4778e9b06 100644 --- a/cpg-language-cxx/src/test/resources/typedefs/typedefs.cpp +++ b/cpg-language-cxx/src/test/resources/typedefs/typedefs.cpp @@ -70,7 +70,7 @@ unsigned long typedef long int ullong; unsigned long long int someUllong1; ullong someUllong2; -// std::add_const, like many other metafunctions, use member typedefs +// a global typedef called "type", which will be used to explain typedef scopes and shadowing typedef long type; type typeMemberOutside; @@ -78,16 +78,20 @@ type typeMemberOutside; typedef uint8_t test; struct add_const { + // this typedef is scoped to the global scope, but its alias name is actually a + // FQN of add_const::type typedef const int type; const int typeMember1; + + // since C++ allows omitting the scope of types, this "type" actually refers to add_const::type type typeMember2; }; // template, not to be confused with multiple typedef typedef template_class_A type_B; - int main() { + // this typedef is scoped only to the function and shadows the global "type" typedef within this function typedef char *type; char *cptr1; type cptr2; 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 82fa295366..6cc921cc0a 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 @@ -182,7 +182,7 @@ class GoLanguage : } // We accept all kind of numbers if the literal is part of the call expression - if (targetHint is FunctionDeclaration && hint is Literal<*>) { + if (targetHint is ParameterDeclaration && hint is Literal<*>) { return type is NumericType && targetType is NumericType } diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt index aac1344044..617837fb8d 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt @@ -70,28 +70,22 @@ class JavaExternalTypeHierarchyResolver(ctx: TranslationContext) : ComponentPass // Iterate over all known types and add their (direct) supertypes. for (t in HashSet(typeManager.firstOrderTypes)) { // TODO: Do we have to check if the type's language is JavaLanguage? - try { - val symbol = resolver.tryToSolveType(t.typeName) - if (symbol?.isSolved ?: false) { - try { - val resolvedSuperTypes = symbol.correspondingDeclaration.getAncestors(true) - for (anc in resolvedSuperTypes) { - // Add all resolved supertypes to the type. - val superType = provider.objectType(anc.qualifiedName) - superType.typeOrigin = Type.Origin.RESOLVED - t.superTypes.add(superType) - } - } catch (e: UnsolvedSymbolException) { - // Even if the symbol itself is resolved, "getAncestors()" may throw - // exception. - LOGGER.warn( - "Could not resolve supertypes of ${symbol.correspondingDeclaration}" - ) + val symbol = resolver.tryToSolveType(t.typeName) + if (symbol.isSolved) { + try { + val resolvedSuperTypes = symbol.correspondingDeclaration.getAncestors(true) + for (anc in resolvedSuperTypes) { + // Add all resolved supertypes to the type. + val superType = provider.objectType(anc.qualifiedName) + superType.typeOrigin = Type.Origin.RESOLVED + t.superTypes.add(superType) } + } catch (e: UnsolvedSymbolException) { + // Even if the symbol itself is resolved, "getAncestors()" may throw exception. + LOGGER.warn( + "Could not resolve supertypes of ${symbol.correspondingDeclaration}" + ) } - } catch (ie: IllegalArgumentException) { - // The type may not exist in the javaparser if it was created as an inference - LOGGER.warn("Could not resolve type ${t.typeName}") } } } diff --git a/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt b/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt index b9f8babf60..8e0436ff8b 100644 --- a/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt +++ b/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt @@ -547,7 +547,7 @@ class Application : Callable { } includesFile?.let { theFile -> - log.info("Load includes form file: $theFile") + log.info("Load includes from file: $theFile") val baseDir = File(theFile.toString()).parentFile?.toString() ?: "" theFile .inputStream()