Merge pull request #3189 from oowekyala:update-LinguisticNaming
[java] Update rule LinguisticNaming #3189
This commit is contained in:
commit
be94effc77
6 files changed
+83
-45
No files matched your search
@@ -85,7 +85,7 @@
|
||||
<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/LinguisticNaming"/> -->
|
||||
<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"/>
|
||||
|
||||
+6
@@ -70,6 +70,12 @@ public final class PrettyPrintingUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static String prettyPrintType(ASTType t) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
prettyPrintTypeNode(t, sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a normalized method name. This just looks at the image of the types of the parameters.
|
||||
*/
|
||||
|
||||
+31
-42
@@ -1,15 +1,14 @@
|
||||
/**
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.codestyle;
|
||||
|
||||
import static net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil.containsCamelCaseWord;
|
||||
import static net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil.startsWithCamelCaseWord;
|
||||
import static net.sourceforge.pmd.properties.PropertyFactory.booleanProperty;
|
||||
import static net.sourceforge.pmd.properties.PropertyFactory.stringListProperty;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -20,11 +19,18 @@ import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTType;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractIgnoredAnnotationRule;
|
||||
import net.sourceforge.pmd.lang.java.ast.Annotatable;
|
||||
import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
|
||||
import net.sourceforge.pmd.lang.java.rule.internal.JavaPropertyUtil;
|
||||
import net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind;
|
||||
import net.sourceforge.pmd.lang.java.types.TypeTestUtil;
|
||||
import net.sourceforge.pmd.properties.PropertyDescriptor;
|
||||
|
||||
public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
|
||||
public class LinguisticNamingRule extends AbstractJavaRulechainRule {
|
||||
private static final PropertyDescriptor<List<String>> IGNORED_ANNOTS =
|
||||
JavaPropertyUtil.ignoredAnnotationsDescriptor("java.lang.Override");
|
||||
|
||||
private static final PropertyDescriptor<Boolean> CHECK_BOOLEAN_METHODS =
|
||||
booleanProperty("checkBooleanMethod").defaultValue(true).desc("Check method names and types for inconsistent naming.").build();
|
||||
private static final PropertyDescriptor<Boolean> CHECK_GETTERS =
|
||||
@@ -57,6 +63,8 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
|
||||
.defaultValues("is", "has", "can", "have", "will", "should").build();
|
||||
|
||||
public LinguisticNamingRule() {
|
||||
super(ASTMethodDeclaration.class, ASTFieldDeclaration.class, ASTLocalVariableDeclaration.class);
|
||||
definePropertyDescriptor(IGNORED_ANNOTS);
|
||||
definePropertyDescriptor(CHECK_BOOLEAN_METHODS);
|
||||
definePropertyDescriptor(CHECK_GETTERS);
|
||||
definePropertyDescriptor(CHECK_SETTERS);
|
||||
@@ -67,14 +75,6 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
|
||||
definePropertyDescriptor(CHECK_FIELDS);
|
||||
definePropertyDescriptor(CHECK_VARIABLES);
|
||||
definePropertyDescriptor(BOOLEAN_FIELD_PREFIXES_PROPERTY);
|
||||
addRuleChainVisit(ASTMethodDeclaration.class);
|
||||
addRuleChainVisit(ASTFieldDeclaration.class);
|
||||
addRuleChainVisit(ASTLocalVariableDeclaration.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> defaultSuppressionAnnotations() {
|
||||
return Collections.checkedList(Arrays.asList("java.lang.Override"), String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,6 +105,10 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
|
||||
return data;
|
||||
}
|
||||
|
||||
private boolean hasIgnoredAnnotation(Annotatable node) {
|
||||
return node.isAnyAnnotationPresent(getProperty(IGNORED_ANNOTS));
|
||||
}
|
||||
|
||||
private void checkPrefixedTransformMethods(ASTMethodDeclaration node, Object data, String nameOfMethod) {
|
||||
List<String> prefixes = getProperty(TRANSFORM_METHOD_NAMES_PROPERTY);
|
||||
String[] splitMethodName = StringUtils.splitByCharacterTypeCamelCase(nameOfMethod);
|
||||
@@ -117,9 +121,8 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
|
||||
}
|
||||
|
||||
private void checkTransformMethods(ASTMethodDeclaration node, Object data, String nameOfMethod) {
|
||||
List<String> infixes = getProperty(TRANSFORM_METHOD_NAMES_PROPERTY);
|
||||
for (String infix : infixes) {
|
||||
if (node.isVoid() && containsWord(nameOfMethod, StringUtils.capitalize(infix))) {
|
||||
for (String infix : getProperty(TRANSFORM_METHOD_NAMES_PROPERTY)) {
|
||||
if (node.isVoid() && containsCamelCaseWord(nameOfMethod, StringUtils.capitalize(infix))) {
|
||||
// "To" or any other configured infix in the middle somewhere
|
||||
addViolationWithMessage(data, node, "Linguistics Antipattern - The transform method ''{0}'' should not return void linguistically",
|
||||
new Object[] { nameOfMethod });
|
||||
@@ -130,21 +133,21 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
|
||||
}
|
||||
|
||||
private void checkGetters(ASTMethodDeclaration node, Object data, String nameOfMethod) {
|
||||
if (hasPrefix(nameOfMethod, "get") && node.isVoid()) {
|
||||
if (startsWithCamelCaseWord(nameOfMethod, "get") && node.isVoid()) {
|
||||
addViolationWithMessage(data, node, "Linguistics Antipattern - The getter ''{0}'' should not return void linguistically",
|
||||
new Object[] { nameOfMethod });
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSetters(ASTMethodDeclaration node, Object data, String nameOfMethod) {
|
||||
if (hasPrefix(nameOfMethod, "set") && !node.isVoid()) {
|
||||
if (startsWithCamelCaseWord(nameOfMethod, "set") && !node.isVoid()) {
|
||||
addViolationWithMessage(data, node, "Linguistics Antipattern - The setter ''{0}'' should not return any type except void linguistically",
|
||||
new Object[] { nameOfMethod });
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBooleanType(ASTType node) {
|
||||
return "boolean".equalsIgnoreCase(node.getTypeImage())
|
||||
return node.getTypeMirror().unbox().isPrimitive(PrimitiveTypeKind.BOOLEAN)
|
||||
|| TypeTestUtil.isA("java.util.concurrent.atomic.AtomicBoolean", node)
|
||||
|| TypeTestUtil.isA("java.util.function.Predicate", node);
|
||||
}
|
||||
@@ -153,9 +156,9 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
|
||||
ASTType t = node.getResultTypeNode();
|
||||
if (!t.isVoid()) {
|
||||
for (String prefix : getProperty(BOOLEAN_METHOD_PREFIXES_PROPERTY)) {
|
||||
if (hasPrefix(nameOfMethod, prefix) && !isBooleanType(t)) {
|
||||
if (startsWithCamelCaseWord(nameOfMethod, prefix) && !isBooleanType(t)) {
|
||||
addViolationWithMessage(data, node, "Linguistics Antipattern - The method ''{0}'' indicates linguistically it returns a boolean, but it returns ''{1}''",
|
||||
new Object[] { nameOfMethod, t.getTypeImage() });
|
||||
new Object[] {nameOfMethod, PrettyPrintingUtil.prettyPrintType(t) });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,18 +166,18 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
|
||||
|
||||
private void checkField(ASTType typeNode, ASTVariableDeclarator node, Object data) {
|
||||
for (String prefix : getProperty(BOOLEAN_FIELD_PREFIXES_PROPERTY)) {
|
||||
if (hasPrefix(node.getName(), prefix) && !isBooleanType(typeNode)) {
|
||||
if (startsWithCamelCaseWord(node.getName(), prefix) && !isBooleanType(typeNode)) {
|
||||
addViolationWithMessage(data, node, "Linguistics Antipattern - The field ''{0}'' indicates linguistically it is a boolean, but it is ''{1}''",
|
||||
new Object[] { node.getName(), typeNode.getTypeImage() });
|
||||
new Object[] { node.getName(), PrettyPrintingUtil.prettyPrintType(typeNode) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkVariable(ASTType typeNode, ASTVariableDeclarator node, Object data) {
|
||||
for (String prefix : getProperty(BOOLEAN_FIELD_PREFIXES_PROPERTY)) {
|
||||
if (hasPrefix(node.getName(), prefix) && !isBooleanType(typeNode)) {
|
||||
if (startsWithCamelCaseWord(node.getName(), prefix) && !isBooleanType(typeNode)) {
|
||||
addViolationWithMessage(data, node, "Linguistics Antipattern - The variable ''{0}'' indicates linguistically it is a boolean, but it is ''{1}''",
|
||||
new Object[] { node.getName(), typeNode.getTypeImage() });
|
||||
new Object[] { node.getName(), PrettyPrintingUtil.prettyPrintType(typeNode) });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,8 +186,7 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
|
||||
public Object visit(ASTFieldDeclaration node, Object data) {
|
||||
ASTType type = node.getTypeNode();
|
||||
if (type != null && getProperty(CHECK_FIELDS)) {
|
||||
List<ASTVariableDeclarator> fields = node.findChildrenOfType(ASTVariableDeclarator.class);
|
||||
for (ASTVariableDeclarator field : fields) {
|
||||
for (ASTVariableDeclarator field : node.children(ASTVariableDeclarator.class)) {
|
||||
checkField(type, field, data);
|
||||
}
|
||||
}
|
||||
@@ -195,24 +197,11 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
|
||||
public Object visit(ASTLocalVariableDeclaration node, Object data) {
|
||||
ASTType type = node.getTypeNode();
|
||||
if (type != null && getProperty(CHECK_VARIABLES)) {
|
||||
List<ASTVariableDeclarator> variables = node.findChildrenOfType(ASTVariableDeclarator.class);
|
||||
for (ASTVariableDeclarator variable : variables) {
|
||||
for (ASTVariableDeclarator variable : node.children(ASTVariableDeclarator.class)) {
|
||||
checkVariable(type, variable, data);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private static boolean hasPrefix(String name, String prefix) {
|
||||
return name.startsWith(prefix) && name.length() > prefix.length()
|
||||
&& Character.isUpperCase(name.charAt(prefix.length()));
|
||||
}
|
||||
|
||||
private static boolean containsWord(String name, String word) {
|
||||
int index = name.indexOf(word);
|
||||
if (index >= 0 && name.length() > index + word.length()) {
|
||||
return Character.isUpperCase(name.charAt(index + word.length()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+31
-1
@@ -262,12 +262,42 @@ public final class JavaRuleUtil {
|
||||
* @param camelCaseString A string
|
||||
* @param prefixWord A prefix
|
||||
*/
|
||||
static boolean startsWithCamelCaseWord(String camelCaseString, String prefixWord) {
|
||||
public static boolean startsWithCamelCaseWord(String camelCaseString, String prefixWord) {
|
||||
return camelCaseString.startsWith(prefixWord)
|
||||
&& camelCaseString.length() > prefixWord.length()
|
||||
&& Character.isUpperCase(camelCaseString.charAt(prefixWord.length()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the string has the given word as a word, not at the start.
|
||||
* There needs to be a camelcase word boundary after the prefix.
|
||||
*
|
||||
* <code>
|
||||
* containsCamelCaseWord("isABoolean", "Bool") == false
|
||||
* containsCamelCaseWord("isABoolean", "A") == true
|
||||
* containsCamelCaseWord("isABoolean", "is") == error (not capitalized)
|
||||
* </code>
|
||||
*
|
||||
* @param camelCaseString A string
|
||||
* @param capitalizedWord A word, non-empty, capitalized
|
||||
*
|
||||
* @throws AssertionError If the word is empty or not capitalized
|
||||
*/
|
||||
public static boolean containsCamelCaseWord(String camelCaseString, String capitalizedWord) {
|
||||
assert capitalizedWord.length() > 0 && Character.isUpperCase(capitalizedWord.charAt(0))
|
||||
: "Not a capitalized string \"" + capitalizedWord + "\"";
|
||||
|
||||
int index = camelCaseString.indexOf(capitalizedWord);
|
||||
if (index >= 0 && camelCaseString.length() > index + capitalizedWord.length()) {
|
||||
return Character.isUpperCase(camelCaseString.charAt(index + capitalizedWord.length()));
|
||||
}
|
||||
if (index >= 0 && camelCaseString.length() == index + capitalizedWord.length()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isGetterOrSetterCall(ASTMethodCall call) {
|
||||
return call.getArguments().size() == 0
|
||||
&& (startsWithCamelCaseWord(call.getMethodName(), "get")
|
||||
|
||||
-1
@@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle;
|
||||
|
||||
import net.sourceforge.pmd.testframework.PmdRuleTst;
|
||||
|
||||
@org.junit.Ignore("Rule has not been updated yet")
|
||||
public class LinguisticNamingTest extends PmdRuleTst {
|
||||
// no additional unit tests
|
||||
}
|
||||
+14
@@ -4,6 +4,7 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.internal;
|
||||
|
||||
import static net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil.containsCamelCaseWord;
|
||||
import static net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil.startsWithCamelCaseWord;
|
||||
import static net.sourceforge.pmd.util.CollectionUtil.listOf;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -31,6 +32,19 @@ public class JavaRuleUtilTest extends BaseNonParserTest {
|
||||
assertThrows(NullPointerException.class, () -> startsWithCamelCaseWord("fnei", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsCamelCaseWords() {
|
||||
|
||||
assertFalse(containsCamelCaseWord("isABoolean", "Bool"), "no word boundary");
|
||||
assertTrue(containsCamelCaseWord("isABoolean", "A"), "ok word in the middle");
|
||||
assertTrue(containsCamelCaseWord("isABoolean", "Boolean"), "ok word at the end");
|
||||
|
||||
assertThrows(NullPointerException.class, () -> containsCamelCaseWord(null, "A"));
|
||||
assertThrows(NullPointerException.class, () -> containsCamelCaseWord("fnei", null));
|
||||
assertThrows(AssertionError.class, () -> containsCamelCaseWord("fnei", ""), "empty string");
|
||||
assertThrows(AssertionError.class, () -> containsCamelCaseWord("fnei", "a"), "not capitalized");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlattenConcatOperands() {
|
||||
ASTExpression e = parseExpr("s1+s2+s3");
|
||||
|
||||
Reference in new issue
Block a user