Skip to content

Commit

Permalink
FindTypes produces data table TypeUses (openrewrite#4395)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkschneider authored Aug 8, 2024
1 parent a75c51d commit 9689f1a
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 25 deletions.
14 changes: 2 additions & 12 deletions rewrite-core/src/main/java/org/openrewrite/Tree.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.openrewrite.internal.MetricsHelper;
import org.openrewrite.internal.StringUtils;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.marker.Markers;
Expand Down Expand Up @@ -72,7 +71,7 @@ static UUID randomId() {
/**
* Checks the supplied argument to see if the supplied visitor and its context would be valid arguments
* to accept().
* Typically this involves checking that the visitor is of a type that operates on this kind of tree.
* Typically, this involves checking that the visitor is of a type that operates on this kind of tree.
* e.g.: A Java Tree implementation would return true for JavaVisitors and false for MavenVisitors
*
* @param <P> the visitor's context argument
Expand All @@ -81,7 +80,6 @@ static UUID randomId() {
<P> boolean isAcceptable(TreeVisitor<?, P> v, P p);

default <P> TreeVisitor<?, PrintOutputCapture<P>> printer(Cursor cursor) {

return cursor.firstEnclosingOrThrow(SourceFile.class).printer(cursor);
}

Expand All @@ -94,7 +92,7 @@ default <P> String print(Cursor cursor, PrintOutputCapture<P> out) {
return out.getOut();
}

default <P> String print(TreeVisitor<?, PrintOutputCapture<Integer>> printer) {
default String print(TreeVisitor<?, PrintOutputCapture<Integer>> printer) {
PrintOutputCapture<Integer> outputCapture = new PrintOutputCapture<>(0);
printer.visit(this, outputCapture);
return outputCapture.getOut();
Expand All @@ -121,12 +119,4 @@ default <T2 extends Tree> T2 cast() {
//noinspection unchecked
return (T2) this;
}

default <T2 extends Tree> @Nullable T2 safeCast() {
try {
return cast();
} catch (ClassCastException ignored) {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.Issue;
import org.openrewrite.java.table.TypeUses;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.assertj.core.api.Assertions.assertThat;
import static org.openrewrite.java.Assertions.java;

@SuppressWarnings({"RedundantThrows", "RedundantCast"})
@SuppressWarnings("RedundantThrows")
class FindTypesTest implements RewriteTest {

@Override
Expand Down Expand Up @@ -145,6 +147,32 @@ public class B {
);
}

@Test
void dataTable() {
rewriteRun(
spec -> spec.recipe(new FindTypes("java.util.Collection", true))
.dataTable(TypeUses.Row.class, rows -> {
assertThat(rows).hasSize(1);
assertThat(rows.get(0).getConcreteType()).isEqualTo("java.util.List");
assertThat(rows.get(0).getCode()).isEqualTo("List<String>");
}),
java(
"""
import java.util.List;
public class B {
List<String> l;
}
""",
"""
import java.util.List;
public class B {
/*~~>*/List<String> l;
}
"""
)
);
}

@Test
void classDecl() {
rewriteRun(
Expand Down Expand Up @@ -241,6 +269,7 @@ public void test() {
);
}

@SuppressWarnings({"EmptyTryBlock", "UnreachableCode"})
@Test
void multiCatch() {
rewriteRun(
Expand Down Expand Up @@ -354,6 +383,7 @@ public class B {
);
}

@SuppressWarnings("RedundantCast")
@Test
void typeCast() {
rewriteRun(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,8 @@
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.NameTree;
import org.openrewrite.java.tree.TypeUtils;
import org.openrewrite.java.table.TypeUses;
import org.openrewrite.java.tree.*;
import org.openrewrite.marker.SearchResult;

import java.util.HashSet;
Expand All @@ -35,6 +33,7 @@
@Value
@EqualsAndHashCode(callSuper = false)
public class FindTypes extends Recipe {
transient TypeUses typeUses = new TypeUses(this);

@Option(displayName = "Fully-qualified type name",
description = "A fully-qualified type name, that is used to find matching type references. " +
Expand Down Expand Up @@ -66,14 +65,14 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
@Override
public J visitIdentifier(J.Identifier ident, ExecutionContext ctx) {
if (ident.getType() != null &&
getCursor().firstEnclosing(J.Import.class) == null &&
getCursor().firstEnclosing(J.FieldAccess.class) == null &&
!(getCursor().getParentOrThrow().getValue() instanceof J.ParameterizedType) &&
!(getCursor().getParentOrThrow().getValue() instanceof J.ArrayType)) {
getCursor().firstEnclosing(J.Import.class) == null &&
getCursor().firstEnclosing(J.FieldAccess.class) == null &&
!(getCursor().getParentOrThrow().getValue() instanceof J.ParameterizedType) &&
!(getCursor().getParentOrThrow().getValue() instanceof J.ArrayType)) {
JavaType.FullyQualified type = TypeUtils.asFullyQualified(ident.getType());
if (typeMatches(Boolean.TRUE.equals(checkAssignability), fullyQualifiedType, type) &&
ident.getSimpleName().equals(type.getClassName())) {
return SearchResult.found(ident);
return found(ident, ctx);
}
}
return super.visitIdentifier(ident, ctx);
Expand All @@ -84,8 +83,8 @@ public <N extends NameTree> N visitTypeName(N name, ExecutionContext ctx) {
N n = super.visitTypeName(name, ctx);
JavaType.FullyQualified type = TypeUtils.asFullyQualified(n.getType());
if (typeMatches(Boolean.TRUE.equals(checkAssignability), fullyQualifiedType, type) &&
getCursor().firstEnclosing(J.Import.class) == null) {
return SearchResult.found(n);
getCursor().firstEnclosing(J.Import.class) == null) {
return found(n, ctx);
}
return n;
}
Expand All @@ -96,10 +95,20 @@ public J visitFieldAccess(J.FieldAccess fieldAccess, ExecutionContext ctx) {
JavaType.FullyQualified type = TypeUtils.asFullyQualified(fa.getTarget().getType());
if (typeMatches(Boolean.TRUE.equals(checkAssignability), fullyQualifiedType, type) &&
fa.getName().getSimpleName().equals("class")) {
return SearchResult.found(fa);
return found(fa, ctx);
}
return fa;
}

private <J2 extends TypedTree> J2 found(J2 j, ExecutionContext ctx) {
JavaType.FullyQualified fqn = TypeUtils.asFullyQualified(j.getType());
typeUses.insertRow(ctx, new TypeUses.Row(
getCursor().firstEnclosingOrThrow(SourceFile.class).getSourcePath().toString(),
j.printTrimmed(getCursor()),
fqn == null ? j.getType().toString() : fqn.getFullyQualifiedName()
));
return SearchResult.found(j);
}
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2022 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.table;

import com.fasterxml.jackson.annotation.JsonIgnoreType;
import lombok.Value;
import org.openrewrite.Column;
import org.openrewrite.DataTable;
import org.openrewrite.Recipe;

@JsonIgnoreType
public class TypeUses extends DataTable<TypeUses.Row> {
public TypeUses(Recipe recipe) {
super(recipe,
"Type uses",
"The source code of matching type uses.");
}

@Value
public static class Row {
@Column(displayName = "Source file",
description = "The source file that the method call occurred in.")
String sourceFile;

@Column(displayName = "Source",
description = "The source code of the type use.")
String code;

@Column(displayName = "Concrete type",
description = "The concrete type in use, which may be a subtype of a searched type.")
String concreteType;
}
}

0 comments on commit 9689f1a

Please sign in to comment.