Skip to content

Commit cffaa8d

Browse files
Improve the stubbing query to handle more language constructs correctly
1 parent 40bb19e commit cffaa8d

File tree

1 file changed

+96
-33
lines changed

1 file changed

+96
-33
lines changed

java/ql/src/Stubs/Stubs.qll

Lines changed: 96 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import java
99

1010
/** A type that should be in the generated code. */
11-
abstract /*private*/ class GeneratedType extends RefType {
11+
abstract private class GeneratedType extends RefType {
1212
GeneratedType() {
1313
(
1414
this instanceof Interface
@@ -23,9 +23,9 @@ abstract /*private*/ class GeneratedType extends RefType {
2323
private string stubKeyword() {
2424
this instanceof Interface and result = "interface"
2525
or
26-
this instanceof Class and result = "class"
27-
// or
28-
// this instanceof Enum and result = "enum"
26+
this instanceof Class and result = "class" and not this instanceof EnumType
27+
or
28+
this instanceof EnumType and result = "enum"
2929
}
3030

3131
private string stubAbstractModifier() {
@@ -51,6 +51,7 @@ abstract /*private*/ class GeneratedType extends RefType {
5151
private RefType getAnInterestingBaseType() {
5252
result = this.getASupertype() and
5353
not result instanceof TypeObject and
54+
not this instanceof EnumType and
5455
result.getSourceDeclaration() != this
5556
}
5657

@@ -78,7 +79,9 @@ abstract /*private*/ class GeneratedType extends RefType {
7879

7980
language[monotonicAggregates]
8081
private string stubMembers() {
81-
result = concat(Member m | m = this.getAGeneratedMember() | stubMember(m))
82+
result =
83+
stubEnumConstants(this) + stubFakeConstructor(this) +
84+
concat(Member m | m = this.getAGeneratedMember() | stubMember(m))
8285
}
8386

8487
private Member getAGeneratedMember() {
@@ -175,7 +178,7 @@ private string stubAccessibility(Member m) {
175178
}
176179

177180
private string stubModifiers(Member m) {
178-
result = stubAccessibility(m) + stubStaticOrFinal(m) + stubAbstract(m)
181+
result = stubAccessibility(m) + stubStaticOrFinal(m) + stubAbstractOrDefault(m)
179182
}
180183

181184
private string stubStaticOrFinal(Member m) {
@@ -187,9 +190,9 @@ private string stubStaticOrFinal(Member m) {
187190
else result = ""
188191
}
189192

190-
private string stubAbstract(Member m) {
193+
private string stubAbstractOrDefault(Member m) {
191194
if m.getDeclaringType() instanceof Interface
192-
then result = ""
195+
then if m.isDefault() then result = "default " else result = ""
193196
else
194197
if m.isAbstract()
195198
then result = "abstract "
@@ -261,7 +264,7 @@ private string stubGenericMethodParams(Method m) {
261264
}
262265

263266
private string stubImplementation(Callable c) {
264-
if c.isAbstract() or c.getDeclaringType() instanceof Interface
267+
if c.isAbstract()
265268
then result = ";"
266269
else
267270
if c instanceof Constructor or c.getReturnType() instanceof VoidType
@@ -305,50 +308,109 @@ private string stubParameter(Parameter p) {
305308
)
306309
}
307310

308-
private string stubMember(Member m) {
309-
exists(Method c | m = c |
310-
result =
311-
" " + stubModifiers(c) + stubGenericMethodParams(c) + stubTypeName(c.getReturnType()) + " "
312-
+ c.getName() + "(" + stubParameters(c) + ")" + stubImplementation(c) + "\n"
313-
)
311+
private string stubEnumConstants(RefType t) {
312+
if t instanceof EnumType
313+
then
314+
exists(EnumType et | et = t |
315+
result =
316+
" " + concat(EnumConstant c | c = et.getAnEnumConstant() | c.getName(), ", ") + ";\n"
317+
)
318+
else result = ""
319+
}
320+
321+
// Holds if the member is to be excluded from stubMember
322+
private predicate excludedMember(Member m) {
323+
m instanceof EnumConstant
314324
or
315-
exists(Constructor c | m = c |
316-
result =
317-
" " + stubModifiers(m) + c.getName() + "(" + stubParameters(c) + ")" +
318-
stubImplementation(c) + "\n"
325+
exists(Method c | m = c |
326+
c.getDeclaringType() instanceof EnumType and
327+
m.hasName(["values", "valueOf"]) and
328+
m.isStatic()
319329
)
320-
or
321-
exists(Field f, string impl | f = m |
322-
/* and not f instanceof EnumConstant */
323-
/*if f.isConst() then impl = " = throw null" else*/ impl = "" and
324-
result =
325-
" " + stubModifiers(m) + stubTypeName(f.getType()) + " " + f.getName() + impl + ";\n"
330+
}
331+
332+
private string stubMember(Member m) {
333+
if excludedMember(m)
334+
then result = ""
335+
else (
336+
exists(Method c | m = c |
337+
result =
338+
" " + stubModifiers(c) + stubGenericMethodParams(c) + stubTypeName(c.getReturnType()) +
339+
" " + c.getName() + "(" + stubParameters(c) + ")" + stubImplementation(c) + "\n"
340+
)
341+
or
342+
exists(Constructor c | m = c |
343+
result =
344+
" " + stubModifiers(m) + c.getName() + "(" + stubParameters(c) + ")" +
345+
stubImplementation(c) + "\n"
346+
)
347+
or
348+
exists(Field f | f = m |
349+
result =
350+
" " + stubModifiers(m) + stubTypeName(f.getType()) + " " + f.getName() + " = " +
351+
stubDefaultValue(f.getType()) + ";\n"
352+
)
353+
or
354+
exists(NestedType nt | nt = m | result = indent(nt.(GeneratedType).getStub()))
326355
)
327-
or
328-
exists(NestedType nt | nt = m | result = indent(nt.(GeneratedType).getStub()))
329356
}
330357

331358
bindingset[s]
332359
private string indent(string s) { result = " " + s.replaceAll("\n", "\n ") + "\n" }
333360

334-
private TopLevelType getTopLevel(RefType t) {
335-
result = t or
336-
result = getTopLevel(t.(NestedType).getEnclosingType())
361+
// If a class's superclass doesn't have a no-arg constructor, then it won't compile when its constructor's bodies are stubbed
362+
// So we synthesise no-arg constructors for each generated type that doesn't have one.
363+
private string stubFakeConstructor(RefType t) {
364+
if not t instanceof Class
365+
then result = ""
366+
else
367+
exists(string mod |
368+
if t instanceof EnumType then mod = " private " else mod = " protected "
369+
|
370+
if hasNoArgConstructor(t) then result = "" else result = mod + t.getName() + "() {}\n"
371+
)
372+
}
373+
374+
private predicate hasNoArgConstructor(Class t) {
375+
exists(Constructor c | c.getDeclaringType() = t |
376+
c.getNumberOfParameters() = 0 and
377+
not c.isPrivate()
378+
)
379+
}
380+
381+
private RefType getAReferencedType(RefType t) {
382+
result = t.(GeneratedType).getAGeneratedType()
383+
or
384+
result =
385+
getAReferencedType(any(NestedType nt |
386+
nt.getEnclosingType().getSourceDeclaration() = t.getSourceDeclaration()
387+
))
388+
or
389+
exists(RefType t1 | t1 = getAReferencedType(t) |
390+
result = t1.(NestedType).getEnclosingType()
391+
or
392+
result = t1.getSourceDeclaration()
393+
or
394+
result = t1.(ParameterizedType).getATypeArgument()
395+
or
396+
result = t1.(Array).getElementType()
397+
)
337398
}
338399

400+
/** A top level type whose file should be stubbed */
339401
class GeneratedTopLevel extends TopLevelType {
340402
GeneratedTopLevel() {
341403
this = this.getSourceDeclaration() and
342404
this instanceof GeneratedType
343405
}
344406

345-
RefType getAReferencedType() {
346-
exists(GeneratedType t | this = getTopLevel(t) | result = getTopLevel(t.getAGeneratedType()))
407+
private TopLevelType getAnImportedType() {
408+
result = getAReferencedType(this).getSourceDeclaration()
347409
}
348410

349411
private string stubAnImport() {
350412
exists(RefType t, string pkg, string name |
351-
t = getAReferencedType().getSourceDeclaration() and
413+
t = getAnImportedType() and
352414
(t instanceof Class or t instanceof Interface) and
353415
t.hasQualifiedName(pkg, name) and
354416
t != this and
@@ -371,6 +433,7 @@ class GeneratedTopLevel extends TopLevelType {
371433
"// Generated automatically from " + this.getQualifiedName() + " for testing purposes\n\n"
372434
}
373435

436+
/** Creates a full stub for the file containing this type. */
374437
string stubFile() {
375438
result = stubComment() + stubPackage() + stubImports() + this.(GeneratedType).getStub() + "\n"
376439
}

0 commit comments

Comments
 (0)