use of spoon.reflect.code.CtInvocation in project spoon by INRIA.
the class CtScannerTest method testScannerCallsAllProperties.
@Test
public void testScannerCallsAllProperties() throws Exception {
// contract: CtScanner must visit all metamodel properties and use correct CtRole!
final Launcher launcher = new Launcher();
launcher.addInputResource("./src/main/java/spoon/reflect/");
launcher.run();
CtTypeReference<?> ctElementRef = launcher.getFactory().createCtTypeReference(CtElement.class);
CtTypeReference<?> ctRefRef = launcher.getFactory().createCtTypeReference(CtReference.class);
CtClass<?> scannerCtClass = (CtClass<?>) launcher.getFactory().Type().get(CtScanner.class);
List<String> problems = new ArrayList<>();
Set<String> ignoredInvocations = new HashSet(Arrays.asList("scan", "enter", "exit"));
SpoonMetaModel metaModel = new SpoonMetaModel(new File("./src/main/java"));
// collect all scanner visit methods, to check if all were checked
Map<String, CtMethod<?>> scannerVisitMethodsByName = new HashMap<>();
scannerCtClass.getAllMethods().forEach(m -> {
if (m.getSimpleName().startsWith("visit")) {
scannerVisitMethodsByName.put(m.getSimpleName(), m);
}
});
class Counter {
int nbChecks = 0;
}
Counter c = new Counter();
for (MetamodelConcept leafConcept : metaModel.getConcepts()) {
// we only consider leaf, actual classes of the metamodel (eg CtInvocation) and not abstract ones (eg CtModifiable)
if (leafConcept.getKind() != MMTypeKind.LEAF) {
continue;
}
CtMethod<?> visitMethod = scannerVisitMethodsByName.remove("visit" + leafConcept.getName());
assertNotNull("CtScanner#" + "visit" + leafConcept.getName() + "(...) not found", visitMethod);
Set<String> calledMethods = new HashSet<>();
Set<String> checkedMethods = new HashSet<>();
// go over the roles and the corresponding fields of this type
leafConcept.getRoleToProperty().forEach((role, mmField) -> {
if (mmField.isDerived()) {
// return of the lambda
return;
}
// ignore fields, which doesn't return CtElement
if (mmField.getItemValueType().isSubtypeOf(ctElementRef) == false) {
// return of the lambda
return;
}
MMMethod getter = mmField.getMethod(MMMethodKind.GET);
checkedMethods.add(getter.getSignature());
// System.out.println("checking "+m.getSignature() +" in "+visitMethod.getSignature());
// now, we collect at least one invocation to this getter in the visit method
CtInvocation invocation = visitMethod.filterChildren(new TypeFilter<CtInvocation>(CtInvocation.class) {
@Override
public boolean matches(CtInvocation element) {
if (ignoredInvocations.contains(element.getExecutable().getSimpleName())) {
return false;
}
calledMethods.add(element.getExecutable().getSignature());
return super.matches(element) && element.getExecutable().getSimpleName().equals(getter.getName());
}
}).first();
if (getter.getName().equals("getComments") && leafConcept.getModelInterface().isSubtypeOf(ctRefRef)) {
// one cannot set comments on references see the @UnsettableProperty of CtReference#setComments
return;
}
// contract: there ia at least one invocation to all non-derived, role-based getters in the visit method of the Scanner
if (invocation == null) {
problems.add("no " + getter.getSignature() + " in " + visitMethod);
} else {
c.nbChecks++;
// System.out.println(invocation.toString());
// contract: the scan method is called with the same role as the one set on field / property
CtRole expectedRole = metaModel.getRoleOfMethod((CtMethod<?>) invocation.getExecutable().getDeclaration());
CtInvocation<?> scanInvocation = invocation.getParent(CtInvocation.class);
String realRoleName = ((CtFieldRead<?>) scanInvocation.getArguments().get(0)).getVariable().getSimpleName();
if (expectedRole.name().equals(realRoleName) == false) {
problems.add("Wrong role " + realRoleName + " used in " + scanInvocation.getPosition());
}
}
});
calledMethods.removeAll(checkedMethods);
// contract: CtScanner only calls methods that have a role and the associated getter
if (calledMethods.size() > 0) {
problems.add("CtScanner " + visitMethod.getPosition() + " calls unexpected methods: " + calledMethods);
}
}
// contract: all visit* methods in CtScanner have been checked
if (scannerVisitMethodsByName.isEmpty() == false) {
problems.add("These CtScanner visit methods were not checked: " + scannerVisitMethodsByName.keySet());
}
if (problems.size() > 0) {
fail(String.join("\n", problems));
}
assertTrue("not enough checks", c.nbChecks >= 200);
}
use of spoon.reflect.code.CtInvocation in project spoon by INRIA.
the class ReplaceScanner method visitCtMethod.
@Override
public <T> void visitCtMethod(CtMethod<T> element) {
if (!element.getSimpleName().startsWith("visitCt")) {
return;
}
Factory factory = element.getFactory();
CtMethod<T> clone = element.clone();
factory.Annotation().annotate(clone, Override.class);
clone.getBody().getStatements().clear();
for (int i = 1; i < element.getBody().getStatements().size() - 1; i++) {
CtInvocation inv = element.getBody().getStatement(i);
List<CtExpression<?>> invArgs = new ArrayList<>(inv.getArguments());
if (invArgs.size() <= 1) {
throw new RuntimeException("You forget the role argument in line " + i + " of method " + element.getSimpleName() + " from " + element.getDeclaringType().getQualifiedName());
}
// remove role argument
invArgs.remove(0);
CtInvocation getter = (CtInvocation) invArgs.get(0);
if (clone.getComments().size() == 0) {
// Add auto-generated comment.
final CtComment comment = factory.Core().createComment();
comment.setCommentType(CtComment.CommentType.INLINE);
comment.setContent("auto-generated, see " + ReplacementVisitorGenerator.class.getName());
clone.addComment(comment);
}
Class actualClass = getter.getType().getActualClass();
CtInvocation<?> invocation = createInvocation(factory, element, invArgs, getter, actualClass);
clone.getBody().addStatement(invocation);
}
target.addMethod(clone);
}
use of spoon.reflect.code.CtInvocation in project spoon by INRIA.
the class AstCheckerTest method testAvoidSetCollectionSavedOnAST.
@Test
public void testAvoidSetCollectionSavedOnAST() throws Exception {
final Launcher launcher = new Launcher();
launcher.getEnvironment().setNoClasspath(true);
launcher.addInputResource("src/main/java");
launcher.buildModel();
final Factory factory = launcher.getFactory();
final List<CtTypeReference<?>> collectionsRef = //
Arrays.asList(//
factory.Type().createReference(Collection.class), //
factory.Type().createReference(List.class), //
factory.Type().createReference(Set.class), factory.Type().createReference(Map.class));
final List<CtInvocation<?>> invocations = Query.getElements(factory, new TypeFilter<CtInvocation<?>>(CtInvocation.class) {
@Override
public boolean matches(CtInvocation<?> element) {
if (!(element.getParent() instanceof CtInvocation)) {
return false;
}
final CtInvocation<?> parent = (CtInvocation<?>) element.getParent();
if (parent.getTarget() == null || !parent.getTarget().equals(element)) {
return false;
}
if (!element.getExecutable().getDeclaringType().getSimpleName().startsWith("Ct")) {
return false;
}
boolean isDataStructure = false;
for (int i = 0; i < collectionsRef.size(); i++) {
CtTypeReference<?> ctTypeReference = collectionsRef.get(i);
if (element.getType().isSubtypeOf(ctTypeReference)) {
isDataStructure = true;
break;
}
}
if (!isDataStructure) {
return false;
}
final String simpleName = parent.getExecutable().getSimpleName();
return simpleName.startsWith("add") || simpleName.startsWith("remove") || simpleName.startsWith("put");
}
});
if (invocations.size() > 0) {
final String error = //
invocations.stream().sorted(//
new CtLineElementComparator()).map(//
i -> "see " + i.getPosition().getFile().getAbsoluteFile() + " at " + i.getPosition().getLine()).collect(Collectors.joining(",\n"));
throw new AssertionError(error);
}
}
use of spoon.reflect.code.CtInvocation in project spoon by INRIA.
the class EqualTest method testEqualsEmptyException.
@Test
public void testEqualsEmptyException() throws Exception {
Factory factory = new Launcher().createFactory();
String realParam1 = "\"\"";
String content = "" + "class X {" + "public Object foo() {" + " Integer.getInteger(" + realParam1 + ");" + " return \"\";" + "}};";
SpoonModelBuilder builder = new JDTSnippetCompiler(factory, content);
try {
builder.build();
} catch (Exception e) {
e.printStackTrace();
fail("Unable create model");
}
CtClass<?> clazz1 = (CtClass<?>) factory.Type().getAll().get(0);
CtMethod<?> method = (CtMethod<?>) clazz1.getMethods().toArray()[0];
CtInvocation<?> invo = (CtInvocation<?>) method.getBody().getStatement(0);
CtLiteral<?> argument1 = (CtLiteral<?>) invo.getArguments().get(0);
assertEquals(realParam1, argument1.toString());
CtReturn<?> returnStatement = (CtReturn<?>) method.getBody().getStatement(1);
CtLiteral<?> returnExp = (CtLiteral<?>) returnStatement.getReturnedExpression();
assertEquals(realParam1, returnExp.toString());
try {
assertEquals(argument1, returnExp);
} catch (Exception e) {
fail(e.getMessage());
}
}
use of spoon.reflect.code.CtInvocation in project spoon by INRIA.
the class JDTTreeBuilder method visit.
@Override
public boolean visit(SingleNameReference singleNameReference, BlockScope scope) {
if (singleNameReference.binding instanceof FieldBinding) {
context.enter(helper.createFieldAccess(singleNameReference), singleNameReference);
} else if (singleNameReference.binding instanceof VariableBinding) {
context.enter(helper.createVariableAccess(singleNameReference), singleNameReference);
} else if (singleNameReference.binding instanceof TypeBinding) {
context.enter(factory.Code().createTypeAccessWithoutCloningReference(references.getTypeReference((TypeBinding) singleNameReference.binding)), singleNameReference);
} else if (singleNameReference.binding instanceof ProblemBinding) {
if (context.stack.peek().element instanceof CtInvocation && Character.isUpperCase(CharOperation.charToString(singleNameReference.token).charAt(0))) {
context.enter(helper.createTypeAccessNoClasspath(singleNameReference), singleNameReference);
} else {
context.enter(helper.createFieldAccessNoClasspath(singleNameReference), singleNameReference);
}
} else if (singleNameReference.binding == null) {
CtExpression access = helper.createVariableAccessNoClasspath(singleNameReference);
if (access == null) {
access = helper.createTypeAccessNoClasspath(singleNameReference);
}
context.enter(access, singleNameReference);
}
return true;
}
Aggregations