Skip to content

Commit

Permalink
fix: consolidate a consistent behavior for CtElement#getParent (#3793)
Browse files Browse the repository at this point in the history
Co-authored-by: Simon Larsén <[email protected]>
  • Loading branch information
dya-tel and slarse authored Feb 24, 2021
1 parent 2b17524 commit d18ed7f
Show file tree
Hide file tree
Showing 14 changed files with 101 additions and 137 deletions.
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
9 changes: 6 additions & 3 deletions src/main/java/spoon/reflect/declaration/CtElement.java
Original file line number Diff line number Diff line change
Expand Up @@ -249,14 +249,17 @@ <E extends CtElement> List<E> getAnnotatedChildren(

/**
* Gets the first parent that matches the given type.
*
* @return the nearest matching parent; null if no match is found or this element has no parent
*/
<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.
* If the receiver (this) matches the filter, it is also returned
*
* @return the nearest matching parent; null if no match is found or this element has no parent
*/
<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 @@ -25,7 +25,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 @@ -246,22 +245,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

0 comments on commit d18ed7f

Please sign in to comment.