@@ -338,9 +338,15 @@ public ImportNode visitImportDeclaration(final ImportDeclarationContext ctx) {
338338 List <AnnotationNode > annotations = this .visitAnnotationsOpt (ctx .annotationsOpt ());
339339
340340 boolean hasStatic = asBoolean (ctx .STATIC ());
341+ boolean hasModule = asBoolean (ctx .MODULE ());
341342 boolean hasStar = asBoolean (ctx .MUL ());
342343 boolean hasAlias = asBoolean (ctx .alias );
343344
345+ if (hasModule ) {
346+ String moduleName = this .visitQualifiedName (ctx .qualifiedName ());
347+ return expandModuleImport (moduleName , annotations , ctx );
348+ }
349+
344350 ImportNode importNode ;
345351
346352 if (hasStatic ) {
@@ -387,6 +393,50 @@ public ImportNode visitImportDeclaration(final ImportDeclarationContext ctx) {
387393 return configureAST (importNode , ctx );
388394 }
389395
396+ /**
397+ * Expands {@code import module java.base} into star imports for all
398+ * packages exported (unqualified) by the named module.
399+ * Packages already covered by Groovy's default imports or existing
400+ * star imports are skipped to avoid redundant resolution work.
401+ * <p>
402+ * Known differences from Java's module import behavior:
403+ * <ul>
404+ * <li>Ambiguous class names from multiple module imports silently resolve
405+ * to the last match, consistent with Groovy's existing star import
406+ * semantics. Java reports a compile-time error for such ambiguities.</li>
407+ * <li>Explicit single-type imports take priority over module-expanded
408+ * star imports (same as Java).</li>
409+ * </ul>
410+ */
411+ private ImportNode expandModuleImport (final String moduleName , final List <AnnotationNode > annotations , final ImportDeclarationContext ctx ) {
412+ java .lang .module .ModuleFinder finder = java .lang .module .ModuleFinder .ofSystem ();
413+ java .lang .module .ModuleReference moduleRef = finder .find (moduleName ).orElse (null );
414+ if (moduleRef == null ) {
415+ throw createParsingFailedException ("Unknown module: " + moduleName , ctx );
416+ }
417+ java .lang .module .ModuleDescriptor descriptor = moduleRef .descriptor ();
418+ java .util .Set <String > skip = new java .util .HashSet <>(java .util .Arrays .asList (
419+ org .codehaus .groovy .control .ResolveVisitor .DEFAULT_IMPORTS ));
420+ moduleNode .getStarImports ().stream ().map (ImportNode ::getPackageName ).forEach (skip ::add );
421+ ImportNode lastImport = null ;
422+ for (java .lang .module .ModuleDescriptor .Exports export : descriptor .exports ()) {
423+ if (!export .isQualified ()) {
424+ String packageName = export .source () + DOT_STR ;
425+ if (!skip .contains (packageName )) {
426+ moduleNode .addStarImport (packageName , annotations );
427+ lastImport = last (moduleNode .getStarImports ());
428+ skip .add (packageName );
429+ }
430+ }
431+ }
432+ if (lastImport == null ) {
433+ // All exported packages were already imported — add a no-op marker so AST is valid
434+ moduleNode .addStarImport (org .codehaus .groovy .control .ResolveVisitor .DEFAULT_IMPORTS [0 ], annotations );
435+ lastImport = last (moduleNode .getStarImports ());
436+ }
437+ return configureAST (lastImport , ctx );
438+ }
439+
390440 private static AnnotationNode makeAnnotationNode (final Class <? extends Annotation > type ) {
391441 AnnotationNode node = new AnnotationNode (ClassHelper .make (type ));
392442 // TODO: source offsets
0 commit comments