Skip to content

[JEP 511] Module Import Declarations#4905

Closed
johannescoetzee wants to merge 7 commits into
javaparser:masterfrom
johannescoetzee:johannes/module-imports
Closed

[JEP 511] Module Import Declarations#4905
johannescoetzee wants to merge 7 commits into
javaparser:masterfrom
johannescoetzee:johannes/module-imports

Conversation

@johannescoetzee

Copy link
Copy Markdown
Collaborator

Fixes #4848

This PR adds parser support for module import statements, as well as support for resolving types from modules.

Changes to ImportDeclaration/CompilationUnit

Constructors and an addImport method with an option to specify whether this is a module import or not (indicated by the value of the new isModule field in ImportDeclaration) were added. The old versions of these constructors and this method without the isModule parameter were kept to avoid breaking user code.

JarTypeSolver

Javassist does not provide much support for handling modules, so I ended up parsing the module attribute manually. This is a bit cumbersome, but does work and I hope the comments explain what is happening sufficiently well.

ClassLoaderTypeSolver

I couldn't figure out a way to do this without using Java 9+ types and methods, so I used a lot of reflection for the implementation to maintain compatibility with Java 8 (although this will always fail if JavaParser is run with Java 8). I also couldn't figure out a good way to get to a list of modules loaded by a classloader (the mechanisms I could find that looked promising were all JDK-internal or blocked for security reasons). The solution as it is presented here requires users to provide a list of module layers that the typesolver will use to find modules. This is done automatically for the ReflectionTypeSolver.

@codecov

codecov Bot commented Nov 24, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 63.21244% with 71 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.394%. Comparing base (f3f0f33) to head (b228a3c).
⚠️ Report is 11 commits behind head on master.

Files with missing lines Patch % Lines
...ver/resolution/typesolvers/CombinedTypeSolver.java 29.166% 14 Missing and 3 partials ⚠️
.../resolution/typesolvers/ClassLoaderTypeSolver.java 59.459% 9 Missing and 6 partials ⚠️
...r/resolution/typesolvers/JavaParserTypeSolver.java 64.516% 6 Missing and 5 partials ⚠️
...olsolver/resolution/typesolvers/JarTypeSolver.java 81.818% 4 Missing and 4 partials ⚠️
...a/com/github/javaparser/resolution/TypeSolver.java 0.000% 5 Missing ⚠️
...a/com/github/javaparser/ast/ImportDeclaration.java 77.777% 4 Missing ⚠️
...ava/com/github/javaparser/ast/CompilationUnit.java 60.000% 1 Missing and 1 partial ⚠️
...om/github/javaparser/ast/visitor/CloneVisitor.java 0.000% 2 Missing ⚠️
...m/github/javaparser/ast/visitor/EqualsVisitor.java 0.000% 0 Missing and 1 partial ⚠️
...github/javaparser/ast/visitor/HashCodeVisitor.java 0.000% 0 Missing and 1 partial ⚠️
... and 5 more
Additional details and impacted files

Impacted file tree graph

@@               Coverage Diff               @@
##              master     #4905       +/-   ##
===============================================
+ Coverage     58.368%   58.394%   +0.026%     
- Complexity      2534      2555       +21     
===============================================
  Files            681       681               
  Lines          39302     39490      +188     
  Branches        7134      7163       +29     
===============================================
+ Hits           22940     23060      +120     
- Misses         13448     13493       +45     
- Partials        2914      2937       +23     
Flag Coverage Δ
AlsoSlowTests 58.394% <63.212%> (+0.026%) ⬆️
javaparser-core 58.394% <63.212%> (+0.026%) ⬆️
javaparser-symbol-solver 58.394% <63.212%> (+0.026%) ⬆️
jdk-10 57.962% <62.694%> (+0.028%) ⬆️
jdk-11 57.961% <62.694%> (+0.025%) ⬆️
jdk-12 57.959% <62.694%> (+0.022%) ⬆️
jdk-13 57.961% <62.694%> (+0.025%) ⬆️
jdk-14 58.194% <62.694%> (+0.021%) ⬆️
jdk-15 58.197% <62.694%> (+0.026%) ⬆️
jdk-16 58.172% <62.694%> (+0.024%) ⬆️
jdk-17 58.324% <62.694%> (+0.023%) ⬆️
jdk-18 58.324% <62.694%> (+0.023%) ⬆️
jdk-8 57.813% <33.160%> (-0.124%) ⬇️
jdk-9 57.960% <62.694%> (+0.028%) ⬆️
macos-latest 58.386% <63.212%> (+0.026%) ⬆️
ubuntu-latest 58.381% <63.212%> (+0.026%) ⬆️
windows-latest 58.376% <63.212%> (+0.026%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...ub/javaparser/ast/observer/ObservableProperty.java 87.027% <100.000%> (ø)
...vaparser/metamodel/ImportDeclarationMetaModel.java 100.000% <ø> (ø)
...thub/javaparser/metamodel/JavaParserMetaModel.java 99.708% <100.000%> (+0.001%) ⬆️
...r/resolution/typesolvers/ReflectionTypeSolver.java 100.000% <100.000%> (ø)
...m/github/javaparser/ast/visitor/EqualsVisitor.java 23.076% <0.000%> (-0.039%) ⬇️
...github/javaparser/ast/visitor/HashCodeVisitor.java 60.975% <0.000%> (-0.166%) ⬇️
...javaparser/ast/visitor/NoCommentEqualsVisitor.java 4.862% <0.000%> (-0.011%) ⬇️
...vaparser/ast/visitor/NoCommentHashCodeVisitor.java 78.400% <0.000%> (-0.315%) ⬇️
...vaparsermodel/contexts/CompilationUnitContext.java 82.323% <85.714%> (+0.124%) ⬆️
...olsolver/resolution/typesolvers/AarTypeSolver.java 77.777% <0.000%> (-4.576%) ⬇️
... and 9 more

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update abe40d5...b228a3c. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@johannescoetzee

Copy link
Copy Markdown
Collaborator Author

I realised while reviewing #4902 that I forgot the validator and printer support for this. I'm closing this for now and will re-open it once that's done.

continue;
}
try {
/* This code is equivalent to the snippet below, but is done with reflection to maintain compatibility

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you isolate this code in a class that could be called, for example, ModuleLayerHelper?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -68,17 +121,24 @@ protected boolean filterName(String name) {
@Override
public SymbolReference<ResolvedReferenceTypeDeclaration> tryToSolveTypeInModule(String qualifiedModuleName, String simpleTypeName) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really necessary to create a new method to handle this particular case?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new method may not be the only solution, but we do need to keep track of the fact that we want to solve the type in a module, as well as the name of the module. This is not information that is currently tracked with the regular solveType methods. Since we know exactly when solving a type in a module is needed (that only being when handling module imports), I think a separate method is a much cleaner approach than trying to modify the already complex solveType structure/API to record the information we need, while also greatly reducing the risk of introducing new bugs into existing resolution logic.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new method may not be the only solution, but we do need to keep track of the fact that we want to solve the type in a module, as well as the name of the module. This is not information that is currently tracked with the regular solveType methods. Since we know exactly when solving a type in a module is needed (that only being when handling module imports), I think a separate method is a much cleaner approach than trying to modify the already complex solveType structure/API to record the information we need, while also greatly reducing the risk of introducing new bugs into existing resolution logic.

I do understand the concerns raised. I was thinking of scanning all modules for the class declaration, but that wouldn't be very efficient.


HashSet<Object> moduleLayers = new HashSet<>();
try {
/* This code is equivalent to the snippet below, but is done with reflection to maintain compatibility with

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably to be declared in a ‘ModuleLayerHelper’ class.


return SymbolReference.unsolved();
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make the PRs easier to read, would it be possible to isolate the JP test cases in a single commit, and also isolate the code generated by JP, which is specific to printers, the features of the symbol resolver, and the tests which are specific to symbol resolvers?

}
}

private void registerModuleInfo() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code could be externalised into a helper class until the Javassist project offers this functionality.
Perhaps we could either report an issue or make a contribution to the project.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think creating an issue is a good idea just to get an idea of whether this is something they would be interested in. The implementation here is very specific to what we need, so a more general approach would require some thought and could lead to some questions about attribute handling in javassist in general, which may be beyond the scope of what they want to do. I'll collect my thoughts on the issue and ask them about it

* }
*/
Set<Object> modulesSet = (Set<Object>) moduleLayer.getClass().getMethod("modules").invoke(moduleLayer);
Set<Object> modulesSet = (Set<Object>)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To outsource.

}

@Test
void testJavaModuleImportFromSource() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EnabledForJreRange ?


@BeforeEach
void setup() throws IOException {
Path pathToJar = adaptPath("src/test/resources/modules/moduletest/com-github-javaparser-testmodule.jar");

@jlerbsc jlerbsc Nov 28, 2025

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you delete the directory "moduletest" from the path?

@jlerbsc

jlerbsc commented Nov 28, 2025

Copy link
Copy Markdown
Collaborator

Thank you for this PR. I have a few comments and I have noted that you have other points to consider.

@johannescoetzee

Copy link
Copy Markdown
Collaborator Author

I opened #4910 with the changes requested here. I could not reopen this PR since I force pushed changes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JEP 511 : Module Import Declarations

2 participants