Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: consolidate a consistent behavior for CtElement#getParent #3793

Merged
merged 10 commits into from
Feb 24, 2021
1 change: 1 addition & 0 deletions src/main/java/spoon/pattern/PatternBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ private CtTypeReference<?> getDeclaringTypeRef(List<? extends CtElement> templat
t = (CtType) ctElement;
type = mergeType(type, t);
}

t = ctElement.getParent(CtType.class);
if (t != null) {
type = mergeType(type, t);
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/spoon/reflect/declaration/CtElement.java
Original file line number Diff line number Diff line change
Expand Up @@ -250,13 +250,12 @@ <E extends CtElement> List<E> getAnnotatedChildren(
/**
* Gets the first parent that matches the given type.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you document this with an @return, in particular that it returns null if there's no match?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is based on the discussion we had previously about the poor documentation of null returns in Spoon :)

*/
<P extends CtElement> P getParent(Class<P> parentType) throws ParentNotInitializedException;
<P extends CtElement> P getParent(Class<P> parentType);

/**
* Gets the first parent that matches the filter.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

* If the receiver (this) matches the filter, it is also returned
*/
<E extends CtElement> E getParent(Filter<E> filter) throws ParentNotInitializedException;
<E extends CtElement> E getParent(Filter<E> filter);

/**
* Manually sets the parent element of the current element.
Expand Down
14 changes: 2 additions & 12 deletions src/main/java/spoon/reflect/visitor/DefaultJavaPrettyPrinter.java
Original file line number Diff line number Diff line change
Expand Up @@ -1301,12 +1301,7 @@ public <T> void visitCtInvocation(CtInvocation<T> invocation) {
if (invocation.getExecutable().isConstructor()) {
// It's a constructor (super or this)
elementPrinterHelper.writeActualTypeArguments(invocation.getExecutable());
CtType<?> parentType;
try {
parentType = invocation.getParent(CtType.class);
} catch (ParentNotInitializedException e) {
parentType = null;
}
CtType<?> parentType = invocation.getParent(CtType.class);
if (parentType == null || parentType.getQualifiedName() != null && parentType.getQualifiedName().equals(invocation.getExecutable().getDeclaringType().getQualifiedName())) {
printer.writeKeyword("this");
} else {
Expand Down Expand Up @@ -1460,13 +1455,8 @@ public <T> void visitCtAnnotationMethod(CtAnnotationMethod<T> annotationMethod)
@SuppressWarnings("rawtypes")
public <T> void visitCtNewArray(CtNewArray<T> newArray) {
enterCtExpression(newArray);
boolean isNotInAnnotation;
try {
isNotInAnnotation = (newArray.getParent(CtAnnotationType.class) == null) && (newArray.getParent(CtAnnotation.class) == null);
} catch (ParentNotInitializedException e) {
isNotInAnnotation = true;
}

boolean isNotInAnnotation = (newArray.getParent(CtAnnotationType.class) == null) && (newArray.getParent(CtAnnotation.class) == null);
if (isNotInAnnotation) {
CtTypeReference<?> ref = newArray.getType();

Expand Down
28 changes: 12 additions & 16 deletions src/main/java/spoon/support/StandardEnvironment.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtExecutable;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.ParentNotInitializedException;
import spoon.reflect.visitor.DefaultImportComparator;
import spoon.reflect.visitor.DefaultJavaPrettyPrinter;
import spoon.reflect.visitor.ForceFullyQualifiedProcessor;
Expand Down Expand Up @@ -247,22 +246,19 @@ public void report(Processor<?> processor, Level level, CtElement element, Strin
buffer.append(message);

// Add sourceposition (javac format)
try {
CtType<?> type = (element instanceof CtType) ? (CtType<?>) element : element.getParent(CtType.class);
SourcePosition sp = element.getPosition();

if (sp == null) {
buffer.append(" (Unknown Source)");
} else {
buffer.append(" at " + type.getQualifiedName() + ".");
CtExecutable<?> exe = (element instanceof CtExecutable) ? (CtExecutable<?>) element : element.getParent(CtExecutable.class);
if (exe != null) {
buffer.append(exe.getSimpleName());
}
buffer.append("(" + sp.getFile().getName() + ":" + sp.getLine() + ")");
CtType<?> type = (element instanceof CtType) ? (CtType<?>) element : element.getParent(CtType.class);
SourcePosition sp = element.getPosition();

if (sp == null) {
buffer.append(" (Unknown Source)");
} else {
// TODO: will explode if type == null
buffer.append(" at " + type.getQualifiedName() + ".");
CtExecutable<?> exe = (element instanceof CtExecutable) ? (CtExecutable<?>) element : element.getParent(CtExecutable.class);
if (exe != null) {
buffer.append(exe.getSimpleName());
}
} catch (ParentNotInitializedException e) {
buffer.append(" (invalid parent)");
buffer.append("(" + sp.getFile().getName() + ":" + sp.getLine() + ")");
}

print(buffer.toString(), level);
Expand Down
10 changes: 3 additions & 7 deletions src/main/java/spoon/support/reflect/code/CtStatementImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,10 @@ public static void insertBefore(CtStatement target, CtStatementList statementsTo
if (targetParent instanceof CtExecutable) {
throw new SpoonException("cannot insert in this context (use insertEnd?)");
}
try {
if (target.getParent(CtConstructor.class) != null) {
if (target instanceof CtInvocation && ((CtInvocation<?>) target).getExecutable().getSimpleName().startsWith(CtExecutableReference.CONSTRUCTOR_NAME)) {
throw new SpoonException("cannot insert a statement before a super or this invocation.");
}
if (target.getParent(CtConstructor.class) != null) {
if (target instanceof CtInvocation && ((CtInvocation<?>) target).getExecutable().getSimpleName().startsWith(CtExecutableReference.CONSTRUCTOR_NAME)) {
throw new SpoonException("cannot insert a statement before a super or this invocation.");
}
} catch (ParentNotInitializedException ignore) {
// no parent set somewhere
}
new InsertVisitor(target, statementsToBeInserted, InsertType.BEFORE).scan(targetParent);
}
Expand Down
34 changes: 16 additions & 18 deletions src/main/java/spoon/support/reflect/declaration/CtElementImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -382,35 +382,33 @@ public boolean isParentInitialized() {

@Override
@SuppressWarnings("unchecked")
public <P extends CtElement> P getParent(Class<P> parentType) throws ParentNotInitializedException {
if (parent == null) {
return null;
}
if (parentType.isAssignableFrom(getParent().getClass())) {
return (P) getParent();
public <P extends CtElement> P getParent(Class<P> parentType) {
CtElement current = this;
while (current.isParentInitialized()) {
current = current.getParent();
if (parentType.isAssignableFrom(current.getClass())) {
return (P) current;
}
}
return getParent().getParent(parentType);

return null;
}

@Override
@SuppressWarnings("unchecked")
public <E extends CtElement> E getParent(Filter<E> filter) throws ParentNotInitializedException {
E current = (E) getParent();
while (true) {
public <E extends CtElement> E getParent(Filter<E> filter) {
CtElement current = this;
while (current.isParentInitialized()) {
current = current.getParent();
try {
while (current != null && !filter.matches(current)) {
current = (E) current.getParent();
if (filter.matches((E) current)) {
return (E) current;
}
break;
} catch (ClassCastException e) {
} catch (ClassCastException ignored) {
// expected, some elements are not of type
current = (E) current.getParent();
}
}

if (current != null && filter.matches(current)) {
return current;
}
return null;
}

Expand Down
13 changes: 2 additions & 11 deletions src/main/java/spoon/support/reflect/declaration/CtPackageImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import spoon.reflect.declaration.CtPackage;
import spoon.reflect.declaration.CtShadowable;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.ParentNotInitializedException;
import spoon.reflect.path.CtRole;
import spoon.reflect.reference.CtPackageReference;
import spoon.reflect.visitor.CtVisitor;
Expand Down Expand Up @@ -121,20 +120,12 @@ public boolean removePackage(CtPackage pack) {

@Override
public CtModule getDeclaringModule() {
try {
return getParent(CtModule.class);
} catch (ParentNotInitializedException e) {
return null;
}
return getParent(CtModule.class);
}

@Override
public CtPackage getDeclaringPackage() {
try {
return getParent(CtPackage.class);
} catch (ParentNotInitializedException e) {
return null;
}
return getParent(CtPackage.class);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import spoon.reflect.declaration.CtTypeMember;
import spoon.reflect.declaration.CtTypeParameter;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.declaration.ParentNotInitializedException;
import spoon.reflect.path.CtRole;
import spoon.reflect.reference.CtArrayTypeReference;
import spoon.reflect.reference.CtExecutableReference;
Expand Down Expand Up @@ -325,11 +324,7 @@ public Class<T> getActualClass() {

@Override
public CtType<?> getDeclaringType() {
try {
return getParent(CtType.class);
} catch (ParentNotInitializedException ex) {
return null;
}
return getParent(CtType.class);
}

@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import spoon.reflect.declaration.CtTypeMember;
import spoon.reflect.declaration.CtTypeParameter;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.declaration.ParentNotInitializedException;
import spoon.reflect.reference.CtExecutableReference;
import spoon.reflect.reference.CtFieldReference;
import spoon.reflect.reference.CtTypeParameterReference;
Expand Down Expand Up @@ -80,11 +79,7 @@ public CtTypeParameter clone() {

@Override
public CtFormalTypeDeclarer getTypeParameterDeclarer() {
try {
return getParent(CtFormalTypeDeclarer.class);
} catch (ParentNotInitializedException e) {
return null;
}
return getParent(CtFormalTypeDeclarer.class);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import spoon.reflect.code.CtCatch;
import spoon.reflect.code.CtCatchVariable;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.ParentNotInitializedException;
import spoon.reflect.reference.CtCatchVariableReference;
import spoon.reflect.visitor.CtVisitor;

Expand All @@ -30,18 +29,15 @@ public CtCatchVariable<T> getDeclaration() {
CtElement element = this;
String name = getSimpleName();
CtCatchVariable var;
try {
do {
CtCatch catchBlock = element.getParent(CtCatch.class);
if (catchBlock == null) {
return null;
}
var = catchBlock.getParameter();
element = catchBlock;
} while (!name.equals(var.getSimpleName()));
} catch (ParentNotInitializedException e) {
return null;
}
do {
CtCatch catchBlock = element.getParent(CtCatch.class);
if (catchBlock == null) {
return null;
}
var = catchBlock.getParameter();
element = catchBlock;
} while (!name.equals(var.getSimpleName()));

return var;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtExecutable;
import spoon.reflect.declaration.CtParameter;
import spoon.reflect.declaration.ParentNotInitializedException;
import spoon.reflect.reference.CtExecutableReference;
import spoon.reflect.reference.CtParameterReference;
import spoon.reflect.visitor.CtVisitor;
Expand Down Expand Up @@ -51,22 +50,19 @@ private CtParameter<T> lookupDynamically() {
CtElement element = this;
CtParameter optional = null;
String name = getSimpleName();
try {
do {
CtExecutable executable = element.getParent(CtExecutable.class);
if (executable == null) {
return null;
do {
CtExecutable executable = element.getParent(CtExecutable.class);
if (executable == null) {
return null;
}
for (CtParameter parameter : (List<CtParameter>) executable.getParameters()) {
if (name.equals(parameter.getSimpleName())) {
optional = parameter;
}
for (CtParameter parameter : (List<CtParameter>) executable.getParameters()) {
if (name.equals(parameter.getSimpleName())) {
optional = parameter;
}
}
element = executable;
} while (optional == null);
} catch (ParentNotInitializedException e) {
return null;
}
}
element = executable;
} while (optional == null);

return optional;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public CtTypeParameter getDeclaration() {
return null;
}

CtElement e = this;
CtElement typeDeclarer = this;
CtElement parent = getParent();

if (parent instanceof CtTypeParameter && Objects.equals(getSimpleName(), ((CtTypeParameter) parent).getSimpleName())) {
Expand All @@ -119,8 +119,8 @@ public CtTypeParameter getDeclaration() {
// we might enter in that case because of a call
// of getSuperInterfaces() for example
CtTypeReference typeReference = (CtTypeReference) parent;
e = typeReference.getTypeDeclaration();
if (e == null) {
typeDeclarer = typeReference.getTypeDeclaration();
if (typeDeclarer == null) {
return null;
}
} else {
Expand All @@ -130,31 +130,27 @@ public CtTypeParameter getDeclaration() {

if (parent instanceof CtExecutableReference) {
CtExecutableReference parentExec = (CtExecutableReference) parent;
if (Objects.nonNull(parentExec.getDeclaringType()) && !parentExec.getDeclaringType().equals(e)) {
if (Objects.nonNull(parentExec.getDeclaringType()) && !parentExec.getDeclaringType().equals(typeDeclarer)) {
CtElement parent2 = parentExec.getExecutableDeclaration();
if (parent2 instanceof CtMethod) {
e = parent2;
} else {
e = e.getParent(CtFormalTypeDeclarer.class);
typeDeclarer = parent2;
}
} else {
e = e.getParent(CtFormalTypeDeclarer.class);
}
} else {
if (!(e instanceof CtFormalTypeDeclarer)) {
e = e.getParent(CtFormalTypeDeclarer.class);
}
}

if (!(typeDeclarer instanceof CtFormalTypeDeclarer)) {
typeDeclarer = typeDeclarer.getParent(CtFormalTypeDeclarer.class);
}

// case #1: we're a type of a method parameter, a local variable, ...
// the strategy is to look in the parents
// collecting all formal type declarers of the hierarchy
while (e != null) {
CtTypeParameter result = findTypeParamDeclaration((CtFormalTypeDeclarer) e, this.getSimpleName());
while (typeDeclarer != null) {
CtTypeParameter result = findTypeParamDeclaration((CtFormalTypeDeclarer) typeDeclarer, this.getSimpleName());
if (result != null) {
return result;
}
e = e.getParent(CtFormalTypeDeclarer.class);
typeDeclarer = typeDeclarer.getParent(CtFormalTypeDeclarer.class);
}
return null;
}
Expand Down
Loading