New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for Java 9 project Jigsaw module-info.java files #800
Conversation
…ead. * Fix compile errors * Add module support to SourceRoot
Build fails because the CSM wants me to implement module info. Can @ftomassetti help a bit? |
Sure: to implement it or make CSM not complain?
Maybe you want to create a common ancestor for the ModuleInfo AST classes and put them in a set and make CSM skipping those |
No no, to implement them. I can probably do it myself if I get the time, but I'm lazy. The PPV is already implemented. |
You got your first PR :D I just sketch something immediately because in the week-end I will unable to do much coding :( |
draft of CSM for the Module Info nodes
Do you think we will have many conflicts with #654 ? |
@ftomassetti - about conflicts: there is one extra parameter to the CU constructor, a bunch of extra nodes, and I've changed the names of the classes that JavaCC generates. So that may cause a lot of conflicts, but they should be simple to fix (mostly name changes.) |
@ftomassetti - I've added a test to |
This works, but it isn't pretty. The problem is with the 10 additional keywords. They have to be specified in the list of keyword tokens. When you do so, all the following tokens will get their ID increased by 10 (because we just inserted 10 new ones.) Now we have a problem with the Concrete Syntax Model, which uses the token ID's from the normal grammar, and starts outputting the wrong tokens for the module info grammar. The quick fix is to define 10 nonsense keywords in the normal grammar. They are named "qqqqqqqqqqqqqqqqqqqqqqq...." I'll try to merge it back into one grammar, and redefine IDENTIFIER to match those ten keywords too... If that doesn't work either I'm out of options. |
# Conflicts: # javaparser-core/src/main/java/com/github/javaparser/ast/visitor/GenericListVisitorAdapter.java # javaparser-core/src/main/java/com/github/javaparser/ast/visitor/ModifierVisitor.java
@ftomassetti what the heck does |
It means that according to the difference it has calculated it is expecting to find a certain token and to keep it. It finds instead a token of a different type so it does not know what to do. Note that it is fine to find extra whitespace tokens or comments and they can be skip. |
@ftomassetti it seems to be an EOL issue again, since travis doesn't complain and it build on my linux machine. |
You mean the failures in Appveyor? Also, isn't surprising that coverage is down by almost 12%? |
Coverage is explained by having a second copy of the grammar, which causes a second set of generated code. |
…are actually just identifiers" trick right.
This was rather surprising: removed the second grammar, and made all the module-info keywords match as identifiers too. String Identifier():
{
String ret;
}
{
// Make sure the module info keywords don't interfere with normal Java parsing by matching them as normal identifiers.
(<MODULE> | <REQUIRES> | <TO> | <WITH> | <OPEN> | <OPENS> | <USES> | <EXPORTS> | <PROVIDES> | <TRANSITIVE> |
<IDENTIFIER>) { ret = token.image; }
{ return ret; }
} So this seems to work correctly! |
Great! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @matozoid looks good to me after a quick look. Put two comments inline, mainly I was wondering why there is not a new version of the grammar file for Java 9.
@@ -37,7 +37,8 @@ | |||
VOLATILE, | |||
SYNCHRONIZED, | |||
NATIVE, | |||
STRICTFP; | |||
STRICTFP, | |||
TRANSITIVE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it should be treated as a regular modifier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I put it there because JP matches all modifiers in every location where modifiers are allowed. The idea is to check if the correct modifiers are used by validating the AST, which is not implemented yet: #594 . Or did you mean something else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see.
I expected this to resemble javax.lang.model.element.Modifier
which doesn't contain TRANSITIVE
. I.e. the "module-only" modifiers are kept separately there. There are separate enums for the different module instruction modifiers, e.g. here for Requires
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, that page says "mandated" is a modifier. I didn't see that in the JLS...
@@ -25,11 +25,11 @@ options { | |||
JAVA_UNICODE_ESCAPE=true; | |||
COMMON_TOKEN_ACTION=true; | |||
JDK_VERSION = "1.8"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How comes that's art of java_1_8.jj? I'd have assumed you'd create a new file java_9.jj instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, looking at the changes in Java 9 it seems that it is a superset of 8, so we can keep a single grammar (much less maintenance work!) - but it should be renamed yes :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll not rename it right away because these comments might disappear...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good.
@gunnarmorling thanks for the feedback - I was hoping you could check this branch out and try it on what you are thinking of using it for (if anything.) |
I checked it out and parsed one test descriptor, all looking good. I noticed though that |
@gunnarmorling currently it is, since it is a separate production, but I'll look into that tomorrow. Thanks for testing! |
I see. Thanks a lot for working on this, @matozoid! |
tn = AnnotationTypeDeclaration(modifier) { types = add(types, tn); } | ||
| | ||
module = ModuleDeclaration(modifier) | ||
| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what I did to make CompilationUnit() match all possible content. So the grammar thinks it's okay to mix classes, multiple module declarations, enums and annotation declarations in the same file. Yet again, a validator could check if a module declaration is mixed with type declarations and flag a warning or error.
Okay, the main JavaParser.parse(...) methods should now parse module declarations, and the grammar file no longer contains the java version, which wasn't helping anyone anyway. |
I think this will do as a nice preview. If anything changes in the specs we can change it later. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, my only real concern is about the token type being IDENTIFIER when parsing identifiers that are keywords in the context of module declarations
new TreeStructureVisitorGenerator(sourceRoot).generate(); | ||
new ModifierVisitorGenerator(sourceRoot).generate(); | ||
|
||
new GetNodeListsGenerator(sourceRoot).generate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why they are divided in two blocks? Is this worth a comment to explain what the two groups of generators represent?
public CloneVisitorGenerator(JavaParser javaParser, SourceRoot sourceRoot) { | ||
super(javaParser, sourceRoot, "com.github.javaparser.ast.visitor", "CloneVisitor", "void", "A", true); | ||
public CloneVisitorGenerator(SourceRoot sourceRoot) { | ||
super(sourceRoot, "com.github.javaparser.ast.visitor", "CloneVisitor", "Visitable", "Object", true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I miss Kotlin named parameters :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True true, but if you really like those then the latest intellij can help you too :)
return result.getResult().get(); | ||
} | ||
throw new ParseProblemException(result.getProblems()); | ||
return result.getResult().orElseThrow(() -> new ParseProblemException(result.getProblems())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice
*/ | ||
default NormalAnnotationExpr addAnnotation(Class<? extends Annotation> clazz) { | ||
default N addAnnotation(Class<? extends Annotation> clazz) { | ||
((Node) this).tryAddImportToParentCompilationUnit(clazz); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this wise? Should the addImport be optional? What if the annotation is in the same package?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This functionality wasn't changed (what I did was correct the use of the "add" and "addAndGet" pattern) but it is a good idea: #803
public Visitable visit(ModuleUsesStmt n, A arg) { | ||
Type type = (Type) n.getType().accept(this, arg); | ||
Comment comment = n.getComment().map( s -> (Comment) s.accept(this, arg)).orElse(null); | ||
if (type == null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: even in generated code I would really love to see the then statement between braces
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Go submit a PR for your nitpicking :-P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I have a better answer. The code in the generator is a little more readable because it doesn't contain the braces :)
@@ -64,17 +65,17 @@ private static CsmElement memberAnnotations() { | |||
} | |||
|
|||
private static CsmElement annotations() { | |||
return list(ObservableProperty.ANNOTATIONS, none(), none(), newline()); | |||
return list(ObservableProperty.ANNOTATIONS, space(), none(), newline()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am surprised this change does not cause tests to fail
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was surprised it was working at all :D
} | ||
|
||
private static CsmElement typeParameters() { | ||
return list(ObservableProperty.TYPE_PARAMETERS, CsmElement.sequence(CsmElement.comma(), CsmElement.space()), CsmElement.token(ASTParserConstants.LT), | ||
CsmElement.sequence(CsmElement.token(ASTParserConstants.GT), CsmElement.space())); | ||
return list(ObservableProperty.TYPE_PARAMETERS, CsmElement.sequence(CsmElement.comma(), CsmElement.space()), CsmElement.token(GeneratedJavaParserConstants.LT), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could use static imports for the token constants
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a big fan of static imports, and I thought you were not using them here because there are constants coming in from several sources. PR please :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure whether you clicked that option or not when creating this PR, but since recently one can have other contributors push to the same PR. I.e. @ftomassetti could add this kind of changes right to this PR (it's a checkbox when submitting a PR). Found it quite useful myself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I missed it completely, but that's good to know!
} | ||
{ | ||
// Make sure the module info keywords don't interfere with normal Java parsing by matching them as normal identifiers. | ||
(<MODULE> | <REQUIRES> | <TO> | <WITH> | <OPEN> | <OPENS> | <USES> | <EXPORTS> | <PROVIDES> | <TRANSITIVE> | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in this case what would be the token type? If the topken type is not IDENTIFIER we could run into issues with the CSM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I was worried about too, but the tests kept running. Would you like to suggest a test for this?
Anyway, the fix would be to override the token type to IDENTIFIER right here, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ftomassetti - do you have an idea how to test this? It is the last thing before I pull this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would:
- parse something that has a name
- change it to a name corresponding to a module keyword
- print it using lexical preservation
Do the viceversa (starting from something having a name like "requires").
These tests would be very similar to the ones in the lexicalpreservation package
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ftomassetti I added the following tests and they work - am I doing this right?
@Test
public void printAModuleInfoSpecificKeywordUsedAsIdentifier1() {
considerCode("class module { }");
cu.getClassByName("module").get().setName("xyz");
assertEquals("class xyz { }", lpp.print(cu));
}
@Test
public void printAModuleInfoSpecificKeywordUsedAsIdentifier2() {
considerCode("class xyz { }");
cu.getClassByName("xyz").get().setName("module");
assertEquals("class module { }", lpp.print(cu));
}
+1 from me: the tests in LexicalPreservingPrinterTest.java are exactly what I meant |
Thank you! |
Awesome, thanks a lot for making it happen, @matozoid and @ftomassetti! Any idea when we can get it in a release? |
Yes, next Sunday. This project releases every Sunday unless something gets really in the way. |
I'm not sure what you want to do it with it, but remember that this is only partial Java 9 support :) Here's the rest: Java 9 support |
See #554.
This is a first draft. It seems to parse and print files quite well.
It has one big disadvantage: there is now a second grammar, and it contains a copy of the first. The differences are small, so that should be fixable with some trickery.
CSM or symbol solver support is not there yet.
I think the most interesting thing to look at is the unit test: https://github.com/javaparser/javaparser/pull/800/files#diff-32ad75ad8b271ad317da30653e5b9c83