-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d61816d
commit a127136
Showing
7 changed files
with
259 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 92 additions & 0 deletions
92
...rt/src/main/java/tech/picnic/errorprone/refaster/matchers/IsLikelyTrivialComputation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package tech.picnic.errorprone.refaster.matchers; | ||
|
||
import com.google.errorprone.VisitorState; | ||
import com.google.errorprone.matchers.Matcher; | ||
import com.sun.source.tree.ArrayAccessTree; | ||
import com.sun.source.tree.ExpressionTree; | ||
import com.sun.source.tree.IdentifierTree; | ||
import com.sun.source.tree.LambdaExpressionTree; | ||
import com.sun.source.tree.LiteralTree; | ||
import com.sun.source.tree.MemberReferenceTree; | ||
import com.sun.source.tree.MemberSelectTree; | ||
import com.sun.source.tree.MethodInvocationTree; | ||
import com.sun.source.tree.ParenthesizedTree; | ||
import com.sun.source.tree.TypeCastTree; | ||
import com.sun.source.tree.UnaryTree; | ||
|
||
/** A matcher of expressions that likely require little to no computation. */ | ||
public final class IsLikelyTrivialComputation implements Matcher<ExpressionTree> { | ||
private static final long serialVersionUID = 1L; | ||
|
||
/** Instantiates a new {@link IsLikelyTrivialComputation} instance. */ | ||
public IsLikelyTrivialComputation() {} | ||
|
||
@Override | ||
public boolean matches(ExpressionTree expressionTree, VisitorState state) { | ||
if (expressionTree instanceof MethodInvocationTree) { | ||
// XXX: Method invocations are generally *not* trivial computations, but we make an exception | ||
// for nullary method invocations on the result of a trivial computation. This exception | ||
// allows this `Matcher` to by the `OptionalOrElseGet` Refaster rule, such that it does not | ||
// suggest the introduction of lambda expressions that are better expressed as method | ||
// references. Once the `MethodReferenceUsage` bug checker is production-ready, this exception | ||
// should be removed. (But at that point, instead defining a `RequiresComputation` matcher may | ||
// be more appropriate.) | ||
MethodInvocationTree methodInvocation = (MethodInvocationTree) expressionTree; | ||
if (methodInvocation.getArguments().isEmpty() | ||
&& matches(methodInvocation.getMethodSelect())) { | ||
return true; | ||
} | ||
} | ||
|
||
return matches(expressionTree); | ||
} | ||
|
||
// XXX: Some `BinaryTree`s may represent what could be considered "trivial computations". | ||
// Depending on feedback such trees may be matched in the future. | ||
private static boolean matches(ExpressionTree expressionTree) { | ||
if (expressionTree instanceof ArrayAccessTree) { | ||
return matches(((ArrayAccessTree) expressionTree).getExpression()) | ||
&& matches(((ArrayAccessTree) expressionTree).getIndex()); | ||
} | ||
|
||
if (expressionTree instanceof LiteralTree) { | ||
return true; | ||
} | ||
|
||
if (expressionTree instanceof LambdaExpressionTree) { | ||
/* | ||
* Lambda expressions encapsulate computations, but their definition does not involve | ||
* significant computation. | ||
*/ | ||
return true; | ||
} | ||
|
||
if (expressionTree instanceof IdentifierTree) { | ||
return true; | ||
} | ||
|
||
if (expressionTree instanceof MemberReferenceTree) { | ||
return matches(((MemberReferenceTree) expressionTree).getQualifierExpression()); | ||
} | ||
|
||
if (expressionTree instanceof MemberSelectTree) { | ||
return matches(((MemberSelectTree) expressionTree).getExpression()); | ||
} | ||
|
||
if (expressionTree instanceof ParenthesizedTree) { | ||
return matches(((ParenthesizedTree) expressionTree).getExpression()); | ||
} | ||
|
||
if (expressionTree instanceof TypeCastTree) { | ||
return matches(((TypeCastTree) expressionTree).getExpression()); | ||
} | ||
|
||
if (expressionTree instanceof UnaryTree) { | ||
// XXX: Arguably side-effectful options such as pre- and post-increment and -decrement are not | ||
// trivial. | ||
return matches(((UnaryTree) expressionTree).getExpression()); | ||
} | ||
|
||
return false; | ||
} | ||
} |
20 changes: 0 additions & 20 deletions
20
...in/java/tech/picnic/errorprone/refaster/matchers/IsMethodInvocationWithTwoOrMoreArgs.java
This file was deleted.
Oops, something went wrong.
147 changes: 147 additions & 0 deletions
147
...rc/test/java/tech/picnic/errorprone/refaster/matchers/IsLikelyTrivialComputationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
package tech.picnic.errorprone.refaster.matchers; | ||
|
||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR; | ||
|
||
import com.google.errorprone.BugPattern; | ||
import com.google.errorprone.CompilationTestHelper; | ||
import com.google.errorprone.bugpatterns.BugChecker; | ||
import com.sun.source.tree.ReturnTree; | ||
import org.junit.jupiter.api.Test; | ||
|
||
final class IsLikelyTrivialComputationTest { | ||
@Test | ||
void matches() { | ||
CompilationTestHelper.newInstance(MatcherTestChecker.class, getClass()) | ||
.addSourceLines( | ||
"A.java", | ||
"import java.util.function.Predicate;", | ||
"", | ||
"class A {", | ||
" String negative1() {", | ||
" return String.valueOf(1);", | ||
" }", | ||
"", | ||
" String negative2() {", | ||
" return toString().toString();", | ||
" }", | ||
"", | ||
" String negative3() {", | ||
" return \"foo\" + toString();", | ||
" }", | ||
"", | ||
" byte negative4() {", | ||
" return \"foo\".getBytes()[0];", | ||
" }", | ||
"", | ||
" int negative5() {", | ||
" int[] arr = new int[0];", | ||
" return arr[hashCode()];", | ||
" }", | ||
"", | ||
" int negative6() {", | ||
" return 1 * 2;", | ||
" }", | ||
"", | ||
" Predicate<String> negative7() {", | ||
" return toString()::equals;", | ||
" }", | ||
"", | ||
" String negative8() {", | ||
" return (toString());", | ||
" }", | ||
"", | ||
" Object negative9() {", | ||
" return (Object) toString();", | ||
" }", | ||
"", | ||
" int negative10() {", | ||
" return -hashCode();", | ||
" }", | ||
"", | ||
" String positive1() {", | ||
" // BUG: Diagnostic contains:", | ||
" return toString();", | ||
" }", | ||
"", | ||
" String positive2() {", | ||
" // BUG: Diagnostic contains:", | ||
" return this.toString();", | ||
" }", | ||
"", | ||
" int positive3() {", | ||
" int[] arr = new int[0];", | ||
" // BUG: Diagnostic contains:", | ||
" return arr[0];", | ||
" }", | ||
"", | ||
" String positive4() {", | ||
" // BUG: Diagnostic contains:", | ||
" return null;", | ||
" }", | ||
"", | ||
" boolean positive5() {", | ||
" // BUG: Diagnostic contains:", | ||
" return false;", | ||
" }", | ||
"", | ||
" int positive6() {", | ||
" // BUG: Diagnostic contains:", | ||
" return 0;", | ||
" }", | ||
"", | ||
" String positive7() {", | ||
" // BUG: Diagnostic contains:", | ||
" return \"foo\" + \"bar\";", | ||
" }", | ||
"", | ||
" Predicate<String> positive8() {", | ||
" // BUG: Diagnostic contains:", | ||
" return v -> \"foo\".equals(v);", | ||
" }", | ||
"", | ||
" A positive9() {", | ||
" // BUG: Diagnostic contains:", | ||
" return this;", | ||
" }", | ||
"", | ||
" Predicate<String> positive10() {", | ||
" // BUG: Diagnostic contains:", | ||
" return \"foo\"::equals;", | ||
" }", | ||
"", | ||
" A positive11() {", | ||
" // BUG: Diagnostic contains:", | ||
" return (this);", | ||
" }", | ||
"", | ||
" Object positive12() {", | ||
" // BUG: Diagnostic contains:", | ||
" return (Object) this;", | ||
" }", | ||
"", | ||
" boolean positive13() {", | ||
" // BUG: Diagnostic contains:", | ||
" return !false;", | ||
" }", | ||
"}") | ||
.doTest(); | ||
} | ||
|
||
/** A {@link BugChecker} that simply delegates to {@link IsLikelyTrivialComputation}. */ | ||
@BugPattern( | ||
summary = "Flags return statement expressions matched by `IsLikelyTrivialComputation`", | ||
severity = ERROR) | ||
public static final class MatcherTestChecker extends AbstractMatcherTestChecker { | ||
private static final long serialVersionUID = 1L; | ||
|
||
// XXX: This is a false positive reported by Checkstyle. See | ||
// https://github.com/checkstyle/checkstyle/issues/10161#issuecomment-1242732120. | ||
@SuppressWarnings("RedundantModifier") | ||
public MatcherTestChecker() { | ||
super( | ||
(expressionTree, state) -> | ||
state.getPath().getParentPath().getLeaf() instanceof ReturnTree | ||
&& new IsLikelyTrivialComputation().matches(expressionTree, state)); | ||
} | ||
} | ||
} |
Oops, something went wrong.