Merge pull request #3113 from oowekyala:java-usage-resolver

[java] Usage resolution for java variables #3113
This commit is contained in:
Andreas Dangel committed 2021-02-19 18:52:37 +01:00
commit 0ea6adfa96
73 files changed
+1581 -1704

No files matched your search

+15 -15
View File
@@ -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"/>
+4
View File
@@ -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();
}
@@ -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() {
@@ -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();
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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;
}
}
@@ -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