Merge pull request #3113 from oowekyala:java-usage-resolver
[java] Usage resolution for java variables #3113
This commit is contained in:
commit
0ea6adfa96
73 files changed
+1581
-1704
No files matched your search
+15
-15
@@ -15,9 +15,9 @@
|
||||
<!-- <rule ref="category/java/bestpractices.xml/ArrayIsStoredDirectly"/> -->
|
||||
<rule ref="category/java/bestpractices.xml/AvoidMessageDigestField"/>
|
||||
<rule ref="category/java/bestpractices.xml/AvoidPrintStackTrace"/>
|
||||
<!-- <rule ref="category/java/bestpractices.xml/AvoidReassigningCatchVariables"/> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/AvoidReassigningLoopVariables"/> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/AvoidReassigningParameters"/> -->
|
||||
<rule ref="category/java/bestpractices.xml/AvoidReassigningCatchVariables"/>
|
||||
<rule ref="category/java/bestpractices.xml/AvoidReassigningLoopVariables"/>
|
||||
<rule ref="category/java/bestpractices.xml/AvoidReassigningParameters"/>
|
||||
<rule ref="category/java/bestpractices.xml/AvoidStringBufferField"/>
|
||||
<!-- <rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP"/> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/CheckResultSet"/> -->
|
||||
@@ -49,11 +49,11 @@
|
||||
<rule ref="category/java/bestpractices.xml/SwitchStmtsShouldHaveDefault"/>
|
||||
<!-- <rule ref="category/java/bestpractices.xml/SystemPrintln"/> -->
|
||||
<rule ref="category/java/bestpractices.xml/UnusedAssignment"/>
|
||||
<!-- <rule ref="category/java/bestpractices.xml/UnusedFormalParameter"/> -->
|
||||
<rule ref="category/java/bestpractices.xml/UnusedFormalParameter"/>
|
||||
<!-- <rule ref="category/java/bestpractices.xml/UnusedImports"/> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/UnusedLocalVariable"/> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/UnusedPrivateField"/> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/UnusedPrivateMethod"/> -->
|
||||
<rule ref="category/java/bestpractices.xml/UnusedLocalVariable"/>
|
||||
<rule ref="category/java/bestpractices.xml/UnusedPrivateField"/>
|
||||
<rule ref="category/java/bestpractices.xml/UnusedPrivateMethod"/>
|
||||
<rule ref="category/java/bestpractices.xml/UseAssertEqualsInsteadOfAssertTrue"/>
|
||||
<rule ref="category/java/bestpractices.xml/UseAssertNullInsteadOfAssertTrue"/>
|
||||
<rule ref="category/java/bestpractices.xml/UseAssertSameInsteadOfAssertTrue"/>
|
||||
@@ -86,15 +86,15 @@
|
||||
<rule ref="category/java/codestyle.xml/ForLoopShouldBeWhileLoop"/>
|
||||
<rule ref="category/java/codestyle.xml/FormalParameterNamingConventions"/>
|
||||
<rule ref="category/java/codestyle.xml/GenericsNaming"/>
|
||||
<!-- <rule ref="category/java/codestyle.xml/IdenticalCatchBranches"/> -->
|
||||
<rule ref="category/java/codestyle.xml/IdenticalCatchBranches"/>
|
||||
<!-- <rule ref="category/java/codestyle.xml/LinguisticNaming"/> -->
|
||||
<rule ref="category/java/codestyle.xml/LocalHomeNamingConvention"/>
|
||||
<rule ref="category/java/codestyle.xml/LocalInterfaceSessionNamingConvention"/>
|
||||
<!-- <rule ref="category/java/codestyle.xml/LocalVariableCouldBeFinal"/> -->
|
||||
<rule ref="category/java/codestyle.xml/LocalVariableCouldBeFinal"/>
|
||||
<rule ref="category/java/codestyle.xml/LocalVariableNamingConventions"/>
|
||||
<rule ref="category/java/codestyle.xml/LongVariable"/>
|
||||
<rule ref="category/java/codestyle.xml/MDBAndSessionBeanNamingConvention"/>
|
||||
<!-- <rule ref="category/java/codestyle.xml/MethodArgumentCouldBeFinal"/> -->
|
||||
<rule ref="category/java/codestyle.xml/MethodArgumentCouldBeFinal"/>
|
||||
<rule ref="category/java/codestyle.xml/MethodNamingConventions"/>
|
||||
<rule ref="category/java/codestyle.xml/NoPackage"/>
|
||||
<!-- <rule ref="category/java/codestyle.xml/OnlyOneReturn"/> -->
|
||||
@@ -110,7 +110,7 @@
|
||||
<!-- <rule ref="category/java/codestyle.xml/UnnecessaryCast"/> -->
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryConstructor"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryFullyQualifiedName"/>
|
||||
<!-- <rule ref="category/java/codestyle.xml/UnnecessaryLocalBeforeReturn"/> -->
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryLocalBeforeReturn"/>
|
||||
<!-- <rule ref="category/java/codestyle.xml/UnnecessaryModifier"/> -->
|
||||
<!-- <rule ref="category/java/codestyle.xml/UnnecessaryReturn"/> -->
|
||||
<!-- <rule ref="category/java/codestyle.xml/UseDiamondOperator"/> -->
|
||||
@@ -153,7 +153,7 @@
|
||||
<!-- <rule ref="category/java/design.xml/SimplifiedTernary"/> -->
|
||||
<!-- <rule ref="category/java/design.xml/SimplifyBooleanAssertion"/> -->
|
||||
<rule ref="category/java/design.xml/SimplifyBooleanExpressions"/>
|
||||
<!-- <rule ref="category/java/design.xml/SimplifyBooleanReturns"/> -->
|
||||
<rule ref="category/java/design.xml/SimplifyBooleanReturns"/>
|
||||
<!-- <rule ref="category/java/design.xml/SimplifyConditional"/> -->
|
||||
<!-- <rule ref="category/java/design.xml/SingularField"/> -->
|
||||
<!-- <rule ref="category/java/design.xml/SwitchDensity"/> -->
|
||||
@@ -173,7 +173,7 @@
|
||||
|
||||
<!-- errorprone.xml -->
|
||||
|
||||
<!-- <rule ref="category/java/errorprone.xml/AssignmentInOperand"/> -->
|
||||
<rule ref="category/java/errorprone.xml/AssignmentInOperand"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/AssignmentToNonFinalStatic"/> -->
|
||||
<rule ref="category/java/errorprone.xml/AvoidAssertAsIdentifier"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/AvoidBranchingStatementAsLastInLoop"/> -->
|
||||
@@ -295,10 +295,10 @@
|
||||
<!-- <rule ref="category/java/performance.xml/BigIntegerInstantiation"/> -->
|
||||
<!-- <rule ref="category/java/performance.xml/BooleanInstantiation"/> -->
|
||||
<rule ref="category/java/performance.xml/ByteInstantiation"/>
|
||||
<!-- <rule ref="category/java/performance.xml/ConsecutiveAppendsShouldReuse"/> -->
|
||||
<rule ref="category/java/performance.xml/ConsecutiveAppendsShouldReuse"/>
|
||||
<!-- <rule ref="category/java/performance.xml/ConsecutiveLiteralAppends"/> -->
|
||||
<rule ref="category/java/performance.xml/InefficientEmptyStringCheck"/>
|
||||
<!-- <rule ref="category/java/performance.xml/InefficientStringBuffering"/> -->
|
||||
<rule ref="category/java/performance.xml/InefficientStringBuffering"/>
|
||||
<!-- <rule ref="category/java/performance.xml/InsufficientStringBufferDeclaration"/> -->
|
||||
<rule ref="category/java/performance.xml/IntegerInstantiation"/>
|
||||
<rule ref="category/java/performance.xml/LongInstantiation"/>
|
||||
|
||||
@@ -122,18 +122,22 @@ The following previously deprecated rules have been finally removed:
|
||||
* java-bestpractices
|
||||
* [#342](https://github.com/pmd/pmd/issues/342): \[java] AccessorMethodGeneration: Name clash with another public field not properly handled
|
||||
* [#755](https://github.com/pmd/pmd/issues/755): \[java] AccessorClassGeneration false positive for private constructors
|
||||
* [#770](https://github.com/pmd/pmd/issues/770): \[java] UnusedPrivateMethod yields false positive for counter-variant arguments
|
||||
* [#807](https://github.com/pmd/pmd/issues/807): \[java] AccessorMethodGeneration false positive with overloads
|
||||
* [#1189](https://github.com/pmd/pmd/issues/1189): \[java] UnusedPrivateMethod false positive from inner class via external class
|
||||
* [#1212](https://github.com/pmd/pmd/issues/1212): \[java] Don't raise JUnitTestContainsTooManyAsserts on JUnit 5's assertAll
|
||||
* [#1422](https://github.com/pmd/pmd/issues/1422): \[java] JUnitTestsShouldIncludeAssert false positive with inherited @Rule field
|
||||
* [#1565](https://github.com/pmd/pmd/issues/1565): \[java] JUnitAssertionsShouldIncludeMessage false positive with AssertJ
|
||||
* [#1969](https://github.com/pmd/pmd/issues/1969): \[java] MissingOverride false-positive triggered by package-private method overwritten in another package by extending class
|
||||
* [#1998](https://github.com/pmd/pmd/issues/1998): \[java] AccessorClassGeneration false-negative: subclass calls private constructor
|
||||
* [#2130](https://github.com/pmd/pmd/issues/2130): \[java] UnusedLocalVariable: false-negative with array
|
||||
* [#2147](https://github.com/pmd/pmd/issues/2147): \[java] JUnitTestsShouldIncludeAssert - false positives with lambdas and static methods
|
||||
* [#2542](https://github.com/pmd/pmd/issues/2542): \[java] UseCollectionIsEmpty can not detect the case `foo.bar().size()`
|
||||
* [#2796](https://github.com/pmd/pmd/issue/2796): \[java] UnusedAssignment false positive with call chains
|
||||
* [#2797](https://github.com/pmd/pmd/issues/2797): \[java] MissingOverride long-standing issues
|
||||
* [#2806](https://github.com/pmd/pmd/issues/2806): \[java] SwitchStmtsShouldHaveDefault false-positive with Java 14 switch non-fallthrough branches
|
||||
* [#2883](https://github.com/pmd/pmd/issues/2883): \[java] JUnitAssertionsShouldIncludeMessage false positive with method call
|
||||
* [#2890](https://github.com/pmd/pmd/issues/2890): \[java] UnusedPrivateMethod false positive with generics
|
||||
* java-codestyle
|
||||
* [#1673](https://github.com/pmd/pmd/issues/1673): \[java] UselessParentheses false positive with conditional operator
|
||||
* [#1790](https://github.com/pmd/pmd/issues/1790): \[java] UnnecessaryFullyQualifiedName false positive with enum constant
|
||||
|
||||
@@ -454,7 +454,7 @@ public interface Node {
|
||||
default @Nullable Node getNextSibling() {
|
||||
Node parent = getParent();
|
||||
int idx = getIndexInParent();
|
||||
if (parent != null && idx < parent.getNumChildren()) {
|
||||
if (parent != null && idx + 1 < parent.getNumChildren()) {
|
||||
return parent.getChild(idx + 1);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -129,7 +129,7 @@ import net.sourceforge.pmd.lang.ast.internal.StreamImpl;
|
||||
*
|
||||
* @since 7.0.0
|
||||
*/
|
||||
public interface NodeStream<T extends Node> extends Iterable<@NonNull T> {
|
||||
public interface NodeStream<@NonNull T extends Node> extends Iterable<@NonNull T> {
|
||||
|
||||
/**
|
||||
* Returns a node stream consisting of the results of replacing each
|
||||
@@ -1086,7 +1086,7 @@ public interface NodeStream<T extends Node> extends Iterable<@NonNull T> {
|
||||
*/
|
||||
@SafeVarargs // this method is static because of the generic varargs
|
||||
@SuppressWarnings("unchecked")
|
||||
static <I, O> Function<@Nullable I, @Nullable O> asInstanceOf(Class<? extends O> c1, Class<? extends O>... rest) {
|
||||
static <O> Function<@Nullable Object, @Nullable O> asInstanceOf(Class<? extends O> c1, Class<? extends O>... rest) {
|
||||
if (rest.length == 0) {
|
||||
return obj -> c1.isInstance(obj) ? (O) obj : null;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import static net.sourceforge.pmd.lang.ast.impl.DummyTreeUtil.root;
|
||||
import static net.sourceforge.pmd.lang.ast.impl.DummyTreeUtil.tree;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.List;
|
||||
@@ -113,6 +114,22 @@ public class AbstractNodeTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrevNextSiblings() {
|
||||
DummyRoot root = tree(() -> root(node(), node()));
|
||||
|
||||
assertNull(root.getNextSibling());
|
||||
assertNull(root.getPreviousSibling());
|
||||
|
||||
DummyNode c0 = root.getChild(0);
|
||||
DummyNode c1 = root.getChild(1);
|
||||
|
||||
assertSame(c0, c1.getPreviousSibling());
|
||||
assertSame(c1, c0.getNextSibling());
|
||||
assertNull(c1.getNextSibling());
|
||||
assertNull(c0.getPreviousSibling());
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly tests the {@code remove} method, and implicitly the {@code removeChildAtIndex} method.
|
||||
* This is a border case as the root node does not have any parent.
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol;
|
||||
import net.sourceforge.pmd.lang.java.types.JVariableSig;
|
||||
|
||||
@@ -95,7 +96,10 @@ public interface ASTAssignableExpr extends ASTPrimaryExpression {
|
||||
/** The value of the variable is read. */
|
||||
READ,
|
||||
|
||||
/** The value is written to, possibly being read before or after. */
|
||||
/**
|
||||
* The value is written to, possibly being read before or after.
|
||||
* Also see {@link JavaRuleUtil#isVarAccessReadAndWrite(ASTNamedReferenceExpr)}.
|
||||
*/
|
||||
WRITE
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import net.sourceforge.pmd.lang.ast.NodeStream;
|
||||
*/
|
||||
public final class ASTBreakStatement extends AbstractStatement {
|
||||
|
||||
private static final Function<JavaNode, ASTStatement> BREAK_TARGET_MAPPER =
|
||||
private static final Function<Object, ASTStatement> BREAK_TARGET_MAPPER =
|
||||
NodeStream.asInstanceOf(ASTLoopStatement.class, ASTSwitchStatement.class);
|
||||
|
||||
ASTBreakStatement(int id) {
|
||||
|
||||
@@ -22,7 +22,7 @@ import net.sourceforge.pmd.lang.ast.NodeStream;
|
||||
*/
|
||||
public final class ASTContinueStatement extends AbstractStatement {
|
||||
|
||||
private static final Function<JavaNode, ASTLoopStatement> CONTINUE_TARGET_MAPPER =
|
||||
private static final Function<Object, ASTLoopStatement> CONTINUE_TARGET_MAPPER =
|
||||
NodeStream.asInstanceOf(ASTLoopStatement.class);
|
||||
|
||||
ASTContinueStatement(int id) {
|
||||
|
||||
@@ -25,6 +25,7 @@ public final class ASTDoStatement extends AbstractStatement implements ASTLoopSt
|
||||
* Returns the node that represents the guard of this loop.
|
||||
* This may be any expression of type boolean.
|
||||
*/
|
||||
@Override
|
||||
public ASTExpression getCondition() {
|
||||
return (ASTExpression) getChild(1);
|
||||
}
|
||||
@@ -34,6 +35,7 @@ public final class ASTDoStatement extends AbstractStatement implements ASTLoopSt
|
||||
* Returns the statement that will be run while the guard
|
||||
* evaluates to true.
|
||||
*/
|
||||
@Override
|
||||
public ASTStatement getBody() {
|
||||
return (ASTStatement) getChild(0);
|
||||
}
|
||||
|
||||
@@ -29,13 +29,7 @@ public final class ASTForStatement extends AbstractStatement implements ASTLoopS
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the node that represents the condition of this loop.
|
||||
* This may be any expression of type boolean.
|
||||
*
|
||||
* <p>If there is no specified guard, then returns null.
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public ASTExpression getCondition() {
|
||||
return getFirstChildOfType(ASTExpression.class);
|
||||
}
|
||||
@@ -58,9 +52,5 @@ public final class ASTForStatement extends AbstractStatement implements ASTLoopS
|
||||
return update == null ? null : update.getExprList();
|
||||
}
|
||||
|
||||
/** Returns the statement that represents the body of this loop. */
|
||||
public ASTStatement getBody() {
|
||||
return (ASTStatement) getChild(getNumChildren() - 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,12 +43,5 @@ public final class ASTForeachStatement extends AbstractStatement implements Inte
|
||||
return getFirstChildOfType(ASTExpression.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the statement that represents the body of this
|
||||
* loop.
|
||||
*/
|
||||
public ASTStatement getBody() {
|
||||
return (ASTStatement) getChild(getNumChildren() - 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -114,6 +114,19 @@ public abstract class ASTList<N extends JavaNode> extends AbstractJavaNode imple
|
||||
return list == null ? 0 : list.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the element if there is exactly one, otherwise returns null.
|
||||
*
|
||||
* @param list List node
|
||||
* @param <N> Type of elements
|
||||
*
|
||||
* @return An element, or null.
|
||||
*/
|
||||
public static <N extends JavaNode> @Nullable N singleOrNull(@Nullable ASTList<N> list) {
|
||||
return list == null || list.size() != 1 ? null : list.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Super type for *nonempty* lists that *only* have nodes of type {@code <T>}
|
||||
* as a child.
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* A loop statement.
|
||||
*
|
||||
@@ -19,4 +21,23 @@ package net.sourceforge.pmd.lang.java.ast;
|
||||
*
|
||||
*/
|
||||
public interface ASTLoopStatement extends ASTStatement {
|
||||
|
||||
|
||||
/** Returns the statement that represents the body of this loop. */
|
||||
default ASTStatement getBody() {
|
||||
return (ASTStatement) getLastChild();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the node that represents the condition of this loop.
|
||||
* This may be any expression of type boolean.
|
||||
*
|
||||
* <p>If there is no specified guard, then returns null (in particular,
|
||||
* returns null if this is a foreach loop).
|
||||
*/
|
||||
default @Nullable ASTExpression getCondition() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,8 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
public final class ASTMethodCall extends AbstractInvocationExpr
|
||||
implements ASTPrimaryExpression,
|
||||
QualifiableExpression,
|
||||
InvocationNode {
|
||||
InvocationNode,
|
||||
MethodUsage {
|
||||
|
||||
ASTMethodCall(int id) {
|
||||
super(id);
|
||||
|
||||
@@ -50,7 +50,6 @@ public final class ASTMethodDeclaration extends AbstractMethodOrConstructorDecla
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if this method is overridden.
|
||||
* TODO for now, this just checks for an @Override annotation,
|
||||
|
||||
@@ -22,7 +22,11 @@ import net.sourceforge.pmd.lang.java.types.TypeSystem;
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
public final class ASTMethodReference extends AbstractJavaExpr implements ASTPrimaryExpression, QualifiableExpression, LeftRecursiveNode {
|
||||
public final class ASTMethodReference extends AbstractJavaExpr
|
||||
implements ASTPrimaryExpression,
|
||||
QualifiableExpression,
|
||||
LeftRecursiveNode,
|
||||
MethodUsage {
|
||||
|
||||
private JMethodSig functionalMethod;
|
||||
private JMethodSig compileTimeDecl;
|
||||
@@ -90,6 +94,7 @@ public final class ASTMethodReference extends AbstractJavaExpr implements ASTPri
|
||||
* Returns the method name, or an {@link JConstructorSymbol#CTOR_NAME}
|
||||
* if this is a {@linkplain #isConstructorReference() constructor reference}.
|
||||
*/
|
||||
@Override
|
||||
public @NonNull String getMethodName() {
|
||||
return super.getImage();
|
||||
}
|
||||
|
||||
+29
-1
@@ -4,6 +4,8 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
@@ -12,6 +14,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import net.sourceforge.pmd.annotation.Experimental;
|
||||
import net.sourceforge.pmd.annotation.InternalApi;
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
|
||||
import net.sourceforge.pmd.lang.rule.xpath.DeprecatedAttribute;
|
||||
@@ -51,6 +54,8 @@ public final class ASTVariableDeclaratorId extends AbstractTypedSymbolDeclarator
|
||||
|
||||
private VariableNameDeclaration nameDeclaration;
|
||||
|
||||
private List<ASTNamedReferenceExpr> usages = Collections.emptyList();
|
||||
|
||||
ASTVariableDeclaratorId(int id) {
|
||||
super(id);
|
||||
}
|
||||
@@ -73,10 +78,33 @@ public final class ASTVariableDeclaratorId extends AbstractTypedSymbolDeclarator
|
||||
nameDeclaration = decl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated transitional, use {@link #getLocalUsages()}
|
||||
*/
|
||||
@Deprecated
|
||||
public List<NameOccurrence> getUsages() {
|
||||
return getScope().getDeclarations(VariableNameDeclaration.class).get(nameDeclaration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable list of the usages of this variable that
|
||||
* are made in this file. Note that for a record component, this returns
|
||||
* usages both for the formal parameter symbol and its field counterpart.
|
||||
*
|
||||
* <p>Note that a variable initializer is not part of the usages
|
||||
* (though this should be evident from the return type).
|
||||
*/
|
||||
public List<ASTNamedReferenceExpr> getLocalUsages() {
|
||||
return usages;
|
||||
}
|
||||
|
||||
void addUsage(ASTNamedReferenceExpr usage) {
|
||||
if (usages.isEmpty()) {
|
||||
usages = new ArrayList<>(4); //make modifiable
|
||||
}
|
||||
usages.add(usage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extra array dimensions associated with this variable.
|
||||
* For example in the declaration {@code int a[]}, {@link #getTypeNode()}
|
||||
@@ -101,13 +129,13 @@ public final class ASTVariableDeclaratorId extends AbstractTypedSymbolDeclarator
|
||||
return getModifierOwnerParent().getModifiers();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Visibility getVisibility() {
|
||||
return isPatternBinding() ? Visibility.V_LOCAL
|
||||
: getModifierOwnerParent().getVisibility();
|
||||
}
|
||||
|
||||
|
||||
private AccessNode getModifierOwnerParent() {
|
||||
JavaNode parent = getParent();
|
||||
if (parent instanceof ASTVariableDeclarator) {
|
||||
|
||||
@@ -24,19 +24,12 @@ public final class ASTWhileStatement extends AbstractStatement implements ASTLoo
|
||||
* Returns the node that represents the guard of this loop.
|
||||
* This may be any expression of type boolean.
|
||||
*/
|
||||
@Override
|
||||
public ASTExpression getCondition() {
|
||||
return (ASTExpression) getChild(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the statement that will be run while the guard
|
||||
* evaluates to true.
|
||||
*/
|
||||
public ASTStatement getBody() {
|
||||
return (ASTStatement) getChild(1);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
|
||||
|
||||
@@ -161,4 +161,28 @@ public enum BinaryOp implements InternalInterfaces.OperatorLike {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Complement, for boolean operators. Eg for {@code ==}, return {@code !=},
|
||||
* for {@code <=}, returns {@code >}. Returns null if this is another kind
|
||||
* of operator.
|
||||
*/
|
||||
public BinaryOp getComplement() {
|
||||
switch (this) {
|
||||
case CONDITIONAL_OR: return CONDITIONAL_AND;
|
||||
case CONDITIONAL_AND: return CONDITIONAL_OR;
|
||||
case OR: return AND;
|
||||
case AND: return OR;
|
||||
|
||||
case EQ: return NE;
|
||||
case NE: return EQ;
|
||||
case LE: return GT;
|
||||
case GE: return LT;
|
||||
case GT: return LE;
|
||||
case LT: return GE;
|
||||
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import net.sourceforge.pmd.annotation.InternalApi;
|
||||
import net.sourceforge.pmd.lang.ast.NodeStream;
|
||||
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
|
||||
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr;
|
||||
import net.sourceforge.pmd.lang.java.internal.JavaAstProcessor;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
|
||||
@@ -122,6 +123,20 @@ public final class InternalApiBridge {
|
||||
AstDisambiguationPass.disambigWithCtx(nodes, ctx);
|
||||
}
|
||||
|
||||
public static void usageResolution(JavaAstProcessor processor, ASTCompilationUnit root) {
|
||||
root.descendants(ASTNamedReferenceExpr.class)
|
||||
.crossFindBoundaries()
|
||||
.forEach(node -> {
|
||||
JVariableSymbol sym = node.getReferencedSym();
|
||||
if (sym != null) {
|
||||
ASTVariableDeclaratorId reffed = sym.tryGetNode();
|
||||
if (reffed != null) { // declared in this file
|
||||
reffed.addUsage(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static @Nullable JTypeMirror getTypeMirrorInternal(TypeNode node) {
|
||||
return ((AbstractJavaTypeNode) node).getTypeMirrorInternal();
|
||||
}
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
|
||||
import net.sourceforge.pmd.lang.java.types.JMethodSig;
|
||||
import net.sourceforge.pmd.lang.java.types.OverloadSelectionResult;
|
||||
|
||||
@@ -24,7 +22,7 @@ import net.sourceforge.pmd.lang.java.types.OverloadSelectionResult;
|
||||
* of the {@linkplain #getMethodType() compile-time declaration}
|
||||
* of this node.
|
||||
*/
|
||||
public interface InvocationNode extends TypeNode {
|
||||
public interface InvocationNode extends TypeNode, MethodUsage {
|
||||
|
||||
/**
|
||||
* Returns the node representing the list of arguments
|
||||
@@ -57,12 +55,5 @@ public interface InvocationNode extends TypeNode {
|
||||
*/
|
||||
OverloadSelectionResult getOverloadSelectionInfo();
|
||||
|
||||
/**
|
||||
* Returns the name of the called method. If this is a constructor
|
||||
* call, returns {@link JConstructorSymbol#CTOR_NAME}.
|
||||
*/
|
||||
default @NonNull String getMethodName() {
|
||||
return JConstructorSymbol.CTOR_NAME;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.AstVisitorBase;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr;
|
||||
|
||||
/**
|
||||
* Base implementation of {@link JavaVisitor}. This adds delegation logic
|
||||
@@ -185,11 +186,6 @@ public class JavaVisitorBase<P, R> extends AstVisitorBase<P, R> implements JavaV
|
||||
return visitPrimaryExpr(node, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R visit(ASTFieldAccess node, P data) {
|
||||
return visitPrimaryExpr(node, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R visit(ASTConstructorCall node, P data) {
|
||||
return visitPrimaryExpr(node, data);
|
||||
@@ -207,10 +203,18 @@ public class JavaVisitorBase<P, R> extends AstVisitorBase<P, R> implements JavaV
|
||||
return visitPrimaryExpr(node, data);
|
||||
}
|
||||
|
||||
public R visitNamedExpr(ASTNamedReferenceExpr node, P data) {
|
||||
return visitPrimaryExpr(node, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R visit(ASTVariableAccess node, P data) {
|
||||
return visitPrimaryExpr(node, data);
|
||||
return visitNamedExpr(node, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R visit(ASTFieldAccess node, P data) {
|
||||
return visitNamedExpr(node, data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
|
||||
|
||||
/**
|
||||
* A node that uses another method or constructor. Those are
|
||||
* {@link InvocationNode#getMethodType() InvocationNode}s and
|
||||
* {@link ASTMethodReference#getReferencedMethod() MethodReference}.
|
||||
*
|
||||
* TODO should these method be named the same, and added to this interface?
|
||||
*/
|
||||
public interface MethodUsage extends JavaNode {
|
||||
|
||||
/**
|
||||
* Returns the name of the called method. If this is a constructor
|
||||
* call, returns {@link JConstructorSymbol#CTOR_NAME}.
|
||||
*/
|
||||
default @NonNull String getMethodName() {
|
||||
return JConstructorSymbol.CTOR_NAME;
|
||||
}
|
||||
}
|
||||
@@ -145,6 +145,7 @@ public final class JavaAstProcessor {
|
||||
bench("2. Symbol table resolution", () -> SymbolTableResolver.traverse(this, acu));
|
||||
bench("3. AST disambiguation", () -> InternalApiBridge.disambigWithCtx(NodeStream.of(acu), ReferenceCtx.root(this, acu)));
|
||||
bench("4. Comment assignment", () -> InternalApiBridge.assignComments(acu));
|
||||
bench("5. Usage resolution", () -> InternalApiBridge.usageResolution(this, acu));
|
||||
}
|
||||
|
||||
public TypeSystem getTypeSystem() {
|
||||
|
||||
+15
-32
@@ -4,48 +4,31 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.bestpractices;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTCatchClause;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTName;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.AccessType;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTCatchParameter;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaNode;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
|
||||
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
|
||||
import net.sourceforge.pmd.lang.rule.RuleTargetSelector;
|
||||
|
||||
public class AvoidReassigningCatchVariablesRule extends AbstractJavaRule {
|
||||
|
||||
public AvoidReassigningCatchVariablesRule() {
|
||||
addRuleChainVisit(ASTCatchClause.class);
|
||||
@Override
|
||||
protected @NonNull RuleTargetSelector buildTargetSelector() {
|
||||
return RuleTargetSelector.forTypes(ASTCatchParameter.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTCatchClause catchStatement, Object data) {
|
||||
ASTVariableDeclaratorId caughtExceptionId = catchStatement.getParameter().getVarId();
|
||||
String caughtExceptionVar = caughtExceptionId.getName();
|
||||
for (NameOccurrence usage : caughtExceptionId.getUsages()) {
|
||||
JavaNode operation = getOperationOfUsage(usage);
|
||||
if (isAssignment(operation)) {
|
||||
String assignedVar = getAssignedVariableName(operation);
|
||||
if (caughtExceptionVar.equals(assignedVar)) {
|
||||
addViolation(data, operation, caughtExceptionVar);
|
||||
}
|
||||
public Object visit(ASTCatchParameter catchParam, Object data) {
|
||||
ASTVariableDeclaratorId caughtExceptionId = catchParam.getVarId();
|
||||
for (ASTNamedReferenceExpr usage : caughtExceptionId.getLocalUsages()) {
|
||||
if (usage.getAccessType() == AccessType.WRITE) {
|
||||
addViolation(data, usage, caughtExceptionId.getName());
|
||||
}
|
||||
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private JavaNode getOperationOfUsage(NameOccurrence usage) {
|
||||
return usage.getLocation()
|
||||
.getFirstParentOfType(ASTPrimaryExpression.class)
|
||||
.getParent();
|
||||
}
|
||||
|
||||
private boolean isAssignment(JavaNode operation) {
|
||||
return operation.hasDescendantOfType(ASTAssignmentOperator.class);
|
||||
}
|
||||
|
||||
private String getAssignedVariableName(JavaNode operation) {
|
||||
return operation.getFirstDescendantOfType(ASTName.class).getImage();
|
||||
}
|
||||
}
|
||||
+150
-220
File diff suppressed because it is too large.
Load diff
+27
-39
@@ -4,55 +4,43 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.bestpractices;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.AccessType;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
|
||||
import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence;
|
||||
import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
|
||||
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
|
||||
|
||||
public class AvoidReassigningParametersRule extends AbstractJavaRule {
|
||||
public class AvoidReassigningParametersRule extends AbstractJavaRulechainRule {
|
||||
|
||||
@Override
|
||||
public Object visit(ASTMethodDeclarator node, Object data) {
|
||||
Map<VariableNameDeclaration, List<NameOccurrence>> params = node.getScope()
|
||||
.getDeclarations(VariableNameDeclaration.class);
|
||||
this.lookForViolation(params, data);
|
||||
return super.visit(node, data);
|
||||
public AvoidReassigningParametersRule() {
|
||||
super(ASTMethodDeclaration.class, ASTConstructorDeclaration.class);
|
||||
}
|
||||
|
||||
private void lookForViolation(Map<VariableNameDeclaration, List<NameOccurrence>> params, Object data) {
|
||||
for (Map.Entry<VariableNameDeclaration, List<NameOccurrence>> entry : params.entrySet()) {
|
||||
VariableNameDeclaration decl = entry.getKey();
|
||||
List<NameOccurrence> usages = entry.getValue();
|
||||
@Override
|
||||
public Object visit(ASTMethodDeclaration node, Object data) {
|
||||
lookForViolations(node, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
// Only look for formal parameters
|
||||
if (!decl.getDeclaratorId().isFormalParameter()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (NameOccurrence occ : usages) {
|
||||
JavaNameOccurrence jocc = (JavaNameOccurrence) occ;
|
||||
if ((jocc.isOnLeftHandSide() || jocc.isSelfAssignment())
|
||||
&& jocc.getNameForWhichThisIsAQualifier() == null && !jocc.useThisOrSuper() && !decl.isVarargs()
|
||||
&& (!decl.isArray()
|
||||
|| jocc.getLocation().getParent().getParent().getNumChildren() == 1)) {
|
||||
// not an array or no primary suffix to access the array
|
||||
// values
|
||||
addViolation(data, decl.getNode(), decl.getImage());
|
||||
@Override
|
||||
public Object visit(ASTConstructorDeclaration node, Object data) {
|
||||
lookForViolations(node, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
private void lookForViolations(ASTMethodOrConstructorDeclaration node, Object data) {
|
||||
for (ASTFormalParameter formal : node.getFormalParameters()) {
|
||||
ASTVariableDeclaratorId varId = formal.getVarId();
|
||||
for (ASTNamedReferenceExpr usage : varId.getLocalUsages()) {
|
||||
if (usage.getAccessType() == AccessType.WRITE) {
|
||||
addViolation(data, usage, varId.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTConstructorDeclaration node, Object data) {
|
||||
Map<VariableNameDeclaration, List<NameOccurrence>> params = node.getScope()
|
||||
.getDeclarations(VariableNameDeclaration.class);
|
||||
this.lookForViolation(params, data);
|
||||
return super.visit(node, data);
|
||||
}
|
||||
}
|
||||
+10
-84
@@ -6,29 +6,14 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices;
|
||||
|
||||
import static net.sourceforge.pmd.properties.PropertyFactory.booleanProperty;
|
||||
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTThrowsList;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTType;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaNode;
|
||||
import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
|
||||
import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil;
|
||||
import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence;
|
||||
import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
|
||||
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
|
||||
import net.sourceforge.pmd.properties.PropertyDescriptor;
|
||||
|
||||
|
||||
@@ -48,83 +33,24 @@ public class UnusedFormalParameterRule extends AbstractJavaRule {
|
||||
|
||||
@Override
|
||||
public Object visit(ASTMethodDeclaration node, Object data) {
|
||||
if (!node.isPrivate() && !getProperty(CHECKALL_DESCRIPTOR)) {
|
||||
if (node.getVisibility() != Visibility.V_PRIVATE && !getProperty(CHECKALL_DESCRIPTOR)) {
|
||||
return data;
|
||||
}
|
||||
if (!node.isNative() && !node.isAbstract() && !isSerializationMethod(node) && !hasOverrideAnnotation(node)) {
|
||||
if (node.getBody() != null && !JavaRuleUtil.isSerializationReadObject(node) && !node.isOverridden()) {
|
||||
check(node, data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private boolean isSerializationMethod(ASTMethodDeclaration node) {
|
||||
ASTMethodDeclarator declarator = node.getFirstDescendantOfType(ASTMethodDeclarator.class);
|
||||
List<ASTFormalParameter> parameters = declarator.findDescendantsOfType(ASTFormalParameter.class);
|
||||
if (node.isPrivate() && "readObject".equals(node.getName()) && parameters.size() == 1
|
||||
&& throwsOneException(node, InvalidObjectException.class)) {
|
||||
ASTType type = parameters.get(0).getTypeNode();
|
||||
if (type.getType() == ObjectInputStream.class
|
||||
|| ObjectInputStream.class.getSimpleName().equals(type.getTypeImage())
|
||||
|| ObjectInputStream.class.getName().equals(type.getTypeImage())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean throwsOneException(ASTMethodDeclaration node, Class<? extends Throwable> exception) {
|
||||
@Nullable ASTThrowsList throwsList = node.getThrowsList();
|
||||
if (throwsList != null && throwsList.getNumChildren() == 1) {
|
||||
ASTClassOrInterfaceType n = throwsList.getChild(0);
|
||||
if (n.getType() == exception || exception.getSimpleName().equals(n.getImage())
|
||||
|| exception.getName().equals(n.getImage())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void check(Node node, Object data) {
|
||||
Node parent = node.getParent().getParent().getParent();
|
||||
if (parent instanceof ASTClassOrInterfaceDeclaration
|
||||
&& !((ASTClassOrInterfaceDeclaration) parent).isInterface()) {
|
||||
Map<VariableNameDeclaration, List<NameOccurrence>> vars = ((JavaNode) node).getScope()
|
||||
.getDeclarations(VariableNameDeclaration.class);
|
||||
for (Map.Entry<VariableNameDeclaration, List<NameOccurrence>> entry : vars.entrySet()) {
|
||||
VariableNameDeclaration nameDecl = entry.getKey();
|
||||
|
||||
ASTVariableDeclaratorId declNode = nameDecl.getDeclaratorId();
|
||||
if (!declNode.isFormalParameter()) {
|
||||
continue;
|
||||
private void check(ASTMethodOrConstructorDeclaration node, Object data) {
|
||||
if (!node.getEnclosingType().isInterface()) {
|
||||
for (ASTFormalParameter formal : node.getFormalParameters()) {
|
||||
ASTVariableDeclaratorId varId = formal.getVarId();
|
||||
if (JavaRuleUtil.isNeverUsed(varId) && !JavaRuleUtil.isExplicitUnusedVarName(varId.getName())) {
|
||||
addViolation(data, varId, new Object[] {node instanceof ASTMethodDeclaration ? "method" : "constructor", varId.getName(), });
|
||||
}
|
||||
|
||||
if (actuallyUsed(nameDecl, entry.getValue())
|
||||
|| JavaRuleUtil.isExplicitUnusedVarName(nameDecl.getName())) {
|
||||
continue;
|
||||
}
|
||||
addViolation(data, nameDecl.getNode(), new Object[] {
|
||||
node instanceof ASTMethodDeclaration ? "method" : "constructor", nameDecl.getImage(), });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean actuallyUsed(VariableNameDeclaration nameDecl, List<NameOccurrence> usages) {
|
||||
for (NameOccurrence occ : usages) {
|
||||
JavaNameOccurrence jocc = (JavaNameOccurrence) occ;
|
||||
if (jocc.isOnLeftHandSide()) {
|
||||
if (nameDecl.isArray() && jocc.getLocation().getParent().getParent().getNumChildren() > 1) {
|
||||
// array element access
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasOverrideAnnotation(ASTMethodDeclaration node) {
|
||||
return node.isAnnotationPresent(Override.class);
|
||||
}
|
||||
}
|
||||
+9
-28
@@ -4,49 +4,30 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.bestpractices;
|
||||
|
||||
import java.util.List;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
|
||||
import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil;
|
||||
import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence;
|
||||
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
|
||||
import net.sourceforge.pmd.lang.rule.RuleTargetSelector;
|
||||
|
||||
public class UnusedLocalVariableRule extends AbstractJavaRule {
|
||||
|
||||
public UnusedLocalVariableRule() {
|
||||
addRuleChainVisit(ASTLocalVariableDeclaration.class);
|
||||
@Override
|
||||
protected @NonNull RuleTargetSelector buildTargetSelector() {
|
||||
return RuleTargetSelector.forTypes(ASTLocalVariableDeclaration.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTLocalVariableDeclaration decl, Object data) {
|
||||
for (int i = 0; i < decl.getNumChildren(); i++) {
|
||||
if (!(decl.getChild(i) instanceof ASTVariableDeclarator)) {
|
||||
continue;
|
||||
}
|
||||
ASTVariableDeclaratorId node = (ASTVariableDeclaratorId) decl.getChild(i).getChild(0);
|
||||
// TODO this isArray() check misses some cases
|
||||
// need to add DFAish code to determine if an array
|
||||
// is initialized locally or gotten from somewhere else
|
||||
if (!node.getNameDeclaration().isArray()
|
||||
&& !actuallyUsed(node.getUsages())
|
||||
&& !JavaRuleUtil.isExplicitUnusedVarName(node.getName())) {
|
||||
addViolation(data, node, node.getNameDeclaration().getImage());
|
||||
for (ASTVariableDeclaratorId varId : decl.getVarIds()) {
|
||||
if (JavaRuleUtil.isNeverUsed(varId)
|
||||
&& !JavaRuleUtil.isExplicitUnusedVarName(varId.getName())) {
|
||||
addViolation(data, varId, varId.getName());
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private boolean actuallyUsed(List<NameOccurrence> usages) {
|
||||
for (NameOccurrence occ : usages) {
|
||||
JavaNameOccurrence jocc = (JavaNameOccurrence) occ;
|
||||
if (!jocc.isOnLeftHandSide()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
+29
-96
@@ -6,28 +6,25 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTBodyDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTEnumBody;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTName;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTTypeBody;
|
||||
import net.sourceforge.pmd.lang.java.ast.AccessNode;
|
||||
import net.sourceforge.pmd.lang.java.ast.Annotatable;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
|
||||
import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility;
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaNode;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractLombokAwareRule;
|
||||
import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence;
|
||||
import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
|
||||
import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
|
||||
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
|
||||
import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil;
|
||||
import net.sourceforge.pmd.lang.rule.RuleTargetSelector;
|
||||
|
||||
public class UnusedPrivateFieldRule extends AbstractLombokAwareRule {
|
||||
|
||||
@Override
|
||||
protected @NonNull RuleTargetSelector buildTargetSelector() {
|
||||
return RuleTargetSelector.forTypes(ASTAnyTypeDeclaration.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> defaultSuppressionAnnotations() {
|
||||
Collection<String> defaultValues = new ArrayList<>(super.defaultSuppressionAnnotations());
|
||||
@@ -39,91 +36,27 @@ public class UnusedPrivateFieldRule extends AbstractLombokAwareRule {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
|
||||
if (hasIgnoredAnnotation(node)) {
|
||||
return super.visit(node, data);
|
||||
}
|
||||
|
||||
Map<VariableNameDeclaration, List<NameOccurrence>> vars = node.getScope()
|
||||
.getDeclarations(VariableNameDeclaration.class);
|
||||
for (Map.Entry<VariableNameDeclaration, List<NameOccurrence>> entry : vars.entrySet()) {
|
||||
VariableNameDeclaration decl = entry.getKey();
|
||||
AccessNode accessNodeParent = decl.getAccessNodeParent();
|
||||
if (!accessNodeParent.isPrivate()
|
||||
|| isOK(decl.getImage())
|
||||
|| hasIgnoredAnnotation((Annotatable) accessNodeParent)) {
|
||||
continue;
|
||||
}
|
||||
if (!actuallyUsed(entry.getValue())) {
|
||||
if (!usedInOuterClass(node, decl) && !usedInOuterEnum(node, decl)) {
|
||||
addViolation(data, decl.getNode(), decl.getImage());
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visit(node, data);
|
||||
}
|
||||
|
||||
private boolean usedInOuterEnum(ASTClassOrInterfaceDeclaration node, NameDeclaration decl) {
|
||||
List<ASTEnumDeclaration> outerEnums = node.getParentsOfType(ASTEnumDeclaration.class);
|
||||
for (ASTEnumDeclaration outerEnum : outerEnums) {
|
||||
ASTEnumBody enumBody = outerEnum.getFirstChildOfType(ASTEnumBody.class);
|
||||
if (usedInOuter(decl, enumBody)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out whether the variable is used in an outer class
|
||||
*/
|
||||
private boolean usedInOuterClass(ASTClassOrInterfaceDeclaration node, NameDeclaration decl) {
|
||||
List<ASTClassOrInterfaceDeclaration> outerClasses = node.getParentsOfType(ASTClassOrInterfaceDeclaration.class);
|
||||
for (ASTClassOrInterfaceDeclaration outerClass : outerClasses) {
|
||||
ASTClassOrInterfaceBody classOrInterfaceBody = outerClass
|
||||
.getFirstChildOfType(ASTClassOrInterfaceBody.class);
|
||||
if (usedInOuter(decl, classOrInterfaceBody)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean usedInOuter(NameDeclaration decl, ASTTypeBody body) {
|
||||
for (ASTBodyDeclaration node : body.toStream()) {
|
||||
for (ASTPrimarySuffix primarySuffix : node.findDescendantsOfType(ASTPrimarySuffix.class, true)) {
|
||||
if (decl.getImage().equals(primarySuffix.getImage())) {
|
||||
return true; // No violation
|
||||
}
|
||||
public Object visitJavaNode(JavaNode node, Object data) {
|
||||
if (node instanceof ASTAnyTypeDeclaration) {
|
||||
ASTAnyTypeDeclaration type = (ASTAnyTypeDeclaration) node;
|
||||
if (hasIgnoredAnnotation(type)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (ASTPrimaryPrefix primaryPrefix : node.findDescendantsOfType(ASTPrimaryPrefix.class, true)) {
|
||||
ASTName name = primaryPrefix.getFirstDescendantOfType(ASTName.class);
|
||||
|
||||
if (name != null) {
|
||||
for (String id : name.getImage().split("\\.")) {
|
||||
if (id.equals(decl.getImage())) {
|
||||
return true; // No violation
|
||||
for (ASTFieldDeclaration field : type.getDeclarations()
|
||||
.filterIs(ASTFieldDeclaration.class)) {
|
||||
if (field.getVisibility() == Visibility.V_PRIVATE
|
||||
&& !JavaRuleUtil.isSerialPersistentFields(field)
|
||||
&& !JavaRuleUtil.isSerialVersionUID(field)
|
||||
&& !hasIgnoredAnnotation(field)) {
|
||||
for (ASTVariableDeclaratorId varId : field.getVarIds()) {
|
||||
if (JavaRuleUtil.isNeverUsed(varId)) {
|
||||
addViolation(data, varId, varId.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean actuallyUsed(List<NameOccurrence> usages) {
|
||||
for (NameOccurrence nameOccurrence : usages) {
|
||||
JavaNameOccurrence jNameOccurrence = (JavaNameOccurrence) nameOccurrence;
|
||||
if (!jNameOccurrence.isOnLeftHandSide()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isOK(String image) {
|
||||
return "serialVersionUID".equals(image) || "serialPersistentFields".equals(image) || "IDENT".equals(image);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Loaded 30 of 73 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user