Skip to content

Commit 490dd0e

Browse files
authored
Merge pull request #1245 from geoffw0/classesmanyfields
CPP: Fix performance issues in ClassesWithManyFields.ql
2 parents d4e1bae + efa3c77 commit 490dd0e

File tree

3 files changed

+149
-63
lines changed

3 files changed

+149
-63
lines changed

cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.ql

Lines changed: 112 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212

1313
import cpp
1414

15+
/**
16+
* Gets a string describing the kind of a `Class`.
17+
*/
1518
string kindstr(Class c) {
1619
exists(int kind | usertypes(unresolveElement(c), _, kind) |
1720
kind = 1 and result = "Struct"
@@ -22,76 +25,129 @@ string kindstr(Class c) {
2225
)
2326
}
2427

28+
/**
29+
* Holds if the arguments correspond to information about a `VariableDeclarationEntry`.
30+
*/
2531
predicate vdeInfo(VariableDeclarationEntry vde, Class c, File f, int line) {
2632
c = vde.getVariable().getDeclaringType() and
2733
f = vde.getLocation().getFile() and
2834
line = vde.getLocation().getStartLine()
2935
}
3036

31-
predicate previousVde(VariableDeclarationEntry previous, VariableDeclarationEntry vde) {
32-
exists(Class c, File f, int line | vdeInfo(vde, c, f, line) |
33-
vdeInfo(previous, c, f, line - 3)
34-
or
35-
vdeInfo(previous, c, f, line - 2)
36-
or
37-
vdeInfo(previous, c, f, line - 1)
38-
or
39-
vdeInfo(previous, c, f, line) and
40-
exists(int prevCol, int vdeCol |
41-
prevCol = previous.getLocation().getStartColumn() and
42-
vdeCol = vde.getLocation().getStartColumn()
43-
|
44-
prevCol < vdeCol
45-
or
46-
prevCol = vdeCol and previous.getName() < vde.getName()
47-
)
48-
)
49-
}
37+
newtype TVariableDeclarationInfo =
38+
TVariableDeclarationLine(Class c, File f, int line) { vdeInfo(_, c, f, line) }
5039

51-
predicate masterVde(VariableDeclarationEntry master, VariableDeclarationEntry vde) {
52-
not previousVde(_, vde) and master = vde
53-
or
54-
exists(VariableDeclarationEntry previous |
55-
previousVde(previous, vde) and masterVde(master, previous)
56-
)
40+
/**
41+
* A line that contains one or more `VariableDeclarationEntry`s (in the same class).
42+
*/
43+
class VariableDeclarationLine extends TVariableDeclarationInfo {
44+
Class c;
45+
46+
File f;
47+
48+
int line;
49+
50+
VariableDeclarationLine() {
51+
vdeInfo(_, c, f, line) and
52+
this = TVariableDeclarationLine(c, f, line)
53+
}
54+
55+
/**
56+
* Gets the class associated with this `VariableDeclarationLine`.
57+
*/
58+
Class getClass() { result = c }
59+
60+
/**
61+
* Gets the line of this `VariableDeclarationLine`.
62+
*/
63+
int getLine() { result = line }
64+
65+
/**
66+
* Gets a `VariableDeclarationEntry` on this line.
67+
*/
68+
VariableDeclarationEntry getAVDE() { vdeInfo(result, c, f, line) }
69+
70+
/**
71+
* Gets the start column of the first `VariableDeclarationEntry` on this line.
72+
*/
73+
int getStartColumn() { result = min(getAVDE().getLocation().getStartColumn()) }
74+
75+
/**
76+
* Gets the end column of the last `VariableDeclarationEntry` on this line.
77+
*/
78+
int getEndColumn() { result = max(getAVDE().getLocation().getEndColumn()) }
79+
80+
/**
81+
* Gets the rank of this `VariableDeclarationLine` in its file and class
82+
* (that is, the first is 0, the second is 1 and so on).
83+
*/
84+
private int getRank() {
85+
line = rank[result](VariableDeclarationLine vdl, int l |
86+
vdl = TVariableDeclarationLine(c, f, l)
87+
|
88+
l
89+
)
90+
}
91+
92+
/**
93+
* Gets the `VariableDeclarationLine` following this one, if any.
94+
*/
95+
VariableDeclarationLine getNext() {
96+
result = TVariableDeclarationLine(c, f, _) and
97+
result.getRank() = getRank() + 1
98+
}
99+
100+
/**
101+
* Gets the `VariableDeclarationLine` following this one, if it is nearby.
102+
*/
103+
VariableDeclarationLine getProximateNext() {
104+
result = getNext() and
105+
result.getLine() <= this.getLine() + 3
106+
}
107+
108+
string toString() { result = "VariableDeclarationLine" }
57109
}
58110

59-
class VariableDeclarationGroup extends ElementBase {
111+
/**
112+
* A group of `VariableDeclarationEntry`s in the same class that are approximately
113+
* contiguous.
114+
*/
115+
class VariableDeclarationGroup extends VariableDeclarationLine {
116+
VariableDeclarationLine end;
117+
60118
VariableDeclarationGroup() {
61-
this instanceof VariableDeclarationEntry and
62-
not previousVde(_, this)
119+
// there is no `VariableDeclarationLine` within three lines previously
120+
not any(VariableDeclarationLine prev).getProximateNext() = this and
121+
// `end` is the last transitively proximate line
122+
end = getProximateNext*() and
123+
not exists(end.getProximateNext())
63124
}
64125

65-
Class getClass() { vdeInfo(this, result, _, _) }
66-
67-
// pragma[noopt] since otherwise the two locationInfo relations get join-ordered
68-
// after each other
69-
pragma[noopt]
70126
predicate hasLocationInfo(string path, int startline, int startcol, int endline, int endcol) {
71-
exists(VariableDeclarationEntry last, Location lstart, Location lend |
72-
masterVde(this, last) and
73-
this instanceof VariableDeclarationGroup and
74-
not previousVde(last, _) and
75-
exists(VariableDeclarationEntry vde |
76-
vde = this and vde instanceof VariableDeclarationEntry and vde.getLocation() = lstart
77-
) and
78-
last.getLocation() = lend and
79-
lstart.hasLocationInfo(path, startline, startcol, _, _) and
80-
lend.hasLocationInfo(path, _, _, endline, endcol)
81-
)
127+
path = f.getAbsolutePath() and
128+
startline = getLine() and
129+
startcol = getStartColumn() and
130+
endline = end.getLine() and
131+
endcol = end.getEndColumn()
82132
}
83133

84-
string describeGroup() {
85-
if previousVde(this, _)
86-
then
87-
result = "group of " +
88-
strictcount(string name |
89-
exists(VariableDeclarationEntry vde |
90-
masterVde(this, vde) and
91-
name = vde.getName()
92-
)
93-
) + " fields here"
94-
else result = "declaration of " + this.(VariableDeclarationEntry).getVariable().getName()
134+
/**
135+
* Gets the number of uniquely named `VariableDeclarationEntry`s in this group.
136+
*/
137+
int getCount() {
138+
result = count(VariableDeclarationLine l |
139+
l = getProximateNext*()
140+
|
141+
l.getAVDE().getVariable().getName()
142+
)
143+
}
144+
145+
override string toString() {
146+
getCount() = 1 and
147+
result = "declaration of " + getAVDE().getVariable().getName()
148+
or
149+
getCount() > 1 and
150+
result = "group of " + getCount() + " fields here"
95151
}
96152
}
97153

@@ -129,4 +185,4 @@ where
129185
if c.hasOneVariableGroup() then suffix = "" else suffix = " - see $@"
130186
select c,
131187
kindstr(c) + " " + c.getName() + " has " + n +
132-
" fields; we suggest refactoring to 15 fields or fewer" + suffix + ".", vdg, vdg.describeGroup()
188+
" fields; we suggest refactoring to 15 fields or fewer" + suffix + ".", vdg, vdg.toString()

cpp/ql/test/query-tests/Architecture/Refactoring Opportunities/ClassesWithManyFields/cwmf.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,29 @@ struct MyParticle {
5656
class texture *tex;
5757
float u1, v1, u2, v2;
5858
};
59+
60+
struct MyAlphaClass1 {
61+
int a1, b1, c1, d1, e1, f1, g1, h1, i1, j1;
62+
int k1, l1, m1, n1, o1, p1, q1, r1, s1, t1;
63+
int u1, v1, w1, x1, y1, z1;
64+
65+
// ...
66+
// ...
67+
// ...
68+
69+
int a2, b2, c2, d2, e2, f2, g2, h2, i2, j2;
70+
int k2, l2, m2, n2, o2, p2, q2, r2, s2, t2;
71+
int u2, v2, w2, x2, y2, z2;
72+
};
73+
74+
struct MyAlphaClass2 {
75+
int x;
76+
77+
// ...
78+
// ...
79+
// ...
80+
81+
int a1, b1, c1, d1, e1, f1, g1, h1, i1, j1;
82+
int k1, l1, m1, n1, o1, p1, q1, r1, s1, t1;
83+
int u1, v1, w1, x1, y1, z1;
84+
};
Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
| cwmf.cpp:8:3:9:12 | aa | Struct aa has 20 fields; we suggest refactoring to 15 fields or fewer. | cwmf.cpp:8:3:9:12 | definition of f1 | group of 20 fields here |
2-
| cwmf.cpp:13:3:14:12 | bb | Class bb has 20 fields; we suggest refactoring to 15 fields or fewer. | cwmf.cpp:13:3:14:12 | definition of f1 | group of 20 fields here |
3-
| cwmf.cpp:24:3:25:12 | dd<T> | Template class dd<T> has 20 fields; we suggest refactoring to 15 fields or fewer. | cwmf.cpp:24:3:25:12 | definition of f1 | group of 20 fields here |
4-
| cwmf.cpp:30:3:31:12 | ee<U> | Template class ee<U> has 20 fields; we suggest refactoring to 15 fields or fewer. | cwmf.cpp:30:3:31:12 | definition of f1 | group of 20 fields here |
5-
| cwmf.cpp:41:8:57:22 | MyParticle | Struct MyParticle has 30 fields; we suggest refactoring to 15 fields or fewer. | cwmf.cpp:41:8:57:22 | definition of isActive | group of 30 fields here |
6-
| different_types.h:15:15:33:10 | DifferentTypes2 | Class DifferentTypes2 has 18 fields; we suggest refactoring to 15 fields or fewer. | different_types.h:15:15:33:10 | definition of i1 | group of 18 fields here |
7-
| different_types.h:15:15:33:10 | DifferentTypes2 | Class DifferentTypes2 has 18 fields; we suggest refactoring to 15 fields or fewer. | different_types.h:15:15:33:10 | definition of i1 | group of 18 fields here |
1+
| cwmf.cpp:8:3:9:12 | aa | Struct aa has 20 fields; we suggest refactoring to 15 fields or fewer. | cwmf.cpp:8:3:9:12 | group of 20 fields here | group of 20 fields here |
2+
| cwmf.cpp:13:3:14:12 | bb | Class bb has 20 fields; we suggest refactoring to 15 fields or fewer. | cwmf.cpp:13:3:14:12 | group of 20 fields here | group of 20 fields here |
3+
| cwmf.cpp:24:3:25:12 | dd<T> | Template class dd<T> has 20 fields; we suggest refactoring to 15 fields or fewer. | cwmf.cpp:24:3:25:12 | group of 20 fields here | group of 20 fields here |
4+
| cwmf.cpp:30:3:31:12 | ee<U> | Template class ee<U> has 20 fields; we suggest refactoring to 15 fields or fewer. | cwmf.cpp:30:3:31:12 | group of 20 fields here | group of 20 fields here |
5+
| cwmf.cpp:41:8:57:22 | MyParticle | Struct MyParticle has 30 fields; we suggest refactoring to 15 fields or fewer. | cwmf.cpp:41:8:57:22 | group of 30 fields here | group of 30 fields here |
6+
| cwmf.cpp:60:8:60:20 | MyAlphaClass1 | Struct MyAlphaClass1 has 52 fields; we suggest refactoring to 15 fields or fewer - see $@. | cwmf.cpp:61:7:63:28 | group of 26 fields here | group of 26 fields here |
7+
| cwmf.cpp:60:8:60:20 | MyAlphaClass1 | Struct MyAlphaClass1 has 52 fields; we suggest refactoring to 15 fields or fewer - see $@. | cwmf.cpp:69:7:71:28 | group of 26 fields here | group of 26 fields here |
8+
| cwmf.cpp:74:8:74:20 | MyAlphaClass2 | Struct MyAlphaClass2 has 27 fields; we suggest refactoring to 15 fields or fewer - see $@. | cwmf.cpp:75:7:75:7 | declaration of x | declaration of x |
9+
| cwmf.cpp:74:8:74:20 | MyAlphaClass2 | Struct MyAlphaClass2 has 27 fields; we suggest refactoring to 15 fields or fewer - see $@. | cwmf.cpp:81:7:83:28 | group of 26 fields here | group of 26 fields here |
10+
| different_types.h:15:15:33:10 | DifferentTypes2 | Class DifferentTypes2 has 18 fields; we suggest refactoring to 15 fields or fewer. | different_types.h:15:15:33:10 | group of 18 fields here | group of 18 fields here |
11+
| different_types.h:15:15:33:10 | DifferentTypes2 | Class DifferentTypes2 has 18 fields; we suggest refactoring to 15 fields or fewer. | different_types.h:15:15:33:10 | group of 18 fields here | group of 18 fields here |

0 commit comments

Comments
 (0)