Skip to content

Commit

Permalink
Issue checkstyle#14726: new implementation of the module
Browse files Browse the repository at this point in the history
  • Loading branch information
Zopsss committed May 4, 2024
1 parent b078ceb commit bc12147
Show file tree
Hide file tree
Showing 20 changed files with 154 additions and 98 deletions.
Expand Up @@ -1673,6 +1673,13 @@
</details>
</checkerFrameworkError>

<checkerFrameworkError unstable="false">
<fileName>src/main/java/com/puppycrawl/tools/checkstyle/checks/coding/ConstructorsDeclarationGroupingCheck.java</fileName>
<specifier>dereference.of.nullable</specifier>
<message>dereference of possibly-null reference previousCtor</message>
<lineContent>firstNonCtorSiblingLineNo = previousCtor.getNextSibling().getLineNo();</lineContent>
</checkerFrameworkError>

<checkerFrameworkError unstable="false">
<fileName>src/main/java/com/puppycrawl/tools/checkstyle/checks/coding/DeclarationOrderCheck.java</fileName>
<specifier>argument</specifier>
Expand Down
Expand Up @@ -47,7 +47,7 @@ public void testClass() throws Exception {

final String[] expectedViolation = {
"10:5: " + getCheckMessage(clazz,
ConstructorsDeclarationGroupingCheck.MSG_KEY, 6),
ConstructorsDeclarationGroupingCheck.MSG_KEY, 8),
};

