[java] Update rule NonThreadSafeSingleton
This commit is contained in:
4 files changed
+89
-77
No files matched your search
@@ -281,7 +281,7 @@
|
||||
<rule ref="category/java/multithreading.xml/DoNotUseThreads"/>
|
||||
<rule ref="category/java/multithreading.xml/DontCallThreadRun"/>
|
||||
<rule ref="category/java/multithreading.xml/DoubleCheckedLocking"/>
|
||||
<!-- <rule ref="category/java/multithreading.xml/NonThreadSafeSingleton"/> -->
|
||||
<rule ref="category/java/multithreading.xml/NonThreadSafeSingleton"/>
|
||||
<!-- <rule ref="category/java/multithreading.xml/UnsynchronizedStaticFormatter"/> -->
|
||||
<rule ref="category/java/multithreading.xml/UseConcurrentHashMap"/>
|
||||
<rule ref="category/java/multithreading.xml/UseNotifyAllInsteadOfNotify"/>
|
||||
|
||||
+39
-52
@@ -10,28 +10,24 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
|
||||
import net.sourceforge.pmd.RuleContext;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignmentExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTName;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTSynchronizedStatement;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
|
||||
import net.sourceforge.pmd.lang.java.ast.JModifier;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol;
|
||||
import net.sourceforge.pmd.properties.PropertyDescriptor;
|
||||
|
||||
|
||||
public class NonThreadSafeSingletonRule extends AbstractJavaRule {
|
||||
|
||||
private Map<String, ASTFieldDeclaration> fieldDecls = new HashMap<>();
|
||||
|
||||
private boolean checkNonStaticMethods = true;
|
||||
private boolean checkNonStaticFields = true;
|
||||
public class NonThreadSafeSingletonRule extends AbstractJavaRulechainRule {
|
||||
|
||||
private static final PropertyDescriptor<Boolean> CHECK_NON_STATIC_METHODS_DESCRIPTOR =
|
||||
booleanProperty("checkNonStaticMethods")
|
||||
@@ -42,70 +38,61 @@ public class NonThreadSafeSingletonRule extends AbstractJavaRule {
|
||||
.desc("Check for non-static fields. Do not set this to true and checkNonStaticMethods to false.")
|
||||
.defaultValue(false).build();
|
||||
|
||||
private Map<String, ASTFieldDeclaration> fieldDecls = new HashMap<>();
|
||||
|
||||
private boolean checkNonStaticMethods = true;
|
||||
private boolean checkNonStaticFields = true;
|
||||
|
||||
public NonThreadSafeSingletonRule() {
|
||||
super(ASTFieldDeclaration.class, ASTMethodDeclaration.class);
|
||||
definePropertyDescriptor(CHECK_NON_STATIC_METHODS_DESCRIPTOR);
|
||||
definePropertyDescriptor(CHECK_NON_STATIC_FIELDS_DESCRIPTOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTCompilationUnit node, Object data) {
|
||||
public void start(RuleContext ctx) {
|
||||
fieldDecls.clear();
|
||||
checkNonStaticMethods = getProperty(CHECK_NON_STATIC_METHODS_DESCRIPTOR);
|
||||
checkNonStaticFields = getProperty(CHECK_NON_STATIC_FIELDS_DESCRIPTOR);
|
||||
return super.visit(node, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTFieldDeclaration node, Object data) {
|
||||
if (checkNonStaticFields || node.isStatic()) {
|
||||
fieldDecls.put(node.getVariableName(), node);
|
||||
if (checkNonStaticFields || node.hasModifiers(JModifier.STATIC)) {
|
||||
for (ASTVariableDeclaratorId varId : node.getVarIds()) {
|
||||
fieldDecls.put(varId.getName(), node);
|
||||
}
|
||||
}
|
||||
return super.visit(node, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTMethodDeclaration node, Object data) {
|
||||
|
||||
if (checkNonStaticMethods && !node.isStatic() || node.isSynchronized()) {
|
||||
return super.visit(node, data);
|
||||
if (checkNonStaticMethods && !node.hasModifiers(JModifier.STATIC) || node.hasModifiers(JModifier.SYNCHRONIZED)) {
|
||||
return data;
|
||||
}
|
||||
|
||||
List<ASTIfStatement> ifStatements = node.findDescendantsOfType(ASTIfStatement.class);
|
||||
List<ASTIfStatement> ifStatements = node.descendants(ASTIfStatement.class).toList();
|
||||
for (ASTIfStatement ifStatement : ifStatements) {
|
||||
if (ifStatement.getFirstParentOfType(ASTSynchronizedStatement.class) == null) {
|
||||
if (!ifStatement.hasDescendantOfType(ASTNullLiteral.class)) {
|
||||
if (ifStatement.ancestors(ASTSynchronizedStatement.class).isEmpty()) {
|
||||
if (ifStatement.getCondition().descendants(ASTNullLiteral.class).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
ASTName n = ifStatement.getFirstDescendantOfType(ASTName.class);
|
||||
if (n == null || !fieldDecls.containsKey(n.getImage())) {
|
||||
ASTNamedReferenceExpr n = ifStatement.getCondition().descendants(ASTNamedReferenceExpr.class).first();
|
||||
if (n == null || !fieldDecls.containsKey(n.getName())) {
|
||||
continue;
|
||||
}
|
||||
List<ASTAssignmentOperator> assignments = ifStatement
|
||||
.findDescendantsOfType(ASTAssignmentOperator.class);
|
||||
List<ASTAssignmentExpression> assignments = ifStatement.descendants(ASTAssignmentExpression.class).toList();
|
||||
boolean violation = false;
|
||||
for (int ix = 0; ix < assignments.size(); ix++) {
|
||||
ASTAssignmentOperator oper = assignments.get(ix);
|
||||
if (!(oper.getParent() instanceof ASTStatementExpression)) {
|
||||
continue;
|
||||
}
|
||||
ASTStatementExpression expr = (ASTStatementExpression) oper.getParent();
|
||||
if (expr.getChild(0) instanceof ASTPrimaryExpression
|
||||
&& ((ASTPrimaryExpression) expr.getChild(0)).getNumChildren() == 1
|
||||
&& ((ASTPrimaryExpression) expr.getChild(0))
|
||||
.getChild(0) instanceof ASTPrimaryPrefix) {
|
||||
ASTPrimaryPrefix pp = (ASTPrimaryPrefix) ((ASTPrimaryExpression) expr.getChild(0))
|
||||
.getChild(0);
|
||||
String name = null;
|
||||
if (pp.usesThisModifier()) {
|
||||
ASTPrimarySuffix priSuf = expr.getFirstDescendantOfType(ASTPrimarySuffix.class);
|
||||
name = priSuf.getImage();
|
||||
} else {
|
||||
ASTName astName = (ASTName) pp.getChild(0);
|
||||
name = astName.getImage();
|
||||
}
|
||||
if (fieldDecls.containsKey(name)) {
|
||||
violation = true;
|
||||
for (ASTAssignmentExpression assignment : assignments) {
|
||||
ASTAssignableExpr left = assignment.getLeftOperand();
|
||||
if (left instanceof ASTNamedReferenceExpr) {
|
||||
JVariableSymbol referencedSym = ((ASTNamedReferenceExpr) left).getReferencedSym();
|
||||
if (referencedSym instanceof JFieldSymbol) {
|
||||
String name = ((ASTNamedReferenceExpr) left).getName();
|
||||
if (fieldDecls.containsKey(name)) {
|
||||
violation = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,6 +101,6 @@ public class NonThreadSafeSingletonRule extends AbstractJavaRule {
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visit(node, data);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
-1
@@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading;
|
||||
|
||||
import net.sourceforge.pmd.testframework.PmdRuleTst;
|
||||
|
||||
@org.junit.Ignore("Rule has not been updated yet")
|
||||
public class NonThreadSafeSingletonTest extends PmdRuleTst {
|
||||
// no additional unit tests
|
||||
}
|
||||
+49
-23
@@ -7,11 +7,15 @@
|
||||
<test-code>
|
||||
<description>failure case</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-linenumbers>7</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Foo {
|
||||
private static List buz;
|
||||
public static List bar() {
|
||||
if (buz == null) buz = new ArrayList();
|
||||
private static List<Object> buz;
|
||||
public static List<Object> bar() {
|
||||
if (buz == null) buz = new ArrayList<>();
|
||||
return buz;
|
||||
}
|
||||
}
|
||||
@@ -22,10 +26,13 @@ public class Foo {
|
||||
<description>OK, method is synchronized</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Foo {
|
||||
private static List buz;
|
||||
public static synchronized List bar() {
|
||||
if (buz == null) buz = new ArrayList();
|
||||
private static List<Object> buz;
|
||||
public static synchronized List<Object> bar() {
|
||||
if (buz == null) buz = new ArrayList<>();
|
||||
return buz;
|
||||
}
|
||||
}
|
||||
@@ -36,11 +43,14 @@ public class Foo {
|
||||
<description>OK, in synchronized block</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Foo {
|
||||
private static List buz;
|
||||
public static List bar() {
|
||||
private static List<Object> buz;
|
||||
public static List<Object> bar() {
|
||||
synchronized (baz) {
|
||||
if (buz == null) buz = new ArrayList();
|
||||
if (buz == null) buz = new ArrayList<>();
|
||||
return buz;
|
||||
}
|
||||
}
|
||||
@@ -52,10 +62,12 @@ public class Foo {
|
||||
<description>OK, in returning non-static data</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
import java.util.Locale;
|
||||
|
||||
public class Foo {
|
||||
private static Locale locale;
|
||||
public static List bar() {
|
||||
if (locale==null) return Locale.getDefault();
|
||||
public static Locale bar() {
|
||||
if (locale == null) return Locale.getDefault();
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
@@ -65,13 +77,17 @@ public class Foo {
|
||||
<test-code>
|
||||
<description>failure case, two if statements</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-linenumbers>8</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Foo {
|
||||
private static List buz;
|
||||
private static List<Object> buz;
|
||||
private static boolean b = false;
|
||||
public static List bar(String foo) {
|
||||
public static List<Object> bar(String foo) {
|
||||
if (buz == null) {
|
||||
buz = new ArrayList();
|
||||
buz = new ArrayList<>();
|
||||
if (foo == null) {
|
||||
b = true;
|
||||
}
|
||||
@@ -85,12 +101,16 @@ public class Foo {
|
||||
<test-code>
|
||||
<description>failure case, compound if statement</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-linenumbers>7</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Foo {
|
||||
private static List list;
|
||||
public static List bar(String param) {
|
||||
private static List<Object> list;
|
||||
public static List<Object> bar(String param) {
|
||||
if (list == null || !param.equals("foo")) {
|
||||
list = new ArrayList();
|
||||
list = new ArrayList<>();
|
||||
param = "x";
|
||||
}
|
||||
return list;
|
||||
@@ -102,12 +122,16 @@ public class Foo {
|
||||
<test-code>
|
||||
<description>failure case 2</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-linenumbers>7</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class Foo {
|
||||
private static List buz = null;
|
||||
private static List bar() {
|
||||
private static List<Object> buz = null;
|
||||
private static List<Object> bar() {
|
||||
if (buz == null) {
|
||||
buz = Collections.get(Integer.MAX_SIZE);
|
||||
buz = Collections.<Object> emptyList();
|
||||
}
|
||||
return buz;
|
||||
}
|
||||
@@ -134,15 +158,17 @@ public class A {
|
||||
<test-code>
|
||||
<description>#997 Rule NonThreadSafeSingleton gives analysis problem</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-linenumbers>7</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
public class A extends B {
|
||||
public class B {}
|
||||
class A extends B {
|
||||
static B instance = null;
|
||||
private boolean bar = false;
|
||||
|
||||
static void foo() {
|
||||
if (instance == null ) {
|
||||
if (instance == null) {
|
||||
instance = new A();
|
||||
((A)instance).bar=false;
|
||||
((A)instance).bar = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user