Skip to content

Commit

Permalink
Improved stability of isDerivedFrom decisions (#1488)
Browse files Browse the repository at this point in the history
This PR adds the way `Type.isDerivedFrom` works. More concretly, we are once again taking the "wrap state" of the type into account. This means that pointer types and non-pointer types will not match even though their root types derive from each other. This was the way this function behaved in the past and it seems this was changed at some point, which led to some weird over-approximations in call resolving, basically ignoring wether a type was a pointer or not.
  • Loading branch information
oxisto authored and maximiliankaul committed Apr 17, 2024
1 parent a102bff commit f737e86
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression
import de.fraunhofer.aisec.cpg.graph.types.*
import de.fraunhofer.aisec.cpg.graph.unknownType
import de.fraunhofer.aisec.cpg.isDerivedFrom
import de.fraunhofer.aisec.cpg.wrapState
import java.io.File
import kotlin.reflect.KClass
import kotlin.reflect.full.primaryConstructor
Expand Down Expand Up @@ -214,21 +215,38 @@ abstract class Language<T : LanguageFrontend<*, *>> : Node() {
}

/**
* This function checks, if [type] is derived from [superType]. Optionally, the nodes that hold
* the respective type can be supplied as [hint] and [superHint].
* This function checks, if [type] is derived from [superType]. Note, this also takes the
* [WrapState] of the type into account, which means that pointer types of derived types will
* not match with a non-pointer type of its base type. But, if both are pointer types, they will
* match.
*
* Optionally, the nodes that hold the respective type can be supplied as [hint] and
* [superHint].
*/
open fun isDerivedFrom(
type: Type,
superType: Type,
hint: HasType?,
superHint: HasType?
): Boolean {
// We can take a shortcut if it is the same type
if (type == superType) {
return true
}

// We can also take a shortcut: if they are not of the same subclass, they will never
// match
if (type::class != superType::class) {
return false
}

// Retrieve all ancestor types of our type (more concretely of the root type)
val root = type.root
val superTypes = root.ancestors.map { it.type }

// Check, if super type (or its root) is in the list
return superType.root in superTypes
// Check, if super type (or its root) is in the list. Also, the wrap state needs to be the
// same
return superType.root in superTypes && type.wrapState == superType.wrapState
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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

import de.fraunhofer.aisec.cpg.graph.newRecordDeclaration
import de.fraunhofer.aisec.cpg.graph.objectType
import de.fraunhofer.aisec.cpg.graph.pointer
import de.fraunhofer.aisec.cpg.isDerivedFrom
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class LanguageTest {

@Test
fun testLanguageDerived() {
with(TestLanguageFrontend()) {
val baseType = objectType("baseType")

val myTypeRecord = newRecordDeclaration("myType", "class")
myTypeRecord.superClasses = mutableListOf(baseType)
val myType = myTypeRecord.toType()

val pointerBaseType = baseType.pointer()
val pointerMyType = myType.pointer()

// pointer-type and non-pointer types -> will not match in any case
var matches = pointerMyType.isDerivedFrom(myType)
assertFalse(matches)

// the same type will always match
matches = pointerMyType.isDerivedFrom(pointerMyType)
assertTrue(matches)

// a pointer to the derived type will match a pointer to its base type
matches = pointerMyType.isDerivedFrom(pointerBaseType)
assertTrue(matches)

// non-pointer types as well
matches = myType.isDerivedFrom(baseType)
assertTrue(matches)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,24 @@ open class CLanguage :
)
return resolveWithImplicitCast(call, initialInvocationCandidates)
}

override fun isDerivedFrom(
type: Type,
superType: Type,
hint: HasType?,
superHint: HasType?
): Boolean {
val match = super.isDerivedFrom(type, superType, hint, superHint)
if (match) {
return true
}

// As a special rule, a non-nested pointer and array of the same type are completely
// interchangeable
if (type.root == superType.root && type is PointerType && superType is PointerType) {
return true
}

return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class ScopeVariables{

try {
throw new error();
} catch (const error& varName) {
} catch (error* varName) {
printLog("func2_catch_varName", varName);
};
ScopeVariables scopeVariables;
Expand Down

0 comments on commit f737e86

Please sign in to comment.