Skip to content
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

Add support for java 14 language features #365

Merged
merged 30 commits into from
Nov 24, 2020
Merged

Add support for java 14 language features #365

merged 30 commits into from
Nov 24, 2020

Conversation

fawind
Copy link
Contributor

@fawind fawind commented Nov 16, 2020

After this PR

Closes #347

Cherry-picking relevant commits from google-java-format and adapting them for pjf.

Changes:

  • Use build in jdk javac instead of errorprone javac.
    • Because of this, the source and target compatibility is increased to 11.
    • That's also the reason for all the import changes.
  • Add initial support for switch-statements, var, yield, records, text blocks.

==COMMIT_MSG==
Add support for java 14 language features
==COMMIT_MSG==

Possible downsides?

Dropping jdk 8 support.

@fawind fawind requested a review from iamdanfox November 16, 2020 12:02
@changelog-app
Copy link

changelog-app bot commented Nov 16, 2020

Generate changelog in changelog/@unreleased

Type

  • Feature
  • Improvement
  • Fix
  • Break
  • Deprecation
  • Manual task
  • Migration

Description

  • Add support for java 14 language features.
  • Break: Increase minimum source compatibility from java 8 to java 11.
    • To use version 2.x of the formatter, you need to run on java 11 or higher.
    • If you require the formatter to run on java 8, you won't be able to upgrade past version 1.x. Note that you can still produce java 8 jars from your repo but need to use at least java 11 to run the formatter.

Check the box to generate changelog(s)

  • Generate changelog entry

]

