Merge pull request #1245 from geoffw0/classesmanyfields

CPP: Fix performance issues in ClassesWithManyFields.ql
This commit is contained in:
Jonas Jensen
2019-05-01 13:58:28 +02:00
committed by GitHub
3 changed files with 149 additions and 63 deletions

View File

@@ -12,6 +12,9 @@
import cpp
/**
* Gets a string describing the kind of a `Class`.
*/
string kindstr(Class c) {
exists(int kind | usertypes(unresolveElement(c), _, kind) |
kind = 1 and result = "Struct"
@@ -22,76 +25,129 @@ string kindstr(Class c) {
)
}
/**
* Holds if the arguments correspond to information about a `VariableDeclarationEntry`.
*/
predicate vdeInfo(VariableDeclarationEntry vde, Class c, File f, int line) {
c = vde.getVariable().getDeclaringType() and
f = vde.getLocation().getFile() and
line = vde.getLocation().getStartLine()
}
predicate previousVde(VariableDeclarationEntry previous, VariableDeclarationEntry vde) {
exists(Class c, File f, int line | vdeInfo(vde, c, f, line) |
vdeInfo(previous, c, f, line - 3)
or
vdeInfo(previous, c, f, line - 2)
or
vdeInfo(previous, c, f, line - 1)
or
vdeInfo(previous, c, f, line) and
exists(int prevCol, int vdeCol |
prevCol = previous.getLocation().getStartColumn() and
vdeCol = vde.getLocation().getStartColumn()
|
prevCol < vdeCol
or
prevCol = vdeCol and previous.getName() < vde.getName()
)
)
newtype TVariableDeclarationInfo =
TVariableDeclarationLine(Class c, File f, int line) { vdeInfo(_, c, f, line) }
/**
* A line that contains one or more `VariableDeclarationEntry`s (in the same class).
*/
class VariableDeclarationLine extends TVariableDeclarationInfo {
Class c;
File f;
int line;
VariableDeclarationLine() {
vdeInfo(_, c, f, line) and
this = TVariableDeclarationLine(c, f, line)
}
/**
* Gets the class associated with this `VariableDeclarationLine`.
*/
Class getClass() { result = c }
/**
* Gets the line of this `VariableDeclarationLine`.
*/
int getLine() { result = line }
/**
* Gets a `VariableDeclarationEntry` on this line.
*/
VariableDeclarationEntry getAVDE() { vdeInfo(result, c, f, line) }
/**
* Gets the start column of the first `VariableDeclarationEntry` on this line.
*/
int getStartColumn() { result = min(getAVDE().getLocation().getStartColumn()) }
/**
* Gets the end column of the last `VariableDeclarationEntry` on this line.
*/
int getEndColumn() { result = max(getAVDE().getLocation().getEndColumn()) }
/**
* Gets the rank of this `VariableDeclarationLine` in its file and class
* (that is, the first is 0, the second is 1 and so on).
*/
private int getRank() {
line = rank[result](VariableDeclarationLine vdl, int l |
vdl = TVariableDeclarationLine(c, f, l)
|
l
)
}
/**
* Gets the `VariableDeclarationLine` following this one, if any.
*/
VariableDeclarationLine getNext() {
result = TVariableDeclarationLine(c, f, _) and
result.getRank() = getRank() + 1
}
/**
* Gets the `VariableDeclarationLine` following this one, if it is nearby.
*/
VariableDeclarationLine getProximateNext() {
result = getNext() and
result.getLine() <= this.getLine() + 3
}
string toString() { result = "VariableDeclarationLine" }
}
predicate masterVde(VariableDeclarationEntry master, VariableDeclarationEntry vde) {
not previousVde(_, vde) and master = vde
or
exists(VariableDeclarationEntry previous |
previousVde(previous, vde) and masterVde(master, previous)
)
}
/**
* A group of `VariableDeclarationEntry`s in the same class that are approximately
* contiguous.
*/
class VariableDeclarationGroup extends VariableDeclarationLine {
VariableDeclarationLine end;
class VariableDeclarationGroup extends ElementBase {
VariableDeclarationGroup() {
this instanceof VariableDeclarationEntry and
not previousVde(_, this)
// there is no `VariableDeclarationLine` within three lines previously
not any(VariableDeclarationLine prev).getProximateNext() = this and
// `end` is the last transitively proximate line
end = getProximateNext*() and
not exists(end.getProximateNext())
}
Class getClass() { vdeInfo(this, result, _, _) }
// pragma[noopt] since otherwise the two locationInfo relations get join-ordered
// after each other
pragma[noopt]
predicate hasLocationInfo(string path, int startline, int startcol, int endline, int endcol) {
exists(VariableDeclarationEntry last, Location lstart, Location lend |
masterVde(this, last) and
this instanceof VariableDeclarationGroup and
not previousVde(last, _) and
exists(VariableDeclarationEntry vde |
vde = this and vde instanceof VariableDeclarationEntry and vde.getLocation() = lstart
) and
last.getLocation() = lend and
lstart.hasLocationInfo(path, startline, startcol, _, _) and
lend.hasLocationInfo(path, _, _, endline, endcol)
)
path = f.getAbsolutePath() and
startline = getLine() and
startcol = getStartColumn() and
endline = end.getLine() and
endcol = end.getEndColumn()
}
string describeGroup() {
if previousVde(this, _)
then
result = "group of " +
strictcount(string name |
exists(VariableDeclarationEntry vde |
masterVde(this, vde) and
name = vde.getName()
)
) + " fields here"
else result = "declaration of " + this.(VariableDeclarationEntry).getVariable().getName()
/**
* Gets the number of uniquely named `VariableDeclarationEntry`s in this group.
*/
int getCount() {
result = count(VariableDeclarationLine l |
l = getProximateNext*()
|
l.getAVDE().getVariable().getName()
)
}
override string toString() {
getCount() = 1 and
result = "declaration of " + getAVDE().getVariable().getName()
or
getCount() > 1 and
result = "group of " + getCount() + " fields here"
}
}
@@ -129,4 +185,4 @@ where
if c.hasOneVariableGroup() then suffix = "" else suffix = " - see $@"
select c,
kindstr(c) + " " + c.getName() + " has " + n +
" fields; we suggest refactoring to 15 fields or fewer" + suffix + ".", vdg, vdg.describeGroup()
" fields; we suggest refactoring to 15 fields or fewer" + suffix + ".", vdg, vdg.toString()