final List<String> expectedXpathQueries = Arrays.asList(
Expand Down Expand Up @@ -79,7 +79,7 @@ public void testEnum() throws Exception {

final String[] expectedViolation = {
"12:5: " + getCheckMessage(clazz,
ConstructorsDeclarationGroupingCheck.MSG_KEY, 8),
ConstructorsDeclarationGroupingCheck.MSG_KEY, 10),
};

final List<String> expectedXpathQueries = Arrays.asList(
Expand Down Expand Up @@ -112,7 +112,7 @@ public void testRecords() throws Exception {

final String[] expectedViolation = {
"14:5: " + getCheckMessage(clazz,
ConstructorsDeclarationGroupingCheck.MSG_KEY, 8),
ConstructorsDeclarationGroupingCheck.MSG_KEY, 12),
};

final List<String> expectedXpathQueries = Arrays.asList(
Expand Down
Expand Up @@ -12,9 +12,5 @@ public MyRecord(int a) {
void foo() {}

public MyRecord {} // warn

public MyRecord(int x, int y, int z) {
this(x+y, z);
}
}
}
Expand Up @@ -19,20 +19,20 @@

package com.puppycrawl.tools.checkstyle.checks.coding;

import java.util.HashMap;
import java.util.Map;

import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
import com.puppycrawl.tools.checkstyle.StatelessCheck;
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.utils.TokenUtil;

/**
* <p>
* Checks that all constructors are grouped together.
* If there is any code between the constructors
* then this check will give an error.
* Comments between constructors are ignored.
* The check identifies and flags any constructors that are not grouped together.
* The error message will specify the line number from where the constructors are separated.
* Comments between constructors are allowed.
* </p>
* <p>
* Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
Expand All @@ -46,10 +46,10 @@
* </li>
* </ul>
*
* @since 10.16.0
* @since 10.17.0
*/

@FileStatefulCheck
@StatelessCheck
public class ConstructorsDeclarationGroupingCheck extends AbstractCheck {

/**
Expand All @@ -58,11 +58,6 @@ public class ConstructorsDeclarationGroupingCheck extends AbstractCheck {
*/
public static final String MSG_KEY = "constructors.declaration.grouping";

/**
* Specifies different Object Blocks scope.
*/
private final Map<DetailAST, Integer> allObjBlocks = new HashMap<>();

@Override
public int[] getDefaultTokens() {
return getRequiredTokens();
Expand All @@ -76,35 +71,59 @@ public int[] getAcceptableTokens() {
@Override
public int[] getRequiredTokens() {
return new int[] {
TokenTypes.CTOR_DEF,
TokenTypes.COMPACT_CTOR_DEF,
TokenTypes.OBJBLOCK,
};
}

@Override
public void beginTree(DetailAST rootAst) {
allObjBlocks.clear();
public void visitToken(DetailAST ast) {
final int parentType = ast.getParent().getType();

final int[] tokenTypes = {
TokenTypes.CLASS_DEF,
TokenTypes.ENUM_DEF,
TokenTypes.RECORD_DEF,
};

if (TokenUtil.isOfType(parentType, tokenTypes)) {
checkConstructorsGrouping(ast);
}
}

@Override
public void visitToken(DetailAST ast) {
final DetailAST currObjBlock = ast.getParent();
final Integer previousCtorLineNo = allObjBlocks.get(currObjBlock);
/**
* Checks that if constructors are grouped together, they should not be
* separated from each other.
*
* @param objectBlock is a class, enum or record block.
*/
private void checkConstructorsGrouping(DetailAST objectBlock) {
DetailAST currentToken = objectBlock.getFirstChild();
DetailAST previousCtor = null;
int firstNonCtorSiblingLineNo = -1;
boolean ctorOccured = false;
boolean isViolation = false;

while (currentToken != null) {
if (currentToken.getType() == TokenTypes.CTOR_DEF
|| currentToken.getType() == TokenTypes.COMPACT_CTOR_DEF) {
final DetailAST previousSibling = currentToken.getPreviousSibling();
final boolean isCtor = previousSibling.getType() == TokenTypes.CTOR_DEF
|| previousSibling.getType() == TokenTypes.COMPACT_CTOR_DEF;

if (previousCtorLineNo != null) {
final DetailAST previousSibling = ast.getPreviousSibling();
final int siblingType = previousSibling.getType();
final boolean isCtor = siblingType == TokenTypes.CTOR_DEF;
final boolean isCompactCtor = siblingType == TokenTypes.COMPACT_CTOR_DEF;
if (ctorOccured && !isViolation && !isCtor) {
firstNonCtorSiblingLineNo = previousCtor.getNextSibling().getLineNo();
isViolation = true;
}

if (!isCtor && !isCompactCtor) {
log(ast, MSG_KEY, previousCtorLineNo);
if (isViolation) {
log(currentToken, MSG_KEY, firstNonCtorSiblingLineNo);
}

previousCtor = currentToken;
ctorOccured = true;
}

allObjBlocks.put(currObjBlock, ast.getLineNo());
}
else {
allObjBlocks.put(currObjBlock, ast.getLineNo());
currentToken = currentToken.getNextSibling();
}
}
}
Expand Up @@ -3,7 +3,7 @@ assignment.inner.avoid=Inner assignments should be avoided.
avoid.clone.method=Avoid using clone method.
avoid.double.brace.init=Avoid double brace initialization.
avoid.finalizer.method=Avoid using finalizer method.
constructors.declaration.grouping=All constructors should be grouped together. Previous constructor was at line number ''{0}''.
constructors.declaration.grouping=All constructors should be grouped together. The constructors got separated at line ''{0}''.
covariant.equals=covariant equals without overriding equals(java.lang.Object).
declaration.order.access=Variable access definition in wrong order.
declaration.order.constructor=Constructor definition in wrong order.
Expand Down
Expand Up @@ -3,7 +3,7 @@ assignment.inner.avoid=Älä käytä sisäkkäisiä sijoituksia.
avoid.clone.method=Vältä klooni menetelmää.
avoid.double.brace.init=Vältä kaksinkertaista ahdintuen alustamista.
avoid.finalizer.method=Vältä Finalizer menetelmää.
constructors.declaration.grouping=Kaikki rakentajat tulee ryhmitellä yhteen. Edellinen rakentaja oli rivillä ''{0}''.
constructors.declaration.grouping=Kaikki rakentajat tulee ryhmitellä yhteen. Rakentajat erotettiin riviltä ''{0}''.
covariant.equals=covariant vastaa ilman painavaa tasavertaisten (java.lang.Object).
declaration.order.access=Muuttuja pääsy määritelmä väärässä järjestyksessä.
declaration.order.constructor=Rakentaja määritelmä väärässä järjestyksessä.
Expand Down
Expand Up @@ -3,7 +3,7 @@ assignment.inner.avoid=Évitez d''affecter une valeur à une variable au sein d'
avoid.clone.method=Évitez d''utiliser la méthode de clonage.
avoid.double.brace.init=Évitez l''initialisation à double accolade.
avoid.finalizer.method=Évitez d''utiliser la méthode de finalisation.
constructors.declaration.grouping=Tous les constructeurs doivent être regroupés. Le constructeur précédent se trouvait au numéro de ligne ''{0}''.
constructors.declaration.grouping=Tous les constructeurs doivent être regroupés. Les constructeurs ont été séparés à la ligne ''{0}''.
covariant.equals=Votre méthode equals compare uniquement les objets de votre classe. N''oubliez pas de surcharger la méthode equals(java.lang.Object).
declaration.order.access=La définition des variables n''est pas triée suivant leur portée.
declaration.order.constructor=La définition des constructeurs n''apparaît pas dans le bon ordre.
Expand Down
Expand Up @@ -3,7 +3,7 @@ assignment.inner.avoid=式内部での代入は避けるべきです。
avoid.clone.method=cloneメソッドを使用しないでください。
avoid.double.brace.init=二重中括弧を使った初期化は使用しないでください。
avoid.finalizer.method=ファイナライザメソッドを使用しないでください。
constructors.declaration.grouping=すべてのコンストラクターはグループ化する必要があります。前のコンストラクターは行番号 ''{0}'' にありました
constructors.declaration.grouping=すべてのコンストラクターはグループ化する必要があります。コンストラクターは行 ''{0}'' で分離されました
covariant.equals=equals(java.lang.Object) をオーバーライドせずに共変 な equals を定義しています。
declaration.order.access=変数アクセスの定義順序が間違っています。
declaration.order.constructor=コンストラクタの定義順序が間違っています。
Expand Down
Expand Up @@ -3,7 +3,7 @@ assignment.inner.avoid=Atribuições aninhadas devem ser evitadas.
avoid.clone.method=Evite o uso do método ''clone()''.
avoid.double.brace.init=Evite a inicialização entre chaves.
avoid.finalizer.method=Evite o uso do método ''finalize()''.
constructors.declaration.grouping=Todos os construtores devem ser agrupados. O construtor anterior estava no número de linha ''{0}''.
constructors.declaration.grouping=Todos os construtores devem ser agrupados. Os construtores foram separados na linha ''{0}''.
covariant.equals="equals" covariante sem sobrescrever ''equals(java.lang.Object)''.
declaration.order.access=Definição de acesso a variável em ordem errada.
declaration.order.constructor=Definição de construtor em ordem errada.
Expand Down
Expand Up @@ -3,7 +3,7 @@ assignment.inner.avoid=Внутренние присвоения следует
avoid.clone.method=Избегайте использования метода clone().
avoid.double.brace.init=Избегайте инициализации в двойных скобках.
avoid.finalizer.method=Избегайте использования метода finalize().
constructors.declaration.grouping=Все конструкторы должны быть сгруппированы вместе. Предыдущий конструктор находился на строке с номером ''{0}''.
constructors.declaration.grouping=Все конструкторы должны быть сгруппированы вместе. Конструкторы разделились на строке ''{0}''.
covariant.equals=Ковариантный equals() без переопределения equals(java.lang.Object).
declaration.order.access=Объявляйте переменные в порядке расширения доступа.
declaration.order.constructor=Определение конструктора в неправильном порядке.
Expand Down
Expand Up @@ -3,7 +3,7 @@ assignment.inner.avoid=Dahili atamalar kullanılmamalıdır.
avoid.clone.method=''clone'' metodu kullanılmamalıdır.
avoid.double.brace.init=Çift ayraç başlatmadan kaçının.
avoid.finalizer.method=''finalize'' metodu kullanılmamalıdır.
constructors.declaration.grouping=Tüm kurucular birlikte gruplandırılmalıdır. Önceki kurucu ''{0}'' satır numarasındaydı.
constructors.declaration.grouping=Tüm kurucular birlikte gruplandırılmalıdır. Yapıcılar ''{0}'' satırında ayrıldı.
covariant.equals=java.lang.Object sınıfının ''equals'' metodundan başka bir ''equals'' metodu tanımlanmış, java.lang.Object sınıfından gelen ''equals'' metodu da ezilmelidir (override).
declaration.order.access=Değişken, erişim seviyesine göre yanlış sırada tanımlanmış.
declaration.order.constructor=''constructor'' tanımı yanlış sırada yapılmış.
Expand Down
Expand Up @@ -3,7 +3,7 @@ assignment.inner.avoid=应避免在子表达式中赋值。
avoid.clone.method=避免重写 clone 方法。
avoid.double.brace.init=避免双括号初始化。
avoid.finalizer.method=避免重写finalize方法。
constructors.declaration.grouping=所有构造函数应该组合在一起。先前的构造函数位于行号''{0}''
constructors.declaration.grouping=所有构造函数应该组合在一起。构造函数在''{0}''行处分开。
covariant.equals=重写''equals()''方法时,必须确保重写了''equals(java.lang.Object)''方法。
declaration.order.access=属性访问器定义顺序错误。
declaration.order.constructor=构造器定义顺序错误。
Expand Down
Expand Up @@ -8,7 +8,9 @@
Checks that all constructors are grouped together.
If there is any code between the constructors
then this check will give an error.
Comments between constructors are ignored.
The check identifies and flags any constructors that are not grouped together.
The error message will specify the line number from where the constructors are separated.
Comments between constructors are allowed.
&lt;/p&gt;</description>
<message-keys>
<message-key key="constructors.declaration.grouping"/>
Expand Down
Expand Up @@ -22,17 +22,9 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static com.puppycrawl.tools.checkstyle.checks.coding.ConstructorsDeclarationGroupingCheck.MSG_KEY;

import java.io.File;
import java.util.Map;
import java.util.Optional;

import org.junit.jupiter.api.Test;

import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
import com.puppycrawl.tools.checkstyle.JavaParser;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;

public class ConstructorsDeclarationGroupingCheckTest extends AbstractModuleTestSupport {
@Override
Expand All @@ -43,12 +35,17 @@ protected String getPackageLocation() {
@Test
public void testDefault() throws Exception {
final String[] expected = {
"23:5: " + getCheckMessage(MSG_KEY, 19),
"27:5: " + getCheckMessage(MSG_KEY, 23),
"41:9: " + getCheckMessage(MSG_KEY, 37),
"50:13: " + getCheckMessage(MSG_KEY, 44),
"53:9: " + getCheckMessage(MSG_KEY, 41),
"56:5: " + getCheckMessage(MSG_KEY, 27),
"23:5: " + getCheckMessage(MSG_KEY, 21),
"27:5: " + getCheckMessage(MSG_KEY, 21),
"43:9: " + getCheckMessage(MSG_KEY, 39),
"52:13: " + getCheckMessage(MSG_KEY, 48),
"55:9: " + getCheckMessage(MSG_KEY, 39),
"58:5: " + getCheckMessage(MSG_KEY, 21),
"60:5: " + getCheckMessage(MSG_KEY, 21),
"78:7: " + getCheckMessage(MSG_KEY, 76),
"82:7: " + getCheckMessage(MSG_KEY, 76),
"84:7: " + getCheckMessage(MSG_KEY, 76),
"87:5: " + getCheckMessage(MSG_KEY, 21),
};
verifyWithInlineConfigParser(
getPath("InputConstructorsDeclarationGrouping.java"), expected);
Expand All @@ -58,8 +55,10 @@ public void testDefault() throws Exception {
public void testConstructorsDeclarationGroupingRecords() throws Exception {

final String[] expected = {
"18:9: " + getCheckMessage(MSG_KEY, 12),
"34:9: " + getCheckMessage(MSG_KEY, 30),
"20:9: " + getCheckMessage(MSG_KEY, 16),
"22:9: " + getCheckMessage(MSG_KEY, 16),
"26:9: " + getCheckMessage(MSG_KEY, 16),
"42:9: " + getCheckMessage(MSG_KEY, 38),
};

verifyWithInlineConfigParser(
Expand All @@ -82,32 +81,4 @@ public void testTokensNotNull() {
.isNotNull();
}

/**
* We cannot reproduce situation when visitToken is called and leaveToken is not.
* So, we have to use reflection to be sure that even in such situation
* state of the field will be cleared.
*
* @throws Exception when code tested throws exception
*/
@Test
@SuppressWarnings("unchecked")
public void testClearState() throws Exception {
final ConstructorsDeclarationGroupingCheck check =
new ConstructorsDeclarationGroupingCheck();
final Optional<DetailAST> ctorDef = TestUtil.findTokenInAstByPredicate(
JavaParser.parseFile(new File(getNonCompilablePath(
"InputConstructorsDeclarationGroupingRecords.java")),
JavaParser.Options.WITHOUT_COMMENTS),
ast -> ast.getType() == TokenTypes.CTOR_DEF);

assertWithMessage("Ast should contain CTOR_DEF")
.that(ctorDef.isPresent())
.isTrue();
assertWithMessage("State is not cleared on beginTree")
.that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, ctorDef.get(),
"allObjBlocks",
allObjBlocks -> ((Map<DetailAST, Integer>) allObjBlocks).isEmpty()))
.isTrue();
}

}
Expand Up @@ -15,9 +15,15 @@ public MyRecord1(int a) {

void foo() {}

void foo2() {}

public MyRecord1 {} // violation

public MyRecord1(int x, int y, int z) {
public MyRecord1(int a, int b, int c, int d) { // violation
this(a+b, c+d);
}

public MyRecord1(int x, int y, int z) { // violation
this(x+y, z);
}
}
Expand All @@ -31,6 +37,20 @@ class MyClass {

String[] str;

String[] str2;

MyClass(int a) {} // violation
}

public record MyRecord4(double d) {
public MyRecord4(double d) {
this(d);
}

public MyRecord4 {}

public MyRecord4(double a, double b) {
this(a+b);
}
}
}

0 comments on commit bc12147

Please sign in to comment.