Skip to content

Commit 3113445

Browse files
authored
SONARJAVA-6092 : add telemetry for java25 features (#5476)
1 parent 9a1f7c8 commit 3113445

13 files changed

Lines changed: 327 additions & 2 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This multi-module Maven project was added to test jew java-25 features reporting in the telemetry.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0"?>
2+
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>org.sonarsource.it.projects</groupId>
8+
<artifactId>parent-project</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<properties>
13+
<maven.compiler.release>25</maven.compiler.release>
14+
</properties>
15+
16+
<groupId>org.sonarsource.it.projects</groupId>
17+
<artifactId>module_a</artifactId>
18+
<version>1.0-SNAPSHOT</version>
19+
<name>module_a</name>
20+
<url>http://maven.apache.org</url>
21+
</project>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// import module
2+
import module java.base;
3+
// compact source file
4+
void main() {}
5+
6+
7+
class Parent {}
8+
class Child extends Parent{
9+
// flexible constructor body
10+
Child() {
11+
IO.println("Hello from Child constructor");
12+
super();
13+
}
14+
15+
// 2nd flexible constructor body
16+
Child(int i) {
17+
IO.println("Hello from Child constructor");
18+
IO.println("Hello from Child constructor");
19+
int j=0;
20+
this();
21+
}
22+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0"?>
2+
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>org.sonarsource.it.projects</groupId>
8+
<artifactId>parent-project</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<properties>
13+
<maven.compiler.release>25</maven.compiler.release>
14+
</properties>
15+
16+
<groupId>org.sonarsource.it.projects</groupId>
17+
<artifactId>module_b</artifactId>
18+
<version>1.0-SNAPSHOT</version>
19+
<name>module_a</name>
20+
<url>http://maven.apache.org</url>
21+
</project>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// 2nd-3rd module imports
2+
import module java.base;
3+
import module java.logging;
4+
5+
// 2nd compact source file
6+
void main(String[] args) {
7+
System.out.println("Hello from module B!");
8+
}
9+
10+
// doesn't counts as a 3rd compact source file
11+
void main() {
12+
}
13+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
2+
<modelVersion>4.0.0</modelVersion>
3+
<properties>
4+
<maven.compiler.release>25</maven.compiler.release>
5+
</properties>
6+
<groupId>org.sonarsource.it.projects</groupId>
7+
<artifactId>parent-project</artifactId>
8+
<packaging>pom</packaging>
9+
<version>1.0-SNAPSHOT</version>
10+
<name>parent-project</name>
11+
<url>http://maven.apache.org</url>
12+
<modules>
13+
<module>module_a</module>
14+
<module>module_b</module>
15+
</modules>
16+
</project>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* SonarQube Java
3+
* Copyright (C) 2013-2025 SonarSource Sàrl
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package com.sonar.it.java.suite;
18+
19+
import com.sonar.orchestrator.build.BuildResult;
20+
import com.sonar.orchestrator.build.MavenBuild;
21+
import com.sonar.orchestrator.junit4.OrchestratorRule;
22+
import org.junit.ClassRule;
23+
import org.junit.Test;
24+
import org.sonar.java.telemetry.TelemetryKey;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
28+
public class Java25FeaturesTelemetryTest {
29+
30+
@ClassRule
31+
public static OrchestratorRule orchestrator = JavaTestSuite.ORCHESTRATOR;
32+
33+
@Test
34+
public void test() {
35+
MavenBuild build = TestUtils.createMavenBuild()
36+
.setPom(TestUtils.projectPom("java-features"))
37+
.setCleanPackageSonarGoals()
38+
.setDebugLogs(true);
39+
40+
String projectKey = "org.sonarsource.it.projects:parent-project";
41+
TestUtils.provisionProject(orchestrator, projectKey, "java-features", "java", "multi-module");
42+
43+
BuildResult buildResult = orchestrator.executeBuild(build);
44+
45+
assertThat(buildResult.getLogs())
46+
.containsOnlyOnce("Telemetry %s: %d".formatted(TelemetryKey.JAVA_FEATURE_FLEXIBLE_CONSTRUCTOR_BODY.key(), 2))
47+
.containsOnlyOnce("Telemetry %s: %d".formatted(TelemetryKey.JAVA_FEATURE_MODULE_IMPORT.key(), 3))
48+
.containsOnlyOnce("Telemetry %s: %d".formatted(TelemetryKey.JAVA_FEATURE_COMPACT_SOURCE_FILES.key(), 2));
49+
}
50+
}

its/plugin/tests/src/test/java/com/sonar/it/java/suite/MultiModuleTelemetryTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,4 @@ public void test() {
4848
.containsOnlyOnce("Telemetry java.is_autoscan: false")
4949
.containsOnlyOnce("Telemetry java.dependency.lombok: 1.18.30,1.18.38");
5050
}
51-
5251
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.sonar.api.batch.fs.InputFile;
3434
import org.sonar.java.ast.JavaAstScanner;
3535
import org.sonar.java.ast.visitors.FileLinesVisitor;
36+
import org.sonar.java.ast.visitors.Java25FeaturesTelemetryVisitor;
3637
import org.sonar.java.ast.visitors.SyntaxHighlighterVisitor;
3738
import org.sonar.java.caching.CacheContextImpl;
3839
import org.sonar.java.classpath.DependencyVersionInference;
@@ -80,6 +81,7 @@ public JavaFrontend(JavaVersion javaVersion, SonarComponents sonarComponents, Me
8081
this.telemetry = telemetry;
8182
List<JavaCheck> commonVisitors = new ArrayList<>();
8283
commonVisitors.add(javaResourceLocator);
84+
commonVisitors.add(new Java25FeaturesTelemetryVisitor(telemetry));
8385
if (postAnalysisIssueFilter != null) {
8486
commonVisitors.add(postAnalysisIssueFilter);
8587
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* SonarQube Java
3+
* Copyright (C) 2012-2025 SonarSource Sàrl
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package org.sonar.java.ast.visitors;
18+
19+
import java.util.List;
20+
import org.sonar.java.model.ExpressionUtils;
21+
import org.sonar.java.telemetry.Telemetry;
22+
import org.sonar.java.telemetry.TelemetryKey;
23+
import org.sonar.plugins.java.api.JavaVersion;
24+
import org.sonar.plugins.java.api.JavaVersionAwareVisitor;
25+
import org.sonar.plugins.java.api.tree.ClassTree;
26+
import org.sonar.plugins.java.api.tree.ExpressionStatementTree;
27+
import org.sonar.plugins.java.api.tree.IdentifierTree;
28+
import org.sonar.plugins.java.api.tree.ImportTree;
29+
import org.sonar.plugins.java.api.tree.MethodInvocationTree;
30+
import org.sonar.plugins.java.api.tree.MethodTree;
31+
import org.sonar.plugins.java.api.tree.StatementTree;
32+
import org.sonar.plugins.java.api.tree.Tree;
33+
34+
public final class Java25FeaturesTelemetryVisitor extends SubscriptionVisitor implements JavaVersionAwareVisitor {
35+
private final Telemetry telemetry;
36+
37+
public Java25FeaturesTelemetryVisitor(Telemetry telemetry) {
38+
this.telemetry = telemetry;
39+
}
40+
41+
@Override
42+
public List<Tree.Kind> nodesToVisit() {
43+
return List.of(Tree.Kind.CONSTRUCTOR, Tree.Kind.IMPORT, Tree.Kind.IMPLICIT_CLASS);
44+
}
45+
46+
@Override
47+
public void visitNode(Tree tree) {
48+
if (tree instanceof ImportTree importTree && importTree.isModule()) {
49+
aggregate(TelemetryKey.JAVA_FEATURE_MODULE_IMPORT);
50+
} else if (tree instanceof ClassTree) {
51+
aggregate(TelemetryKey.JAVA_FEATURE_COMPACT_SOURCE_FILES);
52+
} else if (
53+
tree instanceof MethodTree methodTree
54+
&& methodTree.block().body().stream()
55+
// if the 1st statement of the constructor is a call to another constructor, then the flexible constructor body feature is not used.
56+
.skip(1)
57+
// if any other statement is a call to another constructor, then the flexible constructor body feature is used.
58+
.anyMatch(Java25FeaturesTelemetryVisitor::isSuperInvocation)
59+
) {
60+
aggregate(TelemetryKey.JAVA_FEATURE_FLEXIBLE_CONSTRUCTOR_BODY);
61+
}
62+
}
63+
64+
private void aggregate(TelemetryKey key) {
65+
// MODIFY HERE TO CHANGE AGGREGATION LOGIC (for example, to aggregate as a flag instead of a counter)
66+
telemetry.aggregateAsCounter(key, 1);
67+
}
68+
69+
private static boolean isSuperInvocation(StatementTree statement) {
70+
return statement instanceof ExpressionStatementTree expressionStatementTree
71+
&& expressionStatementTree.expression() instanceof MethodInvocationTree methodInvocationTree
72+
&& methodInvocationTree.methodSelect() instanceof IdentifierTree identifierTree
73+
&& ExpressionUtils.isThisOrSuper(identifierTree.name());
74+
}
75+
76+
@Override
77+
public boolean isCompatibleWithJavaVersion(JavaVersion version) {
78+
return version.isJava25Compatible();
79+
}
80+
}
81+

0 commit comments

Comments
 (0)