From 30b43b932238ac20949e4b85006a070c88aee8a0 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Mon, 2 Mar 2020 15:54:23 +0100 Subject: [PATCH] C++: Tests for variables with ambiguous types --- .../variable_types/extracted_once.h | 16 ++++++++++ .../variable_types/extracted_twice.h | 30 +++++++++++++++++++ .../variable_types/file1.c | 15 ++++++++++ .../variable_types/file2.c | 10 +++++++ .../variable_types/varType.expected | 17 +++++++++++ .../variable_types/varType.ql | 5 ++++ 6 files changed, 93 insertions(+) create mode 100644 cpp/ql/test/library-tests/multiple_declarations/variable_types/extracted_once.h create mode 100644 cpp/ql/test/library-tests/multiple_declarations/variable_types/extracted_twice.h create mode 100644 cpp/ql/test/library-tests/multiple_declarations/variable_types/file1.c create mode 100644 cpp/ql/test/library-tests/multiple_declarations/variable_types/file2.c create mode 100644 cpp/ql/test/library-tests/multiple_declarations/variable_types/varType.expected create mode 100644 cpp/ql/test/library-tests/multiple_declarations/variable_types/varType.ql diff --git a/cpp/ql/test/library-tests/multiple_declarations/variable_types/extracted_once.h b/cpp/ql/test/library-tests/multiple_declarations/variable_types/extracted_once.h new file mode 100644 index 00000000000..0b8253007b0 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/variable_types/extracted_once.h @@ -0,0 +1,16 @@ +// This header file is extracted only once even though it's included by both +// file1.c and file2.c. That's presumably because it's wrongly considered to +// expand to the same trap in both contexts. In practice, this header gets +// extracted together with the extraction of file1.c. + +// BUG: types of members depend on extraction order. +// Only one copy of this struct is extracted, and the types of its members refer +// to the typedefs in file1.c. Had file2.c been extracted first instead, the +// types of its members would be different. +struct UnifiableOnce { + intAlias intMember; + qualifiedIntAlias qualifiedIntMember; +}; + +// BUG: types of parameters depend on extraction order. +void functionOnce(intAlias param); diff --git a/cpp/ql/test/library-tests/multiple_declarations/variable_types/extracted_twice.h b/cpp/ql/test/library-tests/multiple_declarations/variable_types/extracted_twice.h new file mode 100644 index 00000000000..e3965750cfe --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/variable_types/extracted_twice.h @@ -0,0 +1,30 @@ +// This header file is extracted twice because its inclusions in file1.c and +// file2.c lead to different context hashes, seemingly because this file (unlike +// extracted_once.h) refers to `structAlias`. That means the resulting trap has +// two copies of all declarations in this file, and those copies have to be +// unified in the trap import step or in QL. + +// GOOD. The types of the members of this struct are unifiable, which in this +// context means that they share the same unspecified types. This means that the +// two extractions of the struct get the same content hash and therefore become +// one entry in the database. Both struct members have multiple types in the +// `membervariables` table, but those are unified in the +// `MemberVariable.getType()` predicate. +struct UnifiableTwice { + intAlias intMember; + qualifiedIntAlias qualifiedIntMember; +}; + +// BUG: Non-member variables of this type have two types in the database. +// The type of `structMember` is ambiguous, and the two possible types are not +// unifiable, meaning in this context that they don't share an unspecified type. +// The types are nevertheless _compatible_, so it's valid C (not C++) to use +// these two definitions interchangably in the same program. +struct NotUnifiableTwice { + structAlias structMember; +}; + +// BUG: The parameter of this function has two types. +// Because the `MemberVariable.getType()` workaround does not apply to a +// `Parameter`, this `Parameter` gets two types. +void functionTwice(intAlias param); diff --git a/cpp/ql/test/library-tests/multiple_declarations/variable_types/file1.c b/cpp/ql/test/library-tests/multiple_declarations/variable_types/file1.c new file mode 100644 index 00000000000..c85433dfd90 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/variable_types/file1.c @@ -0,0 +1,15 @@ +// These typedefs are all _compatible_ (see +// https://en.cppreference.com/w/c/language/type#Compatible_types) with their +// siblings in file2.c. It varies whether they have a canonical form that's +// common to them both. +typedef int localInt; +typedef localInt intAlias; // has common `getUnderlyingType()` and `getUnspecifiedType()` +typedef int qualifiedIntAlias; // only has common `getUnspecifiedType()` +typedef struct emptyStruct1 { } structAlias; // has no common type + +#include "extracted_once.h" +struct UnifiableOnce uOnce; + +#include "extracted_twice.h" +struct UnifiableTwice uTwice; +struct NotUnifiableTwice nTwice; // BUG: this variable has two types diff --git a/cpp/ql/test/library-tests/multiple_declarations/variable_types/file2.c b/cpp/ql/test/library-tests/multiple_declarations/variable_types/file2.c new file mode 100644 index 00000000000..02800b76945 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/variable_types/file2.c @@ -0,0 +1,10 @@ +typedef int intAlias; +typedef const int qualifiedIntAlias; +typedef struct emptyStruct2 { } structAlias; + +#include "extracted_once.h" +struct UnifiableOnce uOnce; + +#include "extracted_twice.h" +struct UnifiableTwice uTwice; +struct NotUnifiableTwice nTwice; // BUG: this variable has two types diff --git a/cpp/ql/test/library-tests/multiple_declarations/variable_types/varType.expected b/cpp/ql/test/library-tests/multiple_declarations/variable_types/varType.expected new file mode 100644 index 00000000000..b66011c2195 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/variable_types/varType.expected @@ -0,0 +1,17 @@ +| extracted_once.h:11:14:11:22 | intMember | file1.c:6:18:6:25 | intAlias | 1 | +| extracted_once.h:12:23:12:40 | qualifiedIntMember | file1.c:7:13:7:29 | qualifiedIntAlias | 1 | +| extracted_once.h:16:28:16:32 | param | file1.c:6:18:6:25 | intAlias | 1 | +| extracted_twice.h:14:14:14:22 | intMember | file://:0:0:0:0 | int | 1 | +| extracted_twice.h:15:23:15:40 | qualifiedIntMember | file://:0:0:0:0 | int | 1 | +| extracted_twice.h:24:17:24:28 | structMember | file1.c:8:33:8:43 | structAlias | 1 | +| extracted_twice.h:24:17:24:28 | structMember | file2.c:3:33:3:43 | structAlias | 1 | +| extracted_twice.h:30:29:30:33 | param | file1.c:6:18:6:25 | intAlias | 2 | +| extracted_twice.h:30:29:30:33 | param | file2.c:1:13:1:20 | intAlias | 2 | +| file1.c:11:22:11:26 | uOnce | extracted_once.h:10:8:10:20 | UnifiableOnce | 1 | +| file1.c:14:23:14:28 | uTwice | extracted_twice.h:13:8:13:21 | UnifiableTwice | 1 | +| file1.c:15:26:15:31 | nTwice | extracted_twice.h:23:8:23:24 | NotUnifiableTwice | 2 | +| file1.c:15:26:15:31 | nTwice | extracted_twice.h:23:8:23:24 | NotUnifiableTwice | 2 | +| file2.c:6:22:6:26 | uOnce | extracted_once.h:10:8:10:20 | UnifiableOnce | 1 | +| file2.c:9:23:9:28 | uTwice | extracted_twice.h:13:8:13:21 | UnifiableTwice | 1 | +| file2.c:10:26:10:31 | nTwice | extracted_twice.h:23:8:23:24 | NotUnifiableTwice | 2 | +| file2.c:10:26:10:31 | nTwice | extracted_twice.h:23:8:23:24 | NotUnifiableTwice | 2 | diff --git a/cpp/ql/test/library-tests/multiple_declarations/variable_types/varType.ql b/cpp/ql/test/library-tests/multiple_declarations/variable_types/varType.ql new file mode 100644 index 00000000000..d16e8a23a1b --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/variable_types/varType.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable var +where exists(var.getFile().getRelativePath()) +select var, var.getType(), strictcount(var.getType())