Skip to content

Commit 4b0ab60

Browse files
authored
Merge pull request #202 from jbj/resolveClass-conservative
C++: more conservative resolveClass
2 parents dca93f5 + a7d8971 commit 4b0ab60

File tree

15 files changed

+125
-34
lines changed

15 files changed

+125
-34
lines changed

cpp/ql/src/semmle/code/cpp/internal/ResolveClass.qll

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,31 @@
11
import semmle.code.cpp.Type
22

3-
/** Holds if `d` is a complete class named `name`. */
3+
pragma[noinline]
4+
private string getTopLevelClassName(@usertype c) {
5+
isClass(c) and
6+
usertypes(c, result, _) and
7+
not namespacembrs(_, c) and // not in a namespace
8+
not member(_, _, c) and // not in some structure
9+
not class_instantiation(c, _) // not a template instantiation
10+
}
11+
12+
/** Holds if `d` is a unique complete class named `name`. */
413
pragma[noinline]
514
private predicate existsCompleteWithName(string name, @usertype d) {
6-
isClass(d) and
715
is_complete(d) and
8-
usertypes(d, name, _)
16+
name = getTopLevelClassName(d) and
17+
strictcount(@usertype other | is_complete(other) and getTopLevelClassName(other) = name) = 1
918
}
1019

1120
/** Holds if `c` is an incomplete class named `name`. */
1221
pragma[noinline]
1322
private predicate existsIncompleteWithName(string name, @usertype c) {
14-
isClass(c) and
1523
not is_complete(c) and
16-
usertypes(c, name, _)
24+
name = getTopLevelClassName(c)
1725
}
1826

1927
/**
20-
* Holds if `c` is an imcomplete class, and there exists a complete class `d`
28+
* Holds if `c` is an imcomplete class, and there exists a unique complete class `d`
2129
* with the same name.
2230
*/
2331
private predicate hasCompleteTwin(@usertype c, @usertype d) {
@@ -30,10 +38,8 @@ private predicate hasCompleteTwin(@usertype c, @usertype d) {
3038
import Cached
3139
cached private module Cached {
3240
/**
33-
* If `c` is incomplete, and there exists a complete class with the same name,
34-
* then the result is that complete class. Otherwise, the result is `c`. If
35-
* multiple complete classes have the same name, this predicate may have
36-
* multiple results.
41+
* If `c` is incomplete, and there exists a unique complete class with the same name,
42+
* then the result is that complete class. Otherwise, the result is `c`.
3743
*/
3844
cached @usertype resolveClass(@usertype c) {
3945
hasCompleteTwin(c, result)

cpp/ql/test/library-tests/structs/compatible_cpp/b1.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,9 @@ class Damson {
2424
int damson_x;
2525
void foo();
2626
};
27+
28+
namespace unrelated {
29+
class AppleCompatible {
30+
long apple_x;
31+
};
32+
}

cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
| b1.cpp:23:7:23:12 | Damson | 5 members | 2 locations | 1 | foo |
3333
| b1.cpp:23:7:23:12 | Damson | 5 members | 2 locations | 2 | operator= |
3434
| b1.cpp:23:7:23:12 | Damson | 5 members | 2 locations | 3 | operator= |
35+
| b1.cpp:29:9:29:23 | AppleCompatible | 3 members | 1 locations | 0 | apple_x |
36+
| b1.cpp:29:9:29:23 | AppleCompatible | 3 members | 1 locations | 1 | operator= |
37+
| b1.cpp:29:9:29:23 | AppleCompatible | 3 members | 1 locations | 2 | operator= |
3538
| b2.cpp:2:7:2:21 | AppleCompatible | 3 members | 2 locations | 0 | apple_x |
3639
| b2.cpp:2:7:2:21 | AppleCompatible | 3 members | 2 locations | 1 | operator= |
3740
| b2.cpp:2:7:2:21 | AppleCompatible | 3 members | 2 locations | 2 | operator= |

cpp/ql/test/library-tests/structs/compatible_cpp/compatible_types.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
| b1.cpp:11:7:11:22 | BananaCompatible | 0 | file://:0:0:0:0 | int | 1 types |
1111
| b1.cpp:16:7:16:12 | Cherry | 0 | file://:0:0:0:0 | int | 1 types |
1212
| b1.cpp:23:7:23:12 | Damson | 0 | file://:0:0:0:0 | int | 1 types |
13+
| b1.cpp:29:9:29:23 | AppleCompatible | 0 | file://:0:0:0:0 | long | 1 types |
1314
| b2.cpp:2:7:2:21 | AppleCompatible | 0 | file://:0:0:0:0 | int | 1 types |
1415
| b2.cpp:9:7:9:22 | BananaCompatible | 0 | file://:0:0:0:0 | int | 1 types |
1516
| b2.cpp:14:7:14:12 | Cherry | 0 | file://:0:0:0:0 | short | 1 types |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
| a.h:5:8:5:13 | cheese | y.cpp:4:8:4:10 | Foo | 3 |
22
| x.cpp:3:6:3:10 | bar_x | a.h:4:8:4:10 | Bar | 3 |
3+
| x.cpp:19:6:19:10 | foo_x | y.cpp:4:8:4:10 | Foo | 3 |
4+
| x.cpp:23:5:23:17 | templateField | x.cpp:6:10:6:12 | Foo | 3 |
5+
| x.cpp:23:5:23:17 | templateField | x.cpp:12:9:12:11 | Foo | 3 |
6+
| x.cpp:26:18:26:29 | template_foo | x.cpp:22:7:22:14 | Template<Foo *> | 0 |
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
11
#include "a.h"
22

33
Bar *bar_x;
4+
5+
namespace unrelated {
6+
struct Foo {
7+
short val;
8+
};
9+
}
10+
11+
struct ContainsAnotherFoo {
12+
class Foo {
13+
long val;
14+
};
15+
};
16+
17+
// The type of `foo_x` should not refer to any of the above classes, none of
18+
// which are named `Foo` in the global scope.
19+
Foo *foo_x;
20+
21+
template<typename T>
22+
class Template {
23+
T templateField;
24+
};
25+
26+
Template<Foo *> *template_foo;
27+
28+
// Instantiation of the template with unrelated classes named `Foo` should not
29+
// get mixed up with the instantiation above.
30+
template class Template<unrelated::Foo *>;
31+
template class Template<ContainsAnotherFoo::Foo *>;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include "header.h"
2+
3+
struct MultipleDefsButSameHeader {
4+
int i;
5+
};
6+
7+
struct OneDefInDifferentFile {
8+
int i;
9+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include "header.h"
2+
3+
struct MultipleDefsButSameHeader {
4+
char char1;
5+
char char2;
6+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace foo {
2+
class C;
3+
4+
C *x;
5+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
namespace foo {
2+
class C {
3+
};
4+
}
5+
6+
namespace bar {
7+
class C {
8+
};
9+
}
10+
11+
class DefinedAndDeclared {
12+
};
13+
14+
// Despite this declaration being present, the variable below is associated
15+
// with the definition above rather than this declaration.
16+
class DefinedAndDeclared;
17+
18+
DefinedAndDeclared *definedAndDeclared;
19+
20+
#include "header.h"
21+
22+
// Because there are multiple definitions of `MultipleDefsButSameHeader`, the
23+
// type of this variable will refer to the declaration in `header.h` rather
24+
// than any of the definitions.
25+
MultipleDefsButSameHeader *mdbsh;
26+
27+
// Because there is only one definition of `OneDefInDifferentFile`, the type of
28+
// this variable will refer to that definition.
29+
OneDefInDifferentFile *odidf;

0 commit comments

Comments
 (0)