Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import java.util.function.IntUnaryOperator;

class BadLocalVariableName {

// Instance/class fields are not checked by this rule at all, regardless of underscores
int _instanceField;
static int _staticField;

BadLocalVariableName(
int godParam,
int BAD_CONSTRUCTOR_PARAM, // Noncompliant {{Rename this local variable to match the regular expression '^[a-z][a-zA-Z0-9]*$'.}}
// ^^^^^^^^^^^^^^^^^^^^^
int _goodConstructorParam // Noncompliant {{Rename this local variable to match the regular expression '^[a-z][a-zA-Z0-9]*$'.}}
// ^^^^^^^^^^^^^^^^^^^^^
) {}

void method(
int goodParam,
int BAD_FORMAL_PARAMETER, // Noncompliant {{Rename this local variable to match the regular expression '^[a-z][a-zA-Z0-9]*$'.}}
// ^^^^^^^^^^^^^^^^^^^^
int _goodParam // Noncompliant {{Rename this local variable to match the regular expression '^[a-z][a-zA-Z0-9]*$'.}}
// ^^^^^^^^^^
) {
int BAD; // Noncompliant
int good;
int _good; // Compliant, leading underscore stripped before check
int good_; // Compliant, trailing underscore stripped before check
int __myVar__; // Compliant, surrounding underscores stripped before check
int _BAD; // Noncompliant {{Rename this local variable to match the regular expression '^[a-z][a-zA-Z0-9]*$'.}}
// ^^^^
int BAD_; // Noncompliant {{Rename this local variable to match the regular expression '^[a-z][a-zA-Z0-9]*$'.}}
// ^^^^

for (int I = 0; I < 10; I++) {
int D; // Noncompliant
}

for (good = 0; good < 10; good++) {
}

try (Closeable BAD_RESOURCE = open()) { // Noncompliant
} catch (IOException BAD_EXCEPTION) { // Noncompliant
} catch (IllegalStateException E) { // compliant
} catch (Exception EX) { // Noncompliant
}
}

Object FIELD_SHOULD_NOT_BE_CHECKED = new Object(){
{
int BAD; // Noncompliant
}
};

void forEachMethod() {
int MY_CONSTANT_IS_NOT_A_CONSTANT = 21; // Noncompliant
final int MY_LOCAL_CONSTANT = 42; // Compliant
final String MY_OTHER_LOCAL_CONSTANT = "42"; // Compliant
final Integer MY_ALTERNATE_CONSTANT = Integer.valueOf(42); // Compliant
for (byte C : "".getBytes()) {
int D; // Noncompliant
}
}

void foo() {
IntUnaryOperator f1 = (int _) -> 0; // Compliant, unnamed variable
IntUnaryOperator f2 = _ -> 0; // Compliant, unnamed variable
}

// Method names are not checked by this rule
void _underscoreMethod() {
int good;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,28 @@ public void visitCatch(CatchTree tree) {
@Override
public void visitVariable(VariableTree tree) {
IdentifierTree simpleName = tree.simpleName();
if (!simpleName.isUnnamedVariable() && !pattern.matcher(simpleName.name()).matches() && !isLocalConstant(tree)) {
String name = simpleName.name();
Tree parent = tree.parent();
boolean isMethodParameter = parent != null && parent.is(Tree.Kind.METHOD, Tree.Kind.CONSTRUCTOR);
String nameToCheck = isMethodParameter ? name : stripLeadingTrailingUnderscores(name);
Comment thread
asya-vorobeva marked this conversation as resolved.
if (!simpleName.isUnnamedVariable() && !pattern.matcher(nameToCheck).matches() && !isLocalConstant(tree)) {
context.reportIssue(this, simpleName, "Rename this local variable to match the regular expression '" + format + "'.");
}
super.visitVariable(tree);
}

private static String stripLeadingTrailingUnderscores(String name) {
int start = 0;
int end = name.length();
while (start < end && name.charAt(start) == '_') {
start++;
}
while (end > start && name.charAt(end - 1) == '_') {
end--;
}
return name.substring(start, end);
}

private boolean isLocalConstant(VariableTree tree) {
return context.getSemanticModel() != null && isConstantType(tree.symbol().type()) && tree.symbol().isFinal();
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
import org.junit.jupiter.api.Test;
import org.sonar.java.checks.verifier.CheckVerifier;

import static org.sonar.java.checks.verifier.TestUtils.nonCompilingTestSourcesPath;

class BadLocalVariableNameCheckTest {

@Test
void test() {
CheckVerifier.newVerifier()
.onFile("src/test/files/checks/naming/BadLocalVariableNameNoncompliant.java")
.onFile(nonCompilingTestSourcesPath("checks/BadLocalVariableNameCheckSample.java"))
.withCheck(new BadLocalVariableNameCheck())
.verifyIssues();
}
Expand All @@ -34,7 +36,7 @@ void test2() {
BadLocalVariableNameCheck check = new BadLocalVariableNameCheck();
check.format = "^[a-zA-Z0-9_][a-zA-Z0-9_][a-zA-Z0-9_][a-zA-Z0-9_]*$";
CheckVerifier.newVerifier()
.onFile("src/test/files/checks/naming/BadLocalVariableName.java")
.onFile(nonCompilingTestSourcesPath("checks/BadLocalVariableNameCheckSample_Custom.java"))
.withCheck(check)
.verifyNoIssues();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ <h3>Exceptions</h3>
} catch (Exception e) { // Compliant
}
</pre>
<p>Local variables inside a method body may have one or more leading underscores, trailing underscores, or both. This convention signals that the
variable is temporary, unimportant, or intentionally unnamed. The name between the underscores must still match the configured format.</p>
<pre>
public class MyClass {
public void doSomething() {
int _result = compute(); // Compliant
int value__ = 0; // Compliant
int __tmp__ = swap(); // Compliant
}
}
</pre>
<p>Note that this exception applies only to local variables declared inside a method body. Method parameters must match the configured format without
leading or trailing underscores.</p>
<h2>How to fix it</h2>
<p>First, familiarize yourself with the particular naming convention of the project in question. Then, update the name to match the convention, as
well as all usages of the name. For many IDEs, you can use built-in renaming and refactoring features to update all usages at once.</p>
Expand All @@ -47,17 +60,21 @@ <h4>Noncompliant code example</h4>
<p>With the default regular expression <code>^[a-z][a-zA-Z0-9]*$</code>:</p>
<pre data-diff-id="1" data-diff-type="noncompliant">
public class MyClass {
public void doSomething(int myParam) {
int LOCAL; // Noncompliant
public void doSomething(int _myParam) { // Noncompliant - method parameters cannot have leading or trailing underscores
int LOCAL; // Noncompliant - local variable does not match the naming convention
int __BAD__; // Noncompliant - inner part "BAD" does not match the naming convention
// ...
}
}
</pre>
<h4>Compliant solution</h4>
<pre data-diff-id="1" data-diff-type="compliant">
public class MyClass {
public void doSomething(int my_param) {
int local;
public void doSomething(int myParam) { // Compliant
int local; // Compliant
int _local; // Compliant - leading underscore allowed for local variables
int result__; // Compliant - trailing underscores allowed for local variables
int __tmp__; // Compliant - leading and trailing underscores allowed for local variables
// ...
}
}
Expand Down
Loading