diff --git a/cpp/ql/src/semmle/code/cpp/Union.qll b/cpp/ql/src/semmle/code/cpp/Union.qll index f1c033438ba5..5b70378dfa63 100644 --- a/cpp/ql/src/semmle/code/cpp/Union.qll +++ b/cpp/ql/src/semmle/code/cpp/Union.qll @@ -18,6 +18,12 @@ class Union extends Struct { override string explain() { result = "union " + this.getName() } override predicate isDeeplyConstBelow() { any() } // No subparts + + /** Holds if this type is anonymous. */ + final predicate isAnonymous() { + getName() = "union " and + exists(Variable v | v.getType() = this and v.getName() = "(null)") + } } /** @@ -64,3 +70,16 @@ class NestedUnion extends Union { /** Holds if this member is public. */ predicate isPublic() { this.hasSpecifier("public") } } + +/** + * A C/C++ anonymous union. For example: + * ``` + * union { + * int i; + * float f; + * }; + * ``` + */ +class AnonymousUnion extends Union { + AnonymousUnion() { this.isAnonymous() } +} diff --git a/cpp/ql/src/semmle/code/cpp/UserType.qll b/cpp/ql/src/semmle/code/cpp/UserType.qll index cbb7f39adbd2..fd6119ca7ac6 100644 --- a/cpp/ql/src/semmle/code/cpp/UserType.qll +++ b/cpp/ql/src/semmle/code/cpp/UserType.qll @@ -30,9 +30,6 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @ override predicate hasName(string name) { usertypes(underlyingElement(this), name, _) } - /** Holds if this type is anonymous. */ - predicate isAnonymous() { getName().matches("(unnamed%") } - override predicate hasSpecifier(string s) { Type.super.hasSpecifier(s) } override Specifier getASpecifier() { result = Type.super.getASpecifier() } diff --git a/cpp/ql/test/library-tests/unions/Unions1.expected b/cpp/ql/test/library-tests/unions/Unions1.expected index 440e9f8f1b9e..c09bb3f092b7 100644 --- a/cpp/ql/test/library-tests/unions/Unions1.expected +++ b/cpp/ql/test/library-tests/unions/Unions1.expected @@ -4,3 +4,5 @@ | unions.cpp:27:9:27:20 | MyLocalUnion | LocalUnion, Struct, Union | | operator= | | unions.cpp:33:7:33:13 | MyClass | | | operator= | | unions.cpp:36:9:36:21 | MyNestedUnion | NestedUnion, Struct, Union | | operator= | +| unions.cpp:43:9:43:9 | union | AnonymousUnion, LocalUnion, Struct, Union | | operator= | +| unions.cpp:48:9:48:9 | union | LocalUnion, Struct, Union | | operator= | diff --git a/cpp/ql/test/library-tests/unions/Unions1.ql b/cpp/ql/test/library-tests/unions/Unions1.ql index 22652cc20ce9..f211bd93f2cc 100644 --- a/cpp/ql/test/library-tests/unions/Unions1.ql +++ b/cpp/ql/test/library-tests/unions/Unions1.ql @@ -12,6 +12,9 @@ string describe(Class c) { or c instanceof NestedUnion and result = "NestedUnion" + or + c instanceof AnonymousUnion and + result = "AnonymousUnion" } from Class c diff --git a/cpp/ql/test/library-tests/unions/unions.cpp b/cpp/ql/test/library-tests/unions/unions.cpp index 0e3f3c6ee2bb..6bc86df29f3c 100644 --- a/cpp/ql/test/library-tests/unions/unions.cpp +++ b/cpp/ql/test/library-tests/unions/unions.cpp @@ -38,3 +38,15 @@ class MyClass float f; }; }; + +void test_anonymous_union() { + union { + int u1; + char* u2; + }; + + union { + int u3; + char* u4; + } not_an_anonymous_union; +} \ No newline at end of file