Skip to content

Commit 213daaf

Browse files
tomasz-tylenda-sonarsourcedorian-burihabwa-sonarsourcerombirli
authored
SONARJAVA-5984 Support Module Import Declarations (#5403)
Co-authored-by: Dorian Burihabwa <75226315+dorian-burihabwa-sonarsource@users.noreply.github.com> Co-authored-by: rombirli <56340680+rombirli@users.noreply.github.com>
1 parent fd6c76d commit 213daaf

5 files changed

Lines changed: 87 additions & 9 deletions

File tree

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -575,20 +575,32 @@ private JavaTree.CompilationUnitTreeImpl convertCompilationUnit(CompilationUnit
575575
for (int i = 0; i < e.imports().size(); i++) {
576576
ImportDeclaration e2 = (ImportDeclaration) e.imports().get(i);
577577
ExpressionTree name = convertImportName(e2.getName());
578-
if (e2.isOnDemand()) {
579-
name = new MemberSelectExpressionTreeImpl(
580-
name,
581-
lastTokenIn(e2, TerminalToken.TokenNameDOT),
582-
new IdentifierTreeImpl(lastTokenIn(e2, TerminalToken.TokenNameMULTIPLY))
583-
);
578+
579+
boolean isModuleImport = org.eclipse.jdt.core.dom.Modifier.isModule(e2.getModifiers());
580+
581+
// "on demand" means `import pkg.*;`
582+
if (e2.isOnDemand() && !isModuleImport) {
583+
InternalSyntaxToken dotToken = lastTokenIn(e2, TerminalToken.TokenNameDOT);
584+
InternalSyntaxToken identifierToken = lastTokenIn(e2, TerminalToken.TokenNameMULTIPLY);
585+
name = new MemberSelectExpressionTreeImpl(name, dotToken, new IdentifierTreeImpl(identifierToken));
584586
}
587+
588+
InternalSyntaxToken staticKeyword = e2.isStatic() ? firstTokenIn(e2, TerminalToken.TokenNamestatic) : null;
589+
InternalSyntaxToken moduleKeyword = isModuleImport ? firstTokenIn(e2, TerminalToken.TokenNamemodule) : null;
590+
585591
JavaTree.ImportTreeImpl t = new JavaTree.ImportTreeImpl(
586592
firstTokenIn(e2, TerminalToken.TokenNameimport),
587-
e2.isStatic() ? firstTokenIn(e2, TerminalToken.TokenNamestatic) : null,
593+
staticKeyword,
594+
moduleKeyword,
588595
name,
589596
lastTokenIn(e2, TerminalToken.TokenNameSEMICOLON)
590597
);
591-
t.binding = e2.resolveBinding();
598+
599+
if (!isModuleImport) {
600+
// There is no method to resolve bindings for a module import.
601+
t.binding = e2.resolveBinding();
602+
}
603+
592604
imports.add(t);
593605

594606
int tokenIndex = tokenManager.lastIndexIn(e2, TerminalToken.TokenNameSEMICOLON);

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,20 +317,27 @@ public static String packageNameAsString(@Nullable PackageDeclarationTree tree)
317317

318318
public static class ImportTreeImpl extends JavaTree implements ImportTree {
319319
private final boolean isStatic;
320+
private final boolean isModule;
320321
private final Tree qualifiedIdentifier;
321322
private final SyntaxToken semicolonToken;
322323
private final SyntaxToken importToken;
324+
@Nullable
323325
private final SyntaxToken staticToken;
326+
@Nullable
327+
private final SyntaxToken moduleToken;
324328

325329
public IBinding binding;
326330

327331
public ImportTreeImpl(InternalSyntaxToken importToken, @Nullable InternalSyntaxToken staticToken,
332+
@Nullable InternalSyntaxToken moduleToken,
328333
Tree qualifiedIdentifier, InternalSyntaxToken semiColonToken) {
329334
this.importToken = importToken;
330335
this.staticToken = staticToken;
336+
this.moduleToken = moduleToken;
331337
this.qualifiedIdentifier = qualifiedIdentifier;
332338
this.semicolonToken = semiColonToken;
333339
isStatic = staticToken != null;
340+
isModule = moduleToken != null;
334341
}
335342

336343
@Nullable
@@ -358,6 +365,11 @@ public boolean isStatic() {
358365
return isStatic;
359366
}
360367

368+
@Override
369+
public boolean isModule() {
370+
return isModule;
371+
}
372+
361373
@Override
362374
public SyntaxToken importKeyword() {
363375
return importToken;
@@ -369,6 +381,12 @@ public SyntaxToken staticKeyword() {
369381
return staticToken;
370382
}
371383

384+
@Nullable
385+
@Override
386+
public SyntaxToken moduleKeyword() {
387+
return moduleToken;
388+
}
389+
372390
@Override
373391
public Tree qualifiedIdentifier() {
374392
return qualifiedIdentifier;
@@ -389,6 +407,7 @@ public List<Tree> children() {
389407
return ListUtils.concat(
390408
Collections.singletonList(importToken),
391409
isStatic ? Collections.singletonList(staticToken) : Collections.<Tree>emptyList(),
410+
isModule ? Collections.singletonList(moduleToken) : Collections.<Tree>emptyList(),
392411
Arrays.asList(qualifiedIdentifier, semicolonToken));
393412
}
394413
}

java-frontend/src/main/java/org/sonar/plugins/java/api/tree/ImportTree.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,22 @@ public interface ImportTree extends ImportClauseTree {
4141
*/
4242
boolean isStatic();
4343

44+
/**
45+
* @since Java 25
46+
*/
47+
boolean isModule();
48+
4449
SyntaxToken importKeyword();
4550

4651
@Nullable
4752
SyntaxToken staticKeyword();
4853

54+
@Nullable
55+
/**
56+
* @since Java 25
57+
*/
58+
SyntaxToken moduleKeyword();
59+
4960
Tree qualifiedIdentifier();
5061

5162
SyntaxToken semicolonToken();

java-frontend/src/test/java/org/sonar/java/model/JParserSemanticTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.sonar.java.model.expression.MethodInvocationTreeImpl;
4848
import org.sonar.java.model.expression.MethodReferenceTreeImpl;
4949
import org.sonar.java.model.expression.NewClassTreeImpl;
50+
import org.sonar.java.model.location.InternalRange;
5051
import org.sonar.java.model.statement.BlockTreeImpl;
5152
import org.sonar.java.model.statement.BreakStatementTreeImpl;
5253
import org.sonar.java.model.statement.ContinueStatementTreeImpl;
@@ -58,6 +59,7 @@
5859
import org.sonar.java.model.statement.SwitchExpressionTreeImpl;
5960
import org.sonar.java.model.statement.YieldStatementTreeImpl;
6061
import org.sonar.plugins.java.api.JavaVersion;
62+
import org.sonar.plugins.java.api.location.Position;
6163
import org.sonar.plugins.java.api.semantic.Symbol;
6264
import org.sonar.plugins.java.api.semantic.Symbol.MethodSymbol;
6365
import org.sonar.plugins.java.api.semantic.SymbolMetadata;
@@ -2066,4 +2068,38 @@ void f2() {
20662068
assertNotEquals(pOfB.symbol(), pOfC.symbol());
20672069

20682070
}
2071+
2072+
@Test
2073+
void testImportModule() {
2074+
String source = """
2075+
package com.example;
2076+
2077+
import module java.base;
2078+
2079+
import static java.sql.Connection.TRANSACTION_NONE;
2080+
2081+
public class Source {
2082+
List<Integer> xs = new ArrayList<>();
2083+
Set<Integer> ys = new HashSet<>();
2084+
}
2085+
""";
2086+
2087+
JavaTree.CompilationUnitTreeImpl cu = test(source);
2088+
2089+
JavaTree.ImportTreeImpl importModule = (JavaTree.ImportTreeImpl) cu.imports().get(0);
2090+
2091+
assertThat(importModule.isModule()).isTrue();
2092+
assertThat(importModule.isStatic()).isFalse();
2093+
assertThat(importModule.symbol()).isNull();
2094+
2095+
MemberSelectExpressionTree javaBase = (MemberSelectExpressionTree) importModule.qualifiedIdentifier();
2096+
assertThat(javaBase.identifier().name()).isEqualTo("base");
2097+
2098+
assertThat(importModule.moduleKeyword().range()).isEqualTo(
2099+
new InternalRange(Position.at(3, 8), Position.at(3, 14))
2100+
);
2101+
2102+
JavaTree.ImportTreeImpl importConst = (JavaTree.ImportTreeImpl) cu.imports().get(1);
2103+
assertThat(importConst.isModule()).isFalse();
2104+
}
20692105
}

java-frontend/src/test/java/org/sonar/java/model/JWarningTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ void test_matchesTreeExactly() {
201201
private static ImportTree importTree(int startLine, int startColumn, int endLine, int endColumn) {
202202
InternalSyntaxToken fakeStartToken = syntaxToken(startLine, startColumn, " ");
203203
InternalSyntaxToken fakeEndToken = syntaxToken(endLine, endColumn, " ");
204-
return new JavaTree.ImportTreeImpl(fakeStartToken, null, null, fakeEndToken);
204+
return new JavaTree.ImportTreeImpl(fakeStartToken, null, null, null, fakeEndToken);
205205
}
206206

207207
private static VariableTree variableTree(int startLine, int startColumn, int endLine, int endColumn) {

0 commit comments

Comments
 (0)