diff --git a/cpp/ql/src/semmle/code/cpp/internal/ResolveClass.qll b/cpp/ql/src/semmle/code/cpp/internal/ResolveClass.qll index c3cde4c4346c..eaefc8196cc8 100644 --- a/cpp/ql/src/semmle/code/cpp/internal/ResolveClass.qll +++ b/cpp/ql/src/semmle/code/cpp/internal/ResolveClass.qll @@ -1,23 +1,31 @@ import semmle.code.cpp.Type -/** Holds if `d` is a complete class named `name`. */ +pragma[noinline] +private string getTopLevelClassName(@usertype c) { + isClass(c) and + usertypes(c, result, _) and + not namespacembrs(_, c) and // not in a namespace + not member(_, _, c) and // not in some structure + not class_instantiation(c, _) // not a template instantiation +} + +/** Holds if `d` is a unique complete class named `name`. */ pragma[noinline] private predicate existsCompleteWithName(string name, @usertype d) { - isClass(d) and is_complete(d) and - usertypes(d, name, _) + name = getTopLevelClassName(d) and + strictcount(@usertype other | is_complete(other) and getTopLevelClassName(other) = name) = 1 } /** Holds if `c` is an incomplete class named `name`. */ pragma[noinline] private predicate existsIncompleteWithName(string name, @usertype c) { - isClass(c) and not is_complete(c) and - usertypes(c, name, _) + name = getTopLevelClassName(c) } /** - * Holds if `c` is an imcomplete class, and there exists a complete class `d` + * Holds if `c` is an imcomplete class, and there exists a unique complete class `d` * with the same name. */ private predicate hasCompleteTwin(@usertype c, @usertype d) { @@ -30,10 +38,8 @@ private predicate hasCompleteTwin(@usertype c, @usertype d) { import Cached cached private module Cached { /** - * If `c` is incomplete, and there exists a complete class with the same name, - * then the result is that complete class. Otherwise, the result is `c`. If - * multiple complete classes have the same name, this predicate may have - * multiple results. + * If `c` is incomplete, and there exists a unique complete class with the same name, + * then the result is that complete class. Otherwise, the result is `c`. */ cached @usertype resolveClass(@usertype c) { hasCompleteTwin(c, result) diff --git a/cpp/ql/test/library-tests/structs/compatible_cpp/b1.cpp b/cpp/ql/test/library-tests/structs/compatible_cpp/b1.cpp index c7d839b4874d..760147d60ff0 100644 --- a/cpp/ql/test/library-tests/structs/compatible_cpp/b1.cpp +++ b/cpp/ql/test/library-tests/structs/compatible_cpp/b1.cpp @@ -24,3 +24,9 @@ class Damson { int damson_x; void foo(); }; + +namespace unrelated { + class AppleCompatible { + long apple_x; + }; +} diff --git a/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected index 4b631dde98b4..326fea4210f8 100644 --- a/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected +++ b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected @@ -32,6 +32,9 @@ | b1.cpp:23:7:23:12 | Damson | 5 members | 2 locations | 1 | foo | | b1.cpp:23:7:23:12 | Damson | 5 members | 2 locations | 2 | operator= | | b1.cpp:23:7:23:12 | Damson | 5 members | 2 locations | 3 | operator= | +| b1.cpp:29:9:29:23 | AppleCompatible | 3 members | 1 locations | 0 | apple_x | +| b1.cpp:29:9:29:23 | AppleCompatible | 3 members | 1 locations | 1 | operator= | +| b1.cpp:29:9:29:23 | AppleCompatible | 3 members | 1 locations | 2 | operator= | | b2.cpp:2:7:2:21 | AppleCompatible | 3 members | 2 locations | 0 | apple_x | | b2.cpp:2:7:2:21 | AppleCompatible | 3 members | 2 locations | 1 | operator= | | b2.cpp:2:7:2:21 | AppleCompatible | 3 members | 2 locations | 2 | operator= | diff --git a/cpp/ql/test/library-tests/structs/compatible_cpp/compatible_types.expected b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible_types.expected index df2a178f6674..f9446f1b19a6 100644 --- a/cpp/ql/test/library-tests/structs/compatible_cpp/compatible_types.expected +++ b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible_types.expected @@ -10,6 +10,7 @@ | b1.cpp:11:7:11:22 | BananaCompatible | 0 | file://:0:0:0:0 | int | 1 types | | b1.cpp:16:7:16:12 | Cherry | 0 | file://:0:0:0:0 | int | 1 types | | b1.cpp:23:7:23:12 | Damson | 0 | file://:0:0:0:0 | int | 1 types | +| b1.cpp:29:9:29:23 | AppleCompatible | 0 | file://:0:0:0:0 | long | 1 types | | b2.cpp:2:7:2:21 | AppleCompatible | 0 | file://:0:0:0:0 | int | 1 types | | b2.cpp:9:7:9:22 | BananaCompatible | 0 | file://:0:0:0:0 | int | 1 types | | b2.cpp:14:7:14:12 | Cherry | 0 | file://:0:0:0:0 | short | 1 types | diff --git a/cpp/ql/test/library-tests/structs/incomplete_definition/pointerbasetype.expected b/cpp/ql/test/library-tests/structs/incomplete_definition/pointerbasetype.expected index 98a672ad4353..801306a572ab 100644 --- a/cpp/ql/test/library-tests/structs/incomplete_definition/pointerbasetype.expected +++ b/cpp/ql/test/library-tests/structs/incomplete_definition/pointerbasetype.expected @@ -1,2 +1,6 @@ | a.h:5:8:5:13 | cheese | y.cpp:4:8:4:10 | Foo | 3 | | x.cpp:3:6:3:10 | bar_x | a.h:4:8:4:10 | Bar | 3 | +| x.cpp:19:6:19:10 | foo_x | y.cpp:4:8:4:10 | Foo | 3 | +| x.cpp:23:5:23:17 | templateField | x.cpp:6:10:6:12 | Foo | 3 | +| x.cpp:23:5:23:17 | templateField | x.cpp:12:9:12:11 | Foo | 3 | +| x.cpp:26:18:26:29 | template_foo | x.cpp:22:7:22:14 | Template | 0 | diff --git a/cpp/ql/test/library-tests/structs/incomplete_definition/x.cpp b/cpp/ql/test/library-tests/structs/incomplete_definition/x.cpp index 5b2a352e3dd7..ab5eb9474553 100644 --- a/cpp/ql/test/library-tests/structs/incomplete_definition/x.cpp +++ b/cpp/ql/test/library-tests/structs/incomplete_definition/x.cpp @@ -1,3 +1,31 @@ #include "a.h" Bar *bar_x; + +namespace unrelated { + struct Foo { + short val; + }; +} + +struct ContainsAnotherFoo { + class Foo { + long val; + }; +}; + +// The type of `foo_x` should not refer to any of the above classes, none of +// which are named `Foo` in the global scope. +Foo *foo_x; + +template +class Template { + T templateField; +}; + +Template *template_foo; + +// Instantiation of the template with unrelated classes named `Foo` should not +// get mixed up with the instantiation above. +template class Template; +template class Template; diff --git a/cpp/ql/test/library-tests/structs/qualified_names/c1.cpp b/cpp/ql/test/library-tests/structs/qualified_names/c1.cpp new file mode 100644 index 000000000000..f72303701213 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/qualified_names/c1.cpp @@ -0,0 +1,9 @@ +#include "header.h" + +struct MultipleDefsButSameHeader { + int i; +}; + +struct OneDefInDifferentFile { + int i; +}; diff --git a/cpp/ql/test/library-tests/structs/qualified_names/c2.cpp b/cpp/ql/test/library-tests/structs/qualified_names/c2.cpp new file mode 100644 index 000000000000..be5380566150 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/qualified_names/c2.cpp @@ -0,0 +1,6 @@ +#include "header.h" + +struct MultipleDefsButSameHeader { + char char1; + char char2; +}; diff --git a/cpp/ql/test/library-tests/structs/qualified_names/decls.cpp b/cpp/ql/test/library-tests/structs/qualified_names/decls.cpp new file mode 100644 index 000000000000..c6cde7b00b90 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/qualified_names/decls.cpp @@ -0,0 +1,5 @@ +namespace foo { + class C; + + C *x; +} diff --git a/cpp/ql/test/library-tests/structs/qualified_names/defs.cpp b/cpp/ql/test/library-tests/structs/qualified_names/defs.cpp new file mode 100644 index 000000000000..095f84126044 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/qualified_names/defs.cpp @@ -0,0 +1,29 @@ +namespace foo { + class C { + }; +} + +namespace bar { + class C { + }; +} + +class DefinedAndDeclared { +}; + +// Despite this declaration being present, the variable below is associated +// with the definition above rather than this declaration. +class DefinedAndDeclared; + +DefinedAndDeclared *definedAndDeclared; + +#include "header.h" + +// Because there are multiple definitions of `MultipleDefsButSameHeader`, the +// type of this variable will refer to the declaration in `header.h` rather +// than any of the definitions. +MultipleDefsButSameHeader *mdbsh; + +// Because there is only one definition of `OneDefInDifferentFile`, the type of +// this variable will refer to that definition. +OneDefInDifferentFile *odidf; diff --git a/cpp/ql/test/library-tests/structs/qualified_names/header.h b/cpp/ql/test/library-tests/structs/qualified_names/header.h new file mode 100644 index 000000000000..d410b0d35333 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/qualified_names/header.h @@ -0,0 +1,3 @@ +struct MultipleDefsButSameHeader; + +struct OneDefInDifferentFile; diff --git a/cpp/ql/test/library-tests/structs/qualified_names/pointerBaseType.expected b/cpp/ql/test/library-tests/structs/qualified_names/pointerBaseType.expected new file mode 100644 index 000000000000..1573f1c446e2 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/qualified_names/pointerBaseType.expected @@ -0,0 +1,4 @@ +| decls.cpp:4:6:4:6 | x | decls.cpp:2:9:2:9 | C | +| defs.cpp:18:21:18:38 | definedAndDeclared | defs.cpp:11:7:11:24 | DefinedAndDeclared | +| defs.cpp:25:28:25:32 | mdbsh | header.h:1:8:1:32 | MultipleDefsButSameHeader | +| defs.cpp:29:24:29:28 | odidf | c1.cpp:7:8:7:28 | OneDefInDifferentFile | diff --git a/cpp/ql/test/library-tests/structs/qualified_names/pointerBaseType.ql b/cpp/ql/test/library-tests/structs/qualified_names/pointerBaseType.ql new file mode 100644 index 000000000000..b2aeaee63488 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/qualified_names/pointerBaseType.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable x +where exists(x.getFile().getRelativePath()) +select x, x.getType().(PointerType).getBaseType() diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.expected b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.expected index 4d2706e2e952..dc73e749a4cd 100644 --- a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.expected +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.expected @@ -116,32 +116,13 @@ | isfromtemplateinstantiation.cpp:134:29:134:29 | Outer::operator=(const Outer &) | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | | isfromtemplateinstantiation.cpp:134:29:134:29 | declaration of operator= | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | | isfromtemplateinstantiation.cpp:134:29:134:29 | declaration of operator= | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | -| isfromtemplateinstantiation.cpp:135:31:135:31 | Outer::Inner::operator=(Inner &&) | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | | isfromtemplateinstantiation.cpp:135:31:135:31 | Outer::Inner::operator=(Inner &&) | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | -| isfromtemplateinstantiation.cpp:135:31:135:31 | Outer::Inner::operator=(const Inner &) | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | | isfromtemplateinstantiation.cpp:135:31:135:31 | Outer::Inner::operator=(const Inner &) | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | -| isfromtemplateinstantiation.cpp:135:31:135:31 | Outer::Inner::operator=(Inner &&) | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | -| isfromtemplateinstantiation.cpp:135:31:135:31 | Outer::Inner::operator=(Inner &&) | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | -| isfromtemplateinstantiation.cpp:135:31:135:31 | Outer::Inner::operator=(const Inner &) | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | -| isfromtemplateinstantiation.cpp:135:31:135:31 | Outer::Inner::operator=(const Inner &) | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | -| isfromtemplateinstantiation.cpp:135:31:135:31 | declaration of operator= | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | -| isfromtemplateinstantiation.cpp:135:31:135:31 | declaration of operator= | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | | isfromtemplateinstantiation.cpp:135:31:135:31 | declaration of operator= | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | | isfromtemplateinstantiation.cpp:135:31:135:31 | declaration of operator= | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | -| isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | -| isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | -| isfromtemplateinstantiation.cpp:135:31:135:35 | definition of Inner | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | -| isfromtemplateinstantiation.cpp:136:7:136:7 | definition of x | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | -| isfromtemplateinstantiation.cpp:136:7:136:7 | definition of x | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | | isfromtemplateinstantiation.cpp:136:7:136:7 | definition of x | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | -| isfromtemplateinstantiation.cpp:136:7:136:7 | x | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | -| isfromtemplateinstantiation.cpp:136:7:136:7 | x | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | | isfromtemplateinstantiation.cpp:136:7:136:7 | x | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | -| isfromtemplateinstantiation.cpp:137:7:137:7 | definition of y | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | -| isfromtemplateinstantiation.cpp:137:7:137:7 | definition of y | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | | isfromtemplateinstantiation.cpp:137:7:137:7 | definition of y | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | -| isfromtemplateinstantiation.cpp:137:7:137:7 | y | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | -| isfromtemplateinstantiation.cpp:137:7:137:7 | y | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | | isfromtemplateinstantiation.cpp:137:7:137:7 | y | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | | load.cpp:13:7:13:7 | basic_text_iprimitive::basic_text_iprimitive(basic_text_iprimitive &&) | load.cpp:13:7:13:27 | basic_text_iprimitive | | load.cpp:13:7:13:7 | basic_text_iprimitive::basic_text_iprimitive(const basic_text_iprimitive &) | load.cpp:13:7:13:27 | basic_text_iprimitive | diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.expected b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.expected index 38fda5364eef..74a096dff1aa 100644 --- a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.expected +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.expected @@ -3,6 +3,7 @@ isFromUninstantiatedTemplate | file://:0:0:0:0 | 0 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | | file://:0:0:0:0 | (int)... | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | | file://:0:0:0:0 | (int)... | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| file://:0:0:0:0 | Inner | file://:0:0:0:0 | Inner | | file://:0:0:0:0 | declaration of 1st parameter | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | | file://:0:0:0:0 | declaration of 1st parameter | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | | file://:0:0:0:0 | initializer for MyClassEnumConst | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | @@ -433,15 +434,15 @@ isFromUninstantiatedTemplate | isfromtemplateinstantiation.cpp:135:31:135:31 | declaration of operator= | I | T | DeclarationEntry | | | isfromtemplateinstantiation.cpp:135:31:135:31 | operator= | I | T | Declaration | | | isfromtemplateinstantiation.cpp:135:31:135:31 | operator= | I | T | Declaration | | -| isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | I | T | Declaration | | +| isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | | T | Declaration | | | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | I | T | Declaration | | +| isfromtemplateinstantiation.cpp:136:7:136:7 | definition of x | | T | Definition | | | isfromtemplateinstantiation.cpp:136:7:136:7 | definition of x | I | T | Definition | | -| isfromtemplateinstantiation.cpp:136:7:136:7 | definition of x | I | T | Definition | | -| isfromtemplateinstantiation.cpp:136:7:136:7 | x | I | T | Declaration | | +| isfromtemplateinstantiation.cpp:136:7:136:7 | x | | T | Declaration | | | isfromtemplateinstantiation.cpp:136:7:136:7 | x | I | T | Declaration | | +| isfromtemplateinstantiation.cpp:137:7:137:7 | definition of y | | T | Definition | | | isfromtemplateinstantiation.cpp:137:7:137:7 | definition of y | I | T | Definition | | -| isfromtemplateinstantiation.cpp:137:7:137:7 | definition of y | I | T | Definition | | -| isfromtemplateinstantiation.cpp:137:7:137:7 | y | I | T | Declaration | | +| isfromtemplateinstantiation.cpp:137:7:137:7 | y | | T | Declaration | | | isfromtemplateinstantiation.cpp:137:7:137:7 | y | I | T | Declaration | | | isfromtemplateinstantiation.cpp:144:28:144:56 | incomplete_never_instantiated | | T | Declaration | | | isfromtemplateinstantiation.cpp:146:28:146:45 | never_instantiated | | T | Declaration | |