CPP: Rewrite the VDE grouping in ClassesWithManyField.ql to be more performant (and modern).

This commit is contained in:
Geoffrey White
2019-04-12 17:59:19 +01:00
parent 0cc4b23484
commit 89c26ca394
2 changed files with 128 additions and 65 deletions

View File

@@ -33,70 +33,133 @@ predicate vdeInfo(VariableDeclarationEntry vde, Class c, File f, int line)
line = vde.getLocation().getStartLine()
}
/**
* Holds if `previous` describes a `VariableDeclarationEntry` occurring soon before
* `vde` (this may have many results).
*/
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())
))
)
}
/**
* The first `VariableDeclarationEntry` in a group.
*/
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 `VariableDeclaratinEntry`'s in the same class and in close proximity
* to each other.
*/
class VariableDeclarationGroup extends ElementBase {
VariableDeclarationGroup() {
this instanceof VariableDeclarationEntry and
not previousVde(_, this)
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() {
vdeInfo(this, result, _, _)
result = c
}
// 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)
/**
* 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 it's 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
)
}
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 `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"
}
}
/**
* A group of `VariableDeclarationEntry`s in the same class that are approximately
* contiguous.
*/
class VariableDeclarationGroup extends VariableDeclarationLine
{
VariableDeclarationLine end;
VariableDeclarationGroup() {
// 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())
}
predicate hasLocationInfo(string path, int startline, int startcol, int endline, int endcol) {
path = f.getAbsolutePath() and
startline = getLine() and
startcol = getStartColumn() and
endline = end.getLine() and
endcol = end.getEndColumn()
}
/**
* 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"
)
}
}
@@ -128,4 +191,4 @@ where n = strictcount(string fieldName
c = vdg.getClass() and
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()
vdg, vdg.toString()