Skip to content

Commit f305228

Browse files
SONARJAVA-5978 Support Compact Source Files (#5397)
Co-authored-by: Dorian Burihabwa <75226315+dorian-burihabwa-sonarsource@users.noreply.github.com>
1 parent 213daaf commit f305228

19 files changed

Lines changed: 189 additions & 21 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
void main() {
2+
int a = 5;
3+
int x = 10; // Noncompliant {{Rename "x" which hides the field declared at line 11.}}
4+
// ^
5+
float b = 3.14f;
6+
System.out.println(a);
7+
System.out.println(x);
8+
System.out.println(b);
9+
}
10+
11+
int x = 20;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// SONARJAVA-6028: FPs ahead. Only the line with "Too much." should be noncompliant.
2+
3+
void main() { // Noncompliant
4+
System.out.println("Just right."); // Noncompliant
5+
if (true) {
6+
System.out.println("Too much."); // Noncompliant
7+
}
8+
}
9+
10+
class MyClass { // Noncompliant
11+
}

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public List<Tree.Kind> nodesToVisit() {
5959
Tree.Kind.INTERFACE,
6060
Tree.Kind.ANNOTATION_TYPE,
6161
Tree.Kind.RECORD,
62+
Tree.Kind.IMPLICIT_CLASS,
6263
Tree.Kind.VARIABLE,
6364
Tree.Kind.METHOD,
6465
Tree.Kind.CONSTRUCTOR,
@@ -130,7 +131,14 @@ private static boolean isInStaticInnerClass(VariableTree hiddenVariable, Variabl
130131
}
131132

132133
private static boolean isClassTree(Tree tree) {
133-
return tree.is(Tree.Kind.CLASS, Tree.Kind.ENUM, Tree.Kind.INTERFACE, Tree.Kind.ANNOTATION_TYPE, Tree.Kind.RECORD);
134+
return tree.is(
135+
Tree.Kind.CLASS,
136+
Tree.Kind.ENUM,
137+
Tree.Kind.INTERFACE,
138+
Tree.Kind.ANNOTATION_TYPE,
139+
Tree.Kind.RECORD,
140+
Tree.Kind.IMPLICIT_CLASS
141+
);
134142
}
135143

136144
@Override

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,13 @@ public void scanFile(JavaFileScannerContext context) {
7272

7373
@Override
7474
public void visitClass(ClassTree tree) {
75-
// Exclude anonymous classes
76-
boolean isAnonymous = tree.simpleName() == null;
77-
if (!isAnonymous) {
75+
// Exclude anonymous classes other than implicit classed of compact source files.
76+
boolean isExcluded = tree.simpleName() == null && !tree.is(Kind.IMPLICIT_CLASS);
77+
if (!isExcluded) {
7878
checkIndentation(Collections.singletonList(tree));
7979
}
8080
int previousLevel = expectedLevel;
81-
if (isAnonymous) {
81+
if (isExcluded) {
8282
excludeIssueAtLine = LineUtils.startLine(tree.openBraceToken());
8383
expectedLevel = Position.startOf(tree.closeBraceToken()).columnOffset();
8484
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,11 @@ void test_records() {
4040
.verifyIssues();
4141
}
4242

43+
@Test
44+
void test_compact_source() {
45+
CheckVerifier.newVerifier()
46+
.onFile(mainCodeSourcesPath("checks/HiddenFieldCheckCompactSample.java"))
47+
.withCheck(new HiddenFieldCheck())
48+
.verifyIssues();
49+
}
4350
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,12 @@ void tolerates_line_breaking_control_characters() {
5757
.withCheck(new IndentationCheck())
5858
.verifyNoIssues();
5959
}
60+
61+
@Test
62+
void compact_source_file() {
63+
CheckVerifier.newVerifier()
64+
.onFile(mainCodeSourcesPath("checks/IndentationCheck_compactSource.java"))
65+
.withCheck(new IndentationCheck())
66+
.verifyIssues();
67+
}
6068
}

java-frontend/src/main/java/org/sonar/java/JavaFilesCache.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ public void scanFile(JavaFileScannerContext context) {
5050
currentClassKey.clear();
5151
parent.clear();
5252
anonymousInnerClassCounter.clear();
53+
// 0 stored on the top will be used for implicit anonymous classes in compact source files.
54+
anonymousInnerClassCounter.push(0);
5355
scan(tree);
5456
}
5557

@@ -77,9 +79,10 @@ private String getClassKey(String className) {
7779
key = currentPackage + "/" + className;
7880
}
7981
if ("".equals(className) || (parent.peek() != null && parent.peek().is(Tree.Kind.METHOD))) {
80-
// inner class declared within method
82+
// inner class declared within method or implicitly declared class in a compact source file
8183
int count = anonymousInnerClassCounter.pop() + 1;
82-
key = currentClassKey.peek() + "$" + count + className;
84+
String prefix = currentClassKey.isEmpty() ? "" : currentClassKey.peek();
85+
key = prefix + "$" + count + className;
8386
anonymousInnerClassCounter.push(count);
8487
} else if (currentClassKey.peek() != null) {
8588
key = currentClassKey.peek() + "$" + className;

java-frontend/src/main/java/org/sonar/java/Measurer.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.sonar.java;
1818

1919
import java.io.Serializable;
20+
import java.util.ArrayList;
2021
import java.util.Arrays;
2122
import java.util.Deque;
2223
import java.util.LinkedList;
@@ -38,6 +39,15 @@
3839

3940
public class Measurer extends SubscriptionVisitor {
4041

42+
private static final Tree.Kind[] CLASS_KINDS = new Tree.Kind[]{
43+
Tree.Kind.CLASS,
44+
Tree.Kind.INTERFACE,
45+
Tree.Kind.ENUM,
46+
Tree.Kind.ANNOTATION_TYPE,
47+
Tree.Kind.RECORD,
48+
Tree.Kind.IMPLICIT_CLASS
49+
};
50+
4151
private final SensorContext sensorContext;
4252
private final NoSonarFilter noSonarFilter;
4353
private InputFile sonarFile;
@@ -61,9 +71,13 @@ public void scanFile(JavaFileScannerContext context) {
6171

6272
@Override
6373
public List<Tree.Kind> nodesToVisit() {
64-
return Arrays.asList(Tree.Kind.CLASS, Tree.Kind.INTERFACE, Tree.Kind.ENUM, Tree.Kind.ANNOTATION_TYPE, Tree.Kind.RECORD,
65-
Tree.Kind.NEW_CLASS, Tree.Kind.ENUM_CONSTANT,
66-
Tree.Kind.METHOD, Tree.Kind.CONSTRUCTOR);
74+
List<Tree.Kind> nodes = new ArrayList<>(Arrays.asList(CLASS_KINDS));
75+
nodes.addAll(Arrays.asList(
76+
Tree.Kind.NEW_CLASS,
77+
Tree.Kind.ENUM_CONSTANT,
78+
Tree.Kind.METHOD,
79+
Tree.Kind.CONSTRUCTOR));
80+
return nodes;
6781
}
6882

6983

@@ -120,7 +134,7 @@ public void leaveNode(Tree tree) {
120134
}
121135

122136
private static boolean isClassTree(Tree tree) {
123-
return tree.is(Tree.Kind.CLASS, Tree.Kind.INTERFACE, Tree.Kind.ENUM, Tree.Kind.ANNOTATION_TYPE, Tree.Kind.RECORD);
137+
return tree.is(CLASS_KINDS);
124138
}
125139

126140
private <T extends Serializable> void saveMetricOnFile(Metric<T> metric, T value) {

java-frontend/src/main/java/org/sonar/java/ast/visitors/PublicApiChecker.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ private PublicApiChecker() {
4343
Tree.Kind.INTERFACE,
4444
Tree.Kind.ENUM,
4545
Tree.Kind.ANNOTATION_TYPE,
46-
Tree.Kind.RECORD
46+
Tree.Kind.RECORD,
47+
Tree.Kind.IMPLICIT_CLASS
4748
};
4849

4950
private static final Tree.Kind[] METHOD_KINDS = {

java-frontend/src/main/java/org/sonar/java/model/JParser.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,27 @@ private ModuleDirectiveTree convertModuleDirective(ModuleDirective node) {
769769
}
770770
}
771771

772+
/** Converts implicitly declared unnamed class at the top level of a compact compilation unit. */
773+
private ClassTreeImpl convertUnnamedClassDeclaration(AbstractTypeDeclaration e) {
774+
List<Tree> members = new ArrayList<>();
775+
776+
for (Object o : e.bodyDeclarations()) {
777+
processBodyDeclaration((BodyDeclaration) o, members);
778+
}
779+
780+
ClassTreeImpl t = new ClassTreeImpl(Tree.Kind.IMPLICIT_CLASS, null, members, null);
781+
782+
t.typeBinding = e.resolveBinding();
783+
declaration(t.typeBinding, t);
784+
785+
return t;
786+
}
787+
772788
private ClassTreeImpl convertTypeDeclaration(AbstractTypeDeclaration e) {
789+
if (e.getNodeType() == ASTNode.UNNAMED_CLASS) {
790+
return convertUnnamedClassDeclaration(e);
791+
}
792+
773793
List<Tree> members = new ArrayList<>();
774794

775795
int leftBraceTokenIndex = findLeftBraceTokenIndex(e);
@@ -932,7 +952,7 @@ private static List<?> superInterfaceTypes(AbstractTypeDeclaration e) {
932952
return ((EnumDeclaration) e).superInterfaceTypes();
933953
case ASTNode.RECORD_DECLARATION:
934954
return ((RecordDeclaration) e).superInterfaceTypes();
935-
case ASTNode.ANNOTATION_TYPE_DECLARATION:
955+
case ASTNode.ANNOTATION_TYPE_DECLARATION, ASTNode.UNNAMED_CLASS:
936956
return Collections.emptyList();
937957
default:
938958
throw new IllegalStateException(ASTNode.nodeClassForType(e.getNodeType()).toString());
@@ -1021,7 +1041,8 @@ private void processBodyDeclaration(BodyDeclaration node, List<Tree> members) {
10211041
case ASTNode.ANNOTATION_TYPE_DECLARATION,
10221042
ASTNode.ENUM_DECLARATION,
10231043
ASTNode.RECORD_DECLARATION,
1024-
ASTNode.TYPE_DECLARATION:
1044+
ASTNode.TYPE_DECLARATION,
1045+
ASTNode.UNNAMED_CLASS:
10251046
lastTokenIndex = processTypeDeclaration((AbstractTypeDeclaration) node, members);
10261047
break;
10271048
case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION:

0 commit comments

Comments
 (0)