diff --git a/p3c-pmd/.gitignore b/p3c-pmd/.gitignore index ddb1f85c18436c4e4a1ce9fdee71381482b9b308..f9481d30deb3be45c9fd2b5389f5df1110f3267e 100644 --- a/p3c-pmd/.gitignore +++ b/p3c-pmd/.gitignore @@ -69,6 +69,7 @@ configuration/** # sass gitignore# .sass-cache .idea +.vscode # tcc_coverage coverage.ec @@ -88,3 +89,4 @@ hsf.configuration/ #hsf test *.instance +/target/ diff --git a/p3c-pmd/pom.xml b/p3c-pmd/pom.xml index 18c8bbec999a36be6ce59a975a17a4bf0330e63b..b75e58c1a212c85b6c84c03d02490c6996301dc1 100644 --- a/p3c-pmd/pom.xml +++ b/p3c-pmd/pom.xml @@ -9,7 +9,7 @@ com.alibaba.p3c p3c-pmd - 2.0.0 + 2.0.1 jar p3c-pmd @@ -17,6 +17,7 @@ 6.15.0 1.8 1.3.2 + 1.3.50 Alibaba Java Coding Guidelines PMD implementations https://github.com/alibaba/p3c @@ -99,6 +100,11 @@ javax.annotation-api ${annotation.version} + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + @@ -142,6 +148,37 @@ + + kotlin-maven-plugin + org.jetbrains.kotlin + ${kotlin.version} + + + compile + + compile + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/main/java + + + + + test-compile + + test-compile + + + + ${project.basedir}/src/test/kotlin + ${project.basedir}/src/test/java + + + + + org.apache.maven.plugins maven-compiler-plugin @@ -151,6 +188,32 @@ ${maven.compiler.target} UTF-8 + + + + default-compile + none + + + + default-testCompile + none + + + java-compile + compile + + compile + + + + java-test-compile + test-compile + + testCompile + + + maven-assembly-plugin @@ -190,20 +253,38 @@ - + + maven-assembly-plugin + 3.0.0 + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidCallStaticSimpleDateFormatRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidCallStaticSimpleDateFormatRule.java index 4a59a9320425410f41aa193990f456630dec30a1..6a6efea4487f369d3c33d33284535daaf43bf2e3 100644 --- a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidCallStaticSimpleDateFormatRule.java +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidCallStaticSimpleDateFormatRule.java @@ -19,7 +19,6 @@ import java.text.SimpleDateFormat; import java.util.HashSet; import java.util.Set; import java.util.Stack; -import java.util.concurrent.locks.Lock; import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; @@ -37,6 +36,10 @@ import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode; import net.sourceforge.pmd.lang.java.ast.Token; +import static com.alibaba.p3c.pmd.lang.java.rule.util.NodeUtils.isLockNode; +import static com.alibaba.p3c.pmd.lang.java.rule.util.NodeUtils.isLockStatementExpression; +import static com.alibaba.p3c.pmd.lang.java.rule.util.NodeUtils.isUnLockStatementExpression; + /** * [Mandatory] SimpleDataFormat is unsafe, do not define it as a static variable. * If have to, lock or DateUtils class must be used. @@ -46,8 +49,6 @@ import net.sourceforge.pmd.lang.java.ast.Token; */ public class AvoidCallStaticSimpleDateFormatRule extends AbstractAliRule { private static final String FORMAT_METHOD_NAME = "format"; - private static final String LOCK_NAME = "lock"; - private static final String UN_LOCK_NAME = "unlock"; @Override public Object visit(ASTMethodDeclaration node, Object data) { @@ -101,7 +102,9 @@ public class AvoidCallStaticSimpleDateFormatRule extends AbstractAliRule { // add lock node stack.push(flowNode.getNode()); return; - } else if (isUnLockStatementExpression(statementExpression)) { + } + + if (isUnLockStatementExpression(statementExpression)) { // remove element in lock block while (!stack.isEmpty()) { Node node = stack.pop(); @@ -132,16 +135,10 @@ public class AvoidCallStaticSimpleDateFormatRule extends AbstractAliRule { return name.getImage(); } - private boolean isLockNode(Node node) { - if (!(node instanceof ASTStatementExpression)) { - return false; - } - ASTStatementExpression statementExpression = (ASTStatementExpression)node; - return isLockStatementExpression(statementExpression); - } - - private boolean isStaticSimpleDateFormatCall(ASTPrimaryExpression primaryExpression, - Set localSimpleDateFormatNames) { + private boolean isStaticSimpleDateFormatCall( + ASTPrimaryExpression primaryExpression, + Set localSimpleDateFormatNames + ) { if (primaryExpression.jjtGetNumChildren() == 0) { return false; } @@ -149,6 +146,9 @@ public class AvoidCallStaticSimpleDateFormatRule extends AbstractAliRule { if (name == null || name.getType() != SimpleDateFormat.class) { return false; } + if (name.getNameDeclaration() == null || name.getNameDeclaration().getName() == null) { + return false; + } if (localSimpleDateFormatNames.contains(name.getNameDeclaration().getName())) { return false; } @@ -161,20 +161,4 @@ public class AvoidCallStaticSimpleDateFormatRule extends AbstractAliRule { return FORMAT_METHOD_NAME.equals(token.image); } - private boolean isLockStatementExpression(ASTStatementExpression statementExpression) { - return isLockTypeAndMethod(statementExpression, LOCK_NAME); - } - - private boolean isUnLockStatementExpression(ASTStatementExpression statementExpression) { - return isLockTypeAndMethod(statementExpression, UN_LOCK_NAME); - } - - private boolean isLockTypeAndMethod(ASTStatementExpression statementExpression, String methodName) { - ASTName name = statementExpression.getFirstDescendantOfType(ASTName.class); - if (name == null || name.getType() == null || !Lock.class.isAssignableFrom(name.getType())) { - return false; - } - Token token = (Token)name.jjtGetLastToken(); - return methodName.equals(token.image); - } } diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidManuallyCreateThreadRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidManuallyCreateThreadRule.java index 1ad3a8c235fea801b11009169ea45b6d71b15259..ad11f3771b90443183c511f27552857eac81ba30 100644 --- a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidManuallyCreateThreadRule.java +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidManuallyCreateThreadRule.java @@ -19,8 +19,8 @@ import java.util.List; import java.util.concurrent.ThreadFactory; import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; - import com.alibaba.p3c.pmd.lang.java.rule.util.NodeUtils; + import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/LowerCamelCaseVariableNamingRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/LowerCamelCaseVariableNamingRule.java index b723ff26333d22dd615cc1ccf9199af52998e693..e009beff43046050b1642df62c084b7244c5ceb1 100644 --- a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/LowerCamelCaseVariableNamingRule.java +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/LowerCamelCaseVariableNamingRule.java @@ -15,6 +15,8 @@ */ package com.alibaba.p3c.pmd.lang.java.rule.naming; +import java.util.regex.Pattern; + import com.alibaba.p3c.pmd.I18nResources; import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; import com.alibaba.p3c.pmd.lang.java.util.StringAndCharConstants; @@ -27,8 +29,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; import net.sourceforge.pmd.lang.java.ast.ASTTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import java.util.regex.Pattern; - /** * [Mandatory] Method names, parameter names, member variable names, and local variable names should be written in * lowerCamelCase. @@ -39,7 +39,6 @@ import java.util.regex.Pattern; public class LowerCamelCaseVariableNamingRule extends AbstractAliRule { private static final String MESSAGE_KEY_PREFIX = "java.naming.LowerCamelCaseVariableNamingRule.violation.msg"; - private Pattern pattern = Pattern.compile("^[a-z][a-z0-9]*([A-Z][a-z0-9]+)*(DO|DTO|VO|DAO|BO|DOList|DTOList|VOList|DAOList|BOList|X|Y|Z|UDF|UDAF|[A-Z])?$"); @Override diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/WrapperTypeEqualityRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/WrapperTypeEqualityRule.java index 1936940bce17ac7398215e45871ba4e212711788..b1a555ff3b821e4215060de099f82c15d1491515 100644 --- a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/WrapperTypeEqualityRule.java +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/WrapperTypeEqualityRule.java @@ -65,4 +65,5 @@ public class WrapperTypeEqualityRule extends AbstractAliRule { return "length".equals(expression.jjtGetLastToken().getImage()) && ".".equals(expression.jjtGetFirstToken().getNext().getImage()); } + } diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/ClassCastExceptionWithToArrayRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/ClassCastExceptionWithToArrayRule.java index df2d8909f92dcf752b4d3569427a57a216a0068f..53455d41a59e9056d691011ce43114e9f29da780 100644 --- a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/ClassCastExceptionWithToArrayRule.java +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/ClassCastExceptionWithToArrayRule.java @@ -71,7 +71,7 @@ public class ClassCastExceptionWithToArrayRule extends AbstractAliRule { if (childName.endsWith(".toArray") && suffix.getArgumentCount() == 0 && primarySuffixs.size() == 1) { addViolationWithMessage(data, item, - "java.set.ClassCastExceptionWithSubListToArrayListRule.violation.msg", + "java.set.ClassCastExceptionWithToArrayRule.violation.msg", new Object[] {childName}); } } diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/util/NodeUtils.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/util/NodeUtils.java index cc480e092ba14e2c064af5daadb03fb0947abf4e..2582e59c2188388e949c0a97fc55b4779432c32c 100644 --- a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/util/NodeUtils.java +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/util/NodeUtils.java @@ -15,10 +15,15 @@ */ package com.alibaba.p3c.pmd.lang.java.rule.util; +import java.util.concurrent.locks.Lock; + import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; +import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; import net.sourceforge.pmd.lang.java.ast.AbstractJavaAccessTypeNode; +import net.sourceforge.pmd.lang.java.ast.Token; import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; /** @@ -26,6 +31,10 @@ import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; * @date 2016/11/16 */ public class NodeUtils { + public static final String LOCK_NAME = "lock"; + public static final String LOCK_INTERRUPTIBLY_NAME = "lockInterruptibly"; + public static final String UN_LOCK_NAME = "unlock"; + public static boolean isParentOrSelf(Node descendant, Node ancestor) { if (descendant == ancestor) { return true; @@ -64,4 +73,29 @@ public class NodeUtils { public static Class getNodeType(AbstractJavaAccessTypeNode node) { return node == null ? null : node.getType(); } + + public static boolean isLockStatementExpression(ASTStatementExpression statementExpression) { + return isLockTypeAndMethod(statementExpression, LOCK_NAME); + } + + public static boolean isUnLockStatementExpression(ASTStatementExpression statementExpression) { + return isLockTypeAndMethod(statementExpression, UN_LOCK_NAME); + } + + private static boolean isLockTypeAndMethod(ASTStatementExpression statementExpression, String methodName) { + ASTName name = statementExpression.getFirstDescendantOfType(ASTName.class); + if (name == null || name.getType() == null || !Lock.class.isAssignableFrom(name.getType())) { + return false; + } + Token token = (Token)name.jjtGetLastToken(); + return methodName.equals(token.image); + } + + public static boolean isLockNode(Node node) { + if (!(node instanceof ASTStatementExpression)) { + return false; + } + ASTStatementExpression statementExpression = (ASTStatementExpression)node; + return isLockStatementExpression(statementExpression); + } } diff --git a/p3c-pmd/src/main/kotlin/com/alibaba/p3c/pmd/lang/java/rule/concurrent/LockShouldWithTryFinallyRule.kt b/p3c-pmd/src/main/kotlin/com/alibaba/p3c/pmd/lang/java/rule/concurrent/LockShouldWithTryFinallyRule.kt new file mode 100644 index 0000000000000000000000000000000000000000..fe2a74ccd4012498c5a70d847cd65c3522592906 --- /dev/null +++ b/p3c-pmd/src/main/kotlin/com/alibaba/p3c/pmd/lang/java/rule/concurrent/LockShouldWithTryFinallyRule.kt @@ -0,0 +1,120 @@ +package com.alibaba.p3c.pmd.lang.java.rule.concurrent + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule +import com.alibaba.p3c.pmd.lang.java.rule.util.NodeUtils.LOCK_INTERRUPTIBLY_NAME +import com.alibaba.p3c.pmd.lang.java.rule.util.NodeUtils.LOCK_NAME +import com.alibaba.p3c.pmd.lang.java.rule.util.NodeUtils.UN_LOCK_NAME +import net.sourceforge.pmd.lang.java.ast.ASTBlock +import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement +import net.sourceforge.pmd.lang.java.ast.ASTFinallyStatement +import net.sourceforge.pmd.lang.java.ast.ASTName +import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression +import net.sourceforge.pmd.lang.java.ast.ASTTryStatement +import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode +import java.util.concurrent.locks.Lock + +/** + * @author caikang + * @date 2019/09/29 + */ +open class LockShouldWithTryFinallyRule : AbstractAliRule() { + + override fun visit(node: ASTBlock, data: Any): Any? { + checkBlock(node, data) + return super.visit(node, data) + } + + private fun checkBlock(block: ASTBlock, data: Any) { + val statements = block.findChildrenOfType(ASTBlockStatement::class.java) + if (statements.isNullOrEmpty()) { + return + } + var lockExpression: ASTStatementExpression? = null + for (statement in statements) { + if (lockExpression != null) { + // check try finally + val tryStatement = findNodeByXpath( + statement, XPATH_TRY_STATEMENT, + ASTTryStatement::class.java + ) + if (!checkTryStatement(tryStatement)) { + addLockViolation(data, lockExpression) + } + lockExpression = null + continue + } + // find lock expression + val expression = findNodeByXpath( + statement, XPATH_LOCK_STATEMENT, + ASTStatementExpression::class.java + ) ?: continue + + if (!expression.isLock) { + continue + } + val astName = expression.getFirstDescendantOfType(ASTName::class.java) + val lockMethod = astName?.image?.let { + it.endsWith(".$LOCK_NAME") || it.endsWith(".$LOCK_INTERRUPTIBLY_NAME") + } ?: false + if (!lockMethod) { + continue + } + lockExpression = expression + } + lockExpression?.let { + addLockViolation(data, it) + } + } + + private fun addLockViolation(data: Any, lockExpression: ASTStatementExpression) { + addViolationWithMessage( + data, lockExpression, + "java.concurrent.LockShouldWithTryFinallyRule.violation.msg", + arrayOf(getExpressName(lockExpression)) + ) + } + + private fun checkTryStatement(tryStatement: ASTTryStatement?): Boolean { + if (tryStatement == null) { + return false + } + val finallyStatement = tryStatement.getFirstChildOfType(ASTFinallyStatement::class.java) ?: return false + val statementExpression = findNodeByXpath( + finallyStatement, + XPATH_UNLOCK_STATEMENT, ASTStatementExpression::class.java + ) ?: return false + + if (!statementExpression.isLock) { + return false + } + val astName = statementExpression.getFirstDescendantOfType(ASTName::class.java) ?: return false + return astName.image?.endsWith(".$UN_LOCK_NAME") ?: false + } + + private fun findNodeByXpath(statement: AbstractJavaNode, xpath: String, clazz: Class): T? { + val nodes = statement.findChildNodesWithXPath(xpath) + if (nodes == null || nodes.isEmpty()) { + return null + } + val node = nodes[0] + return if (!clazz.isAssignableFrom(node.javaClass)) { + null + } else clazz.cast(node) + } + + private fun getExpressName(statementExpression: ASTStatementExpression): String { + val name = statementExpression.getFirstDescendantOfType(ASTName::class.java) + return name.image + } + + companion object { + private const val XPATH_LOCK_STATEMENT = "Statement/StatementExpression" + private const val XPATH_UNLOCK_STATEMENT = "Block/BlockStatement/Statement/StatementExpression" + private const val XPATH_TRY_STATEMENT = "Statement/TryStatement" + } + + private val ASTStatementExpression?.isLock: Boolean + get() = this?.type?.let { + Lock::class.java.isAssignableFrom(it) + } ?: false +} diff --git a/p3c-pmd/src/main/resources/messages.xml b/p3c-pmd/src/main/resources/messages.xml index b5b1cba95e9ef828509fc3d9a2dd603f892188b1..848f3edd735d8f6175852d7d1c48bc2056821fdd 100644 --- a/p3c-pmd/src/main/resources/messages.xml +++ b/p3c-pmd/src/main/resources/messages.xml @@ -178,6 +178,15 @@ + + + + + + diff --git a/p3c-pmd/src/main/resources/messages_en.xml b/p3c-pmd/src/main/resources/messages_en.xml index 4666a7f33d8f4ce41b13666edb01288f06e4bcf7..9491ff065d978bd08f49bb687ae57e5b8cfd2bc6 100644 --- a/p3c-pmd/src/main/resources/messages_en.xml +++ b/p3c-pmd/src/main/resources/messages_en.xml @@ -180,7 +180,15 @@ Note: Below are the problems created by usage of Executors for thread pool creat - + + + + + + diff --git a/p3c-pmd/src/main/resources/rulesets/java/ali-concurrent.xml b/p3c-pmd/src/main/resources/rulesets/java/ali-concurrent.xml index fc0ab14ea9db42094e97f45da38ddd378fd75d53..d4ba1c819bcd603a8c5f1f539fffa747e5a865f3 100644 --- a/p3c-pmd/src/main/resources/rulesets/java/ali-concurrent.xml +++ b/p3c-pmd/src/main/resources/rulesets/java/ali-concurrent.xml @@ -286,4 +286,44 @@ Positive example 2: ]]> + + java.concurrent.LockShouldWithTryFinallyRule.rule.desc + 1 + + + + + + + diff --git a/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ConcurrentRuleTest.java b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ConcurrentRuleTest.java index 6cff269a00645efcd001cc18d036836c1b4bde66..7b2b8799d5e02e10896b4a30f2f485b3111fb459 100644 --- a/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ConcurrentRuleTest.java +++ b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ConcurrentRuleTest.java @@ -37,5 +37,6 @@ public class ConcurrentRuleTest extends SimpleAggregatorTst { addRule(RULE_NAME, "ThreadLocalShouldRemoveRule"); addRule(RULE_NAME, "AvoidConcurrentCompetitionRandomRule"); addRule(RULE_NAME, "CountDownShouldInFinallyRule"); + addRule(RULE_NAME, "LockShouldWithTryFinallyRule"); } } diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/LockShouldWithTryFinallyRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/LockShouldWithTryFinallyRule.xml new file mode 100644 index 0000000000000000000000000000000000000000..683b343c50c86d0db56219c9eb32446ae97328da --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/LockShouldWithTryFinallyRule.xml @@ -0,0 +1,43 @@ + + + + LockShouldWithTryFinallyRule + 2 + 15,20 + + +