if (JavaVersion.current() < JavaVersion.VERSION_14) {
excludes = ['**/Java14InputAstVisitor.java']
Copy link
Contributor

Choose a reason for hiding this comment

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

oooh nice, i was wondering how painful it was gonna be to do some kinda of dual compilation here, or if were going to have to do some multi-release jar thing!

One caveat with this approach is I think gradle has some new functionality where you can specify the JDK to use for these compile tasks independently from the JDK used to evaluate gradle, and JavaVersion.current() will grab the gradle evaluating one

case 0:
yield 0;
default:
yield n / i;
Copy link
Contributor

@iamdanfox iamdanfox Nov 16, 2020

Choose a reason for hiding this comment

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

woah this is a funky keyword... based on the reading of https://www.baeldung.com/java-thread-yield it sounds like not something that we expect palantir codebases to contain!

nvm Thread.yield and yield keyword are totally different... looks like this one is just a siwtch-expression specific return!

/**
* Extends {@link JavaInputAstVisitor} with support for AST nodes that were added or modified for Java 14.
*/
public class Java14InputAstVisitor extends JavaInputAstVisitor {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering what the longer-term implications of this pattern are - e.g. java 15 introduces a teeny bit more syntax right (specifically the sealed keyword I think)... would the idea be to have one of these classes for each of the java versions we support compiling with? and then at some point take a hard stance and increment our required min version, collapsing some of these classes into one?

export PRIMARY_BRANCH=develop
export ONLY_11=true
export UNIT_TEST_15=true
export UNIT_TEST_14=false
Copy link
Contributor

@iamdanfox iamdanfox Nov 16, 2020

Choose a reason for hiding this comment

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

I think for the final publish step, it's important the the java compiler is java 14 to ensure we don't hit the exclusion below, as we definitely want that class to appear in the final jar!

    if (JavaVersion.current() < JavaVersion.VERSION_14) {
        excludes = ['**/Java14InputAstVisitor.java']
    }

Maybe we have to diverge from the CircleCI template?

Or maybe JDK=14 / JDK=15 would be enough? Seems like we do want. the entirely independent full ./gradlew build pipelines on both java11, java14 and maybe other higher versions too? (i.e. effectively our unit-test-11/unit-test-15 thingies I think).

@@ -3396,7 +3412,10 @@ int declareOne(
if (modifiers.isPresent()) {
visitAndBreakModifiers(modifiers.get(), annotationsDirection, Optional.of(verticalAnnotationBreak));
}
builder.open(type != null ? plusFour : ZERO);
boolean isVar = builder.peekToken().get().equals("var")
Copy link
Contributor

@iamdanfox iamdanfox Nov 16, 2020

Choose a reason for hiding this comment

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

ok so this is the first block where we diverge from the google-java-format version? google/google-java-format@4ddb914#diff-0c133fd389bc6f207e146ba08dbc50dd78aa088ff31cdc56cb14ebb31e97ec49R158

EDIT nvm seems like we're up to date with their current impl, not their initial PR

https://github.com/google/google-java-format/blob/93d649d0fbc07fc604f7b47b3098cf6e1df521de/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java#L3287-L3291.

It's kinda interesting because they're using the peekToken and doing a string comparison to get away with supporting java13+ features here in this plain JavaInputAstVisitor, rather than making another verbose Java13InputAstVisitor.

Copy link
Contributor

Choose a reason for hiding this comment

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

I put a debugger on this, and it seems like the call stack of visitEnhancedForLoop -> visitToDeclare -> declareOne, we lost the node variable which could give us a first-class, typed way of detecting this rather than just relying on string comparisons.

I believe it would just be a matter of calling the JCVariableDecl#isImplicitlyTyped() method, and that would tell us we're looking at a var declaration. Not 100% sure if it's worth diverging from their implementation in order to plumb this down though, as it might make future maintenance more annoying, despite slightly cleaner code

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Happy to make the change but I would probably stick to the original impl if possible. That way we can cherry-pick new features and fixes instead of reimplementing them which will likely save us quite a bit of work down the road.

scan(node.getValue(), null);
token(";");
return null;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

return null;
}

public void visitRecordDeclaration(ClassTree node) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this is pretty hard to read, but it looks like at least it matches the current g-j-f impl, so at least we'll be able to stay in sync. https://github.com/google/google-java-format/blob/93d649d0fbc07fc604f7b47b3098cf6e1df521de/core/src/main/java/com/google/googlejavaformat/java/java14/Java14InputAstVisitor.java#L99-L160

}
}
switch (node.getCaseKind()) {
case STATEMENT:
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The pjf visitStatement method as a different signature to support inlining statements:

The code used here is consistent to how we handle case statements in the non-j14 visitor:

boolean isBlock =
node.getStatements().size() == 1 && node.getStatements().get(0).getKind() == BLOCK;
builder.open(isBlock ? ZERO : plusTwo);
if (isBlock) {
builder.space();
}
visitStatements(node.getStatements(), isBlock);

@iamdanfox
Copy link
Contributor

Thanks for burning through all the thorny gradle bits to make this actually work in our setup, I'm pretty psyched to actually use these new preview features :)

I think there's the publishing thing to fix (needs a jdk14 in the docker container to compile with), then I think you should probably cut a release candidate here, and just see if there are any additional changes needed in baseline at all for this to be useable. Probably worth making your own version of that devx/template-witchcraft#1414 PR, and seeing how many hacks are strictly necessary!

- save_cache:
key: 'trial-publish-gradle-cache-v2-{{ checksum "versions.props" }}-{{ checksum "build.gradle" }}'
paths: [ ~/.gradle/caches ]
- store_test_results: { path: ~/junit }
- store_artifacts: { path: ~/artifacts }

publish:
docker: [{ image: 'circleci/openjdk:8u222-stretch-node' }]
docker: [{ image: 'circleci/openjdk:15-jdk-buster-node' }]
Copy link
Contributor

Choose a reason for hiding this comment

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

nice, so we're publishing with java 15, but unit-test-11 should ensure that older codepaths still work :)

Copy link
Contributor

@iamdanfox iamdanfox left a comment

Choose a reason for hiding this comment

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

Looks great to me! Might be worth fleshing out the implications of the sourceCompat 11 thing a little bit more in the changelog though, kinda to explain that palantir-java-format 2.X can only run on java11+ now (which is LTS). It's OK run the formatter on java11, but still produce java8 jars from your repo, but if you want to run the formatter on java8 you won't be able to upgrade past 1.X

@fawind
Copy link
Contributor Author

fawind commented Nov 24, 2020

👍

@bulldozer-bot bulldozer-bot bot merged commit 828e3d2 into develop Nov 24, 2020
@bulldozer-bot bulldozer-bot bot deleted the fw/java14 branch November 24, 2020 13:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support Java 14 language features (and decouple from javac-shaded)
2 participants