Skip to content

Commit 681d80c

Browse files
SONARJAVA-4093 don't raise issues in S3252 when class owning static field is not accessible from the current package (#4948)
1 parent 1b8e004 commit 681d80c

7 files changed

Lines changed: 100 additions & 12 deletions

File tree

java-checks-test-sources/default/src/main/files/non-compiling/checks/StaticMemberAccess.java renamed to java-checks-test-sources/default/src/main/files/non-compiling/checks/StaticMemberAccessCheckSample.java

File renamed without changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
public class ClassInDefaultPackage {
2+
3+
static class A {
4+
public static final int CONSTANT = 42;
5+
6+
}
7+
8+
static class B extends A {
9+
10+
public static void foo() {
11+
int x = B.CONSTANT; // Noncompliant
12+
}
13+
}
14+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package checks.S3252_StaticMemberAccessCheckSample;
2+
3+
import checks.S3252_StaticMemberAccessCheckSample.hide_non_public.StaticMemberAccessCheckSampleHelper;
4+
5+
public class ImportFromOtherPackage {
6+
public void foo(){
7+
int x = StaticMemberAccessCheckSampleHelper.B.CONSTANT; // Compliant A is not accessible so we should not raise an issue
8+
int y = StaticMemberAccessCheckSampleHelper.Bar.CONSTANT; // Noncompliant
9+
}
10+
}

java-checks-test-sources/default/src/main/java/checks/StaticMemberAccess.java renamed to java-checks-test-sources/default/src/main/java/checks/S3252_StaticMemberAccessCheckSample/StaticMemberAccessCheckSample.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package checks;
1+
package checks.S3252_StaticMemberAccessCheckSample;
22

33
import com.google.common.collect.ImmutableSet;
44
import java.util.Set;
@@ -14,7 +14,7 @@ static void foo() {
1414

1515
class StaticMemberAccessChild extends StaticMemberAccessParent {
1616
public StaticMemberAccessChild() {
17-
StaticMemberAccessChild.counter++; // Noncompliant {{Use static access with "checks.StaticMemberAccessParent" for "counter".}}
17+
StaticMemberAccessChild.counter++; // Noncompliant {{Use static access with "checks.S3252_StaticMemberAccessCheckSample.StaticMemberAccessParent" for "counter".}}
1818
StaticMemberAccessParent.counter++; // Compliant
1919

2020
StaticMemberAccessChild.foo(); // Noncompliant
@@ -47,5 +47,4 @@ class GuavaFP {
4747
"sun.rmi.transport.misc",
4848
"sun.rmi.server.call",
4949
"sun.rmi.dgc");
50-
5150
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package checks.S3252_StaticMemberAccessCheckSample.hide_non_public;
2+
3+
public class StaticMemberAccessCheckSampleHelper {
4+
5+
static class A {
6+
public static final int CONSTANT = 42;
7+
}
8+
9+
static public class B extends A {
10+
}
11+
12+
static public class Foo {
13+
public static final int CONSTANT = 42;
14+
}
15+
16+
static public class Bar extends Foo {
17+
}
18+
19+
20+
}

java-checks/src/main/java/org/sonar/java/checks/StaticMemberAccessCheck.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,21 @@
1616
*/
1717
package org.sonar.java.checks;
1818

19-
import java.util.Collections;
2019
import java.util.List;
2120
import org.sonar.check.Rule;
2221
import org.sonar.java.checks.helpers.QuickFixHelper;
22+
import org.sonar.java.model.PackageUtils;
2323
import org.sonar.java.reporting.JavaQuickFix;
2424
import org.sonar.java.reporting.JavaTextEdit;
2525
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
2626
import org.sonar.plugins.java.api.semantic.MethodMatchers;
2727
import org.sonar.plugins.java.api.semantic.Symbol;
2828
import org.sonar.plugins.java.api.semantic.Type;
29+
import org.sonar.plugins.java.api.tree.CompilationUnitTree;
2930
import org.sonar.plugins.java.api.tree.ExpressionTree;
3031
import org.sonar.plugins.java.api.tree.MemberSelectExpressionTree;
3132
import org.sonar.plugins.java.api.tree.MethodInvocationTree;
33+
import org.sonar.plugins.java.api.tree.PackageDeclarationTree;
3234
import org.sonar.plugins.java.api.tree.Tree;
3335

3436
@Rule(key = "S3252")
@@ -40,22 +42,36 @@ public class StaticMemberAccessCheck extends IssuableSubscriptionVisitor {
4042
.withAnyParameters()
4143
.build();
4244

45+
private String currentPackage = "";
46+
4347
@Override
4448
public List<Tree.Kind> nodesToVisit() {
45-
return Collections.singletonList(Tree.Kind.MEMBER_SELECT);
49+
return List.of(Tree.Kind.MEMBER_SELECT, Tree.Kind.COMPILATION_UNIT);
4650
}
4751

4852

4953
@Override
5054
public void visitNode(Tree tree) {
55+
if (tree instanceof CompilationUnitTree file) {
56+
PackageDeclarationTree packageDecl = file.packageDeclaration();
57+
currentPackage = packageDecl == null ? "" : PackageUtils.packageName(packageDecl, ".");
58+
return;
59+
}
60+
5161
MemberSelectExpressionTree mse = (MemberSelectExpressionTree) tree;
5262
Symbol symbol = mse.identifier().symbol();
53-
if (symbol.isStatic() && !isListOrSetOf(mse)) {
63+
if (symbol.isStatic() && !isListOrSetOf(mse)) {
5464
ExpressionTree expression = mse.expression();
55-
Type staticType = symbol.owner().type();
65+
66+
Symbol owner = symbol.owner();
67+
Type staticType = owner.type();
5668
Type expressionType = expression.symbolType();
69+
boolean memberOwnerIsInCurrentPackage = currentPackage.equals(classPackage(expressionType));
70+
5771
if (!staticType.isUnknown() && !expressionType.isUnknown()
58-
&& !expressionType.erasure().equals(staticType.erasure())) {
72+
&& !expressionType.erasure().equals(staticType.erasure())
73+
&& (memberOwnerIsInCurrentPackage || owner.isPublic())
74+
) {
5975
QuickFixHelper.newIssue(context)
6076
.forRule(this)
6177
.onTree(mse.identifier())
@@ -66,6 +82,11 @@ public void visitNode(Tree tree) {
6682
}
6783
}
6884

85+
private static String classPackage(Type classType) {
86+
int endPackage = classType.fullyQualifiedName().lastIndexOf('.');
87+
return endPackage == -1 ? "" : classType.fullyQualifiedName().substring(0, endPackage);
88+
}
89+
6990
private static boolean isListOrSetOf(MemberSelectExpressionTree mse) {
7091
// this is necessary because we incorrectly resolve to Set#of List#of methods on JDK11
7192
// see SONARJAVA-3095

java-checks/src/test/java/org/sonar/java/checks/StaticMemberAccessCheckTest.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,37 @@
2323
import static org.sonar.java.checks.verifier.TestUtils.nonCompilingTestSourcesPath;
2424

2525
class StaticMemberAccessCheckTest {
26-
27-
private static final String FILE_NAME = "checks/StaticMemberAccess.java";
26+
private static final String COMPILING_SAMPLE_FOLDER = "checks/S3252_StaticMemberAccessCheckSample/";
2827

2928
@Test
3029
void test() {
3130
CheckVerifier.newVerifier()
32-
.onFile(mainCodeSourcesPath(FILE_NAME))
31+
.onFile(mainCodeSourcesPath(COMPILING_SAMPLE_FOLDER + "StaticMemberAccessCheckSample.java"))
32+
.withCheck(new StaticMemberAccessCheck())
33+
.verifyIssues();
34+
}
35+
36+
@Test
37+
void test_import_from_other_package() {
38+
CheckVerifier.newVerifier()
39+
.onFile(mainCodeSourcesPath(COMPILING_SAMPLE_FOLDER + "ImportFromOtherPackage.java"))
40+
.withCheck(new StaticMemberAccessCheck())
41+
.verifyIssues();
42+
}
43+
44+
@Test
45+
void test_import_from_other_package_without_semantic() {
46+
CheckVerifier.newVerifier()
47+
.onFile(mainCodeSourcesPath(COMPILING_SAMPLE_FOLDER + "ImportFromOtherPackage.java"))
48+
.withoutSemantic()
49+
.withCheck(new StaticMemberAccessCheck())
50+
.verifyNoIssues();
51+
}
52+
53+
@Test
54+
void test_when_class_is_in_default_package() {
55+
CheckVerifier.newVerifier()
56+
.onFile(mainCodeSourcesPath(COMPILING_SAMPLE_FOLDER + "ClassInDefaultPackage.java"))
3357
.withCheck(new StaticMemberAccessCheck())
3458
.verifyIssues();
3559
}
@@ -45,7 +69,7 @@ void quick_fixes() {
4569
@Test
4670
void test_non_compiling() {
4771
CheckVerifier.newVerifier()
48-
.onFile(nonCompilingTestSourcesPath(FILE_NAME))
72+
.onFile(nonCompilingTestSourcesPath("checks/StaticMemberAccessCheckSample.java"))
4973
.withCheck(new StaticMemberAccessCheck())
5074
.verifyIssues();
5175
}

0 commit comments

Comments
 (0)