p: Project = input;
out: output set of array of Statement;
projects: set of string;
methods: set of array of Statement;
files: set of string;
imports_List: bool;
uses_List: bool;
visit(p, visitor {
before repo: CodeRepository -> {
# Visit only newest snapshot.
snapshot := getsnapshot(repo, "SOURCE_JAVA_JLS");
foreach (i: int; def(snapshot[i])) {
visit(snapshot[i]);
}
stop;
}
before f: ChangedFile -> {
if (contains(projects,
p.name) || contains(files,
f.name) || match("test", lowercase(
f.name))) stop;
}
before astRoot: ASTRoot -> {
imports: = astRoot.imports;
imports_List = false;
# Check imports to know whether simple type references match the type.
# `java.lang.*` types are always implicitly imported.
foreach (i: int; def(imports[i])) {
if ((imports[i] == "java.util.List") || (imports[i] == "java.util.*")) {
imports_List = true;
}
}
}
before method: Method -> {
if (contains(projects,
p.name)) stop;
# Searching for methods that use _all_ requested type, hence, resetting uses.
uses_List = false;
}
before t: Type -> {
# Check type literals.
if ((imports_List &&
t.name == "List") || (
t.name == "java.util.List")) {
uses_List = true;
}
}
before variable: Variable -> {
# Check variable/parameter types.
uses_List = true;
}
}
before e: Expression -> {
# Check static method call receivers.
if (e.kind == ExpressionKind.METHODCALL) {
# BOA does not distinguish static calls from calls on variables. We assume a match, if the variable
# name matches the simple type name and the type is imported. This causes false positives, if a
# variable shadows the type.
exists(i: int; e.expressions[i].kind == ExpressionKind.VARACCESS) {
if ((imports_List && e.expressions[i].variable == "List") || (e.expressions[i].variable == "java.util.List")) {
uses_List = true;
}
}
}
}
after method: Method -> {
if (uses_List) {
out << method.statements;
add(methods, method.statements);
}
}
});