Skip to content

Commit

Permalink
Better detection of typedefs in C++ (#1896)
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto authored Dec 12, 2024
1 parent f1cb0dc commit 9898155
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ class ScopeManager : ScopeProvider {
// 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.isQualified() && current == currentScope) {
if (!alias.isQualified() && current == scope) {
val decl = current.typedefs[alias]
if (decl != null) {
return decl.type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,18 +242,20 @@ class DeclarationHandler(lang: CXXLanguageFrontend) :
*/
private val IASTSimpleDeclaration.isTypedef: Boolean
get() {
return if (this.rawSignature.contains("typedef")) {
if (this.declSpecifier is CPPASTCompositeTypeSpecifier) {
// we need to make a difference between structs that have typedefs and structs
// that are typedefs themselves
this.declSpecifier.toString() == "struct" &&
this.rawSignature.trim().startsWith("typedef")
return if (this.declSpecifier is IASTCompositeTypeSpecifier) {
if (this.declSpecifier.rawSignature.contains("typedef")) {
// This is very stupid. For composite type specifiers, we need to make sure that
// we do not match simply because our declarations contain a typedef.
// The problem is that we cannot correctly detect the case where both our "main"
// declaration and our sub declarations contain a typedef :(
(this.declSpecifier as IASTCompositeTypeSpecifier).getDeclarations(true).none {
it.rawSignature.contains("typedef")
}
} else {

true
false
}
} else {
false
this.declSpecifier.rawSignature.contains("typedef")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
*/
package de.fraunhofer.aisec.cpg.enhancements.types

import de.fraunhofer.aisec.cpg.InferenceConfiguration.Companion.builder
import de.fraunhofer.aisec.cpg.frontends.cxx.CLanguage
import de.fraunhofer.aisec.cpg.frontends.cxx.CPPLanguage
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration
Expand All @@ -34,6 +36,7 @@ import de.fraunhofer.aisec.cpg.graph.types.NumericType
import de.fraunhofer.aisec.cpg.graph.types.ObjectType
import de.fraunhofer.aisec.cpg.graph.variables
import de.fraunhofer.aisec.cpg.test.*
import java.io.File
import java.nio.file.Path
import kotlin.test.*

Expand Down Expand Up @@ -187,16 +190,23 @@ internal class TypedefTest : BaseTest() {
fun testArbitraryTypedefLocation() {
val tu =
analyzeAndGetFirstTU(
listOf(topLevel.resolve("typedefs.cpp").toFile()),
listOf(topLevel.resolve("weird_typedefs.cpp").toFile()),
topLevel,
true
) {
it.registerLanguage<CPPLanguage>()
}

val ullong1 = tu.variables["someUllong1"]
assertNotNull(ullong1)

val ullong2 = tu.variables["someUllong2"]
assertEquals(ullong1?.type, ullong2?.type)
assertNotNull(ullong2)
assertEquals(ullong1.type, ullong2.type)

val records = tu.records
assertEquals(2, records.size)
assertEquals(listOf("bar", "foo"), records.map { it.name.localName })
}

@Test
Expand Down Expand Up @@ -243,4 +253,40 @@ internal class TypedefTest : BaseTest() {
assertNotNull(size)
assertRefersTo(size, sizeField)
}

@Test
fun testTypedefStructCPP() {
val file = File("src/test/resources/cxx/typedef_struct.cpp")
val tu =
analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) {
it.registerLanguage<CPPLanguage>()
it.inferenceConfiguration(builder().enabled(false).build())
}
with(tu) {
val me = tu.memberExpressions
me.forEach { assertNotNull(it.refersTo) }

val test = tu.records.singleOrNull()
assertNotNull(test)
assertLocalName("test", test)
}
}

@Test
fun testTypedefStructC() {
val file = File("src/test/resources/c/typedef_struct.c")
val tu =
analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) {
it.registerLanguage<CLanguage>()
it.inferenceConfiguration(builder().enabled(false).build())
}
with(tu) {
val me = tu.memberExpressions
me.forEach { assertNotNull(it.refersTo) }

val test = tu.records.singleOrNull()
assertNotNull(test)
assertLocalName("test", test)
}
}
}
13 changes: 13 additions & 0 deletions cpg-language-cxx/src/test/resources/c/typedef_struct.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
typedef struct test {
int a;
int b;
} S;

int structs() {
S s;
S t;
S* p=&s;
s.a=1;
s.b=2;
printf("%d %d\n", s.a, s.b);
}
15 changes: 15 additions & 0 deletions cpg-language-cxx/src/test/resources/cxx/typedef_struct.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
typedef struct test {
int a;
int b;
} S;

int structs() {
S s;
S t;
S* p=&s;
s.a=1;
s.b=2;
printf("%d %d\n", s.a, s.b);
}

long typedef bla;
19 changes: 19 additions & 0 deletions cpg-language-cxx/src/test/resources/typedefs/weird_typedefs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// typedef can be used anywhere in the decl-specifier-seq
// more conventionally spelled "typedef unsigned long long int ullong;"
unsigned long typedef long int ullong;

// usage of type that is identical to typedef
unsigned long long int someUllong1;
// usage of typedef
ullong someUllong2;

// also possible with structs
struct bar {
int a;
int b;
} typedef baz;

// just some type that contains a typedef for more confusion
struct foo {
typedef const int a;
};

0 comments on commit 9898155

Please sign in to comment.