Skip to content

Conversation

@amaembo
Copy link
Contributor

@amaembo amaembo commented Mar 26, 2021

This change is powered by IntelliJ IDEA quick-fix.
I also took the liberty to fix C-style arrays in changed files.

In SourceCodeAnalysisImpl.java:501, I left case PARAMETERIZED_TYPE -> FALSE as a separate switch case, because it had a comment. Better formatting ideas are welcome.

If you like this, I can provide similar PR in other components as well (e.g. in javac)


Progress

  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue
  • Change must be properly reviewed

Issue

  • JDK-8264222: Use switch expression in jshell where possible

Reviewers

Download

To checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/3210/head:pull/3210
$ git checkout pull/3210

To update a local copy of the PR:
$ git checkout pull/3210
$ git pull https://git.openjdk.java.net/jdk pull/3210/head

@bridgekeeper
Copy link

bridgekeeper bot commented Mar 26, 2021

👋 Welcome back tvaleev! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk openjdk bot added the rfr Pull request is ready for review label Mar 26, 2021
@openjdk
Copy link

openjdk bot commented Mar 26, 2021

@amaembo The following label will be automatically applied to this pull request:

  • kulla

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command.

@mlbridge
Copy link

mlbridge bot commented Mar 26, 2021

Webrevs

Copy link
Contributor

@briangoetz briangoetz left a comment

Choose a reason for hiding this comment

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

Cute trick for the partial update to c.
The comment in the last hunk (resultTypeOf()) only applied to the INSTANCE and STATIC init cases, but now it applies to CONSTRUCTOR too (in a previous hunk, you deliberately had a redundant case, to preserve this information.) I would update the comment.

Otherwise, all looks good to me, +1.

@openjdk
Copy link

openjdk bot commented Mar 26, 2021

@amaembo This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8264222: Use switch expression in jshell where possible

Reviewed-by: briangoetz

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 20 new commits pushed to the master branch:

  • a209ed0: 8263670: pmap and pstack in jhsdb do not work on debug server
  • 38e0a58: 8264273: macOS: zero VM is broken due to no member named 'is_cpu_emulated' after JDK-8261966
  • c9d2d02: 8263632: Improve exception handling of APIs in classLoader.cpp
  • 59ed1fa: 8264087: Use the blessed modifier order in jdk.jconsole
  • 054e0a4: 8264017: Correctly report inlined frame in JFR sampling
  • d6bb153: 8264240: [macos_aarch64] enable appcds support after JDK-8263002
  • 7284f01: 8262110: DST starts from incorrect time in 2038
  • 3a28dc8: 8264178: Unused method Threads::nmethods_do
  • 33c94ff: 8263376: CTW (Shenandoah): assert(mems <= 1) failed: No node right after call if multiple mem projections
  • 4e74de4: 8264111: (fs) Leaking NativeBuffers in case of errors during UnixUserDefinedFileAttributeView.read/write
  • ... and 10 more: https://git.openjdk.java.net/jdk/compare/4e708e58dcd2cfa54807e8e412b7c6b472467efe...master

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Mar 26, 2021
@amaembo
Copy link
Contributor Author

amaembo commented Mar 26, 2021

@briangoetz thank you for review!

I would update the comment.

I updated the comment and moved it before the case branch. Please take a look.

Cute trick for the partial update to c.

Yeah, IDEA does this automatically!

@stuart-marks
Copy link
Member

stuart-marks commented Mar 27, 2021

I have some formatting questions. I also have some weak opinions on formatting, but not strong enough to make recommendations yet. (I see you wrote "Better formatting ideas are welcome" so thanks for being open to this.) I'm interested in feedback on how the code ends up reading, particularly from the people who will be maintaining this code. But comments from others are welcome too.

The switch (c) in ArgTokenizer::nextToken is pretty nicely formatted, since each case has a single constant, they're all short, and the values are also all constants that are textually short. The only thing I'd say here is to add a bit of spacing to the default case so that the arrows line up.

In Eval::prettyExpr switch (typeName), the switch is a bit irregular since one of the case arms has multiple constants. This makes it a bit harder to find one if you're looking for it in particular. For example, suppose you want to find out how "int" is handled. In the old code you can scan down looking at each case and then look at the constant right next to it. In the new code (as modified) the "int" is farther away from the case keyword and is a bit lost among the values, so it's harder to see. Let me see how the variations look.

(1) Here's the modified version as proposed in the PR:

    String sinit = switch (typeName) {
        case "byte", "short", "int" -> "0";
        case "long" -> "0L";
        case "float" -> "0.0f";
        case "double" -> "0.0d";
        case "boolean" -> "false";
        case "char" -> "'\\u0000'";
        default -> "null";
    };

(2) Variation with the arrows on the same line, but lined up:

    String sinit = switch (typeName) {
        case "byte", "short", "int" -> "0";
        case "long"                 -> "0L";
        case "float"                -> "0.0f";
        case "double"               -> "0.0d";
        case "boolean"              -> "false";
        case "char"                 -> "'\\u0000'";
        default                     -> "null";
    };

(3) Variation with arrows on the next line:

    String sinit = switch (typeName) {
        case "byte", "short", "int"
            -> "0";
        case "long"
            -> "0L";
        case "float"
            -> "0.0f";
        case "double"
            -> "0.0d";
        case "boolean"
            -> "false";
        case "char"
            -> "'\\u0000'";
        default
            -> "null";
    };

(4) Variation with arrows on the same line, aligned, with case constants one per line:

    String sinit = switch (typeName) {
        case "byte",
             "short",
             "int"     -> "0";
        case "long"    -> "0L";
        case "float"   -> "0.0f";
        case "double"  -> "0.0d";
        case "boolean" -> "false";
        case "char"    -> "'\\u0000'";
        default        -> "null";
    };

Personally I like variations (2) and (4), since they not only line the arrows up, the column of arrows creates a visual divider between the case constants and the values. Variation (3) is quite visually noisy to my eye, though still probably an improvement over the original code (a switch statement, not expression). Are there other variations? What do people think?

@briangoetz
Copy link
Contributor

briangoetz commented Mar 27, 2021 via email

@stuart-marks
Copy link
Member

stuart-marks commented Mar 27, 2021

Right, with longer case constants or values, the formatting dynamics of the entire expression change. Here's another example from the changeset, from Eval::kindOfTree.

(5) Here's the version as proposed in the PR:

    OuterWrap outer = switch (probableKind) {
        case IMPORT -> state.outerMap.wrapImport(Wrap.simpleWrap(compileSource), snip);
        case EXPRESSION -> state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(compileSource));
        case VAR, TYPE_DECL, METHOD -> state.outerMap.wrapInTrialClass(Wrap.classMemberWrap(compileSource));
        default -> state.outerMap.wrapInTrialClass(Wrap.methodWrap(compileSource));
    };

(6) Line breaks before the arrow:

    OuterWrap outer = switch (probableKind) {
        case IMPORT
            -> state.outerMap.wrapImport(Wrap.simpleWrap(compileSource), snip);
        case EXPRESSION
            -> state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(compileSource));
        case VAR, TYPE_DECL, METHOD 
            -> state.outerMap.wrapInTrialClass(Wrap.classMemberWrap(compileSource));
        default
            -> state.outerMap.wrapInTrialClass(Wrap.methodWrap(compileSource));
    };

(7) An extra blank line between each case:

    OuterWrap outer = switch (probableKind) {
        case IMPORT
            -> state.outerMap.wrapImport(Wrap.simpleWrap(compileSource), snip);

        case EXPRESSION
            -> state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(compileSource));

        case VAR, TYPE_DECL, METHOD 
            -> state.outerMap.wrapInTrialClass(Wrap.classMemberWrap(compileSource));

        default
            -> state.outerMap.wrapInTrialClass(Wrap.methodWrap(compileSource));
    };

(8) As above, but with the case constants one to a line:

    OuterWrap outer = switch (probableKind) {
        case IMPORT
            -> state.outerMap.wrapImport(Wrap.simpleWrap(compileSource), snip);

        case EXPRESSION
            -> state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(compileSource));

        case VAR,
             TYPE_DECL,
             METHOD
            -> state.outerMap.wrapInTrialClass(Wrap.classMemberWrap(compileSource));

        default
            -> state.outerMap.wrapInTrialClass(Wrap.methodWrap(compileSource));
    };

To my eye, (5) is too dense. The lines are too long. This is indented two levels (one for method in class, the second for code in method) and the longest line is 116 characters long. (Personally I tend to try to keep lines close to 100 or so at most.) The density of the lines makes it difficult to pick out the case constants from the values, as the arrows are buried in the middle. It's impossible to align the arrows, since that would make the lines even longer.

(6) puts line breaks before each arrow, which is kind of like (3) from the previous round of examples. This is an improvement, I think, and it's tolerable, whereas in the previous round (3) was quite bad. It's still quite dense, however.

For (7) I simply added line breaks between each case. I think this actually improves things a lot. It still takes up less vertical space than the original switch statement, but the additional white space makes it easier to read, and to pick out the case constants from values, I think. I note that in a switch statement, since break; often appears on its own line, it does have the useful effect of separating the case arms with an almost-blank line. Since we don't need break here, we have to add a blank line explicitly.

(8) is a variation of (7) with the case constants on individual lines. The advantage here is that all the constants line up with each other, but frankly I don't think this buys much. It also creates a weird disjointed indent, where the second and subsequent case constants are indented by five because of C-A-S-E-space, but the arrow is at the standard indent of four. You could line up the arrows at an indent level of five, but bleargh, this doesn't really help anything.

=====

From my first round of examples, I said I liked (2) and (4), but revisiting them, I've decided I like (4) less. I suppose I could tolerate (1) but the constants and values being all smashed together makes it difficult to separate them. I'd recommend (2) more strongly because having the arrows all line up provides a clear visual separation between the constants and the values. Of course, the recommendation for (2) stands only if the constants and values can all fit on a single line.

For the second round of examples, I think adding the blank lines makes a big difference. It means that the switch expression takes up almost as much vertical space as the switch statement. It can save a bit because with the statement, the multiple case labels were each on their own line, whereas for the expression I'm ok with putting the constants on the same line. Even though almost as much vertical space is consumed, I think the readability is quite good, and it still gains the benefits of switch expressions like exhaustiveness and lack of fall-thru.

@briangoetz
Copy link
Contributor

briangoetz commented Mar 27, 2021 via email

@amaembo
Copy link
Contributor Author

amaembo commented Mar 28, 2021

@briangoetz @stuart-marks thank you for your comments. I don't like (2) because the first case is significantly longer than the other ones. As a result, there's too much of whitespace on the subsequent lines, and it's hard to follow visually from the case label to the resulting expression without the editor's help (e. g. highlighting the current line). (4) solves this problem, so for code reading, it looks the best.

There's another consideration though: the code maintenance cost. I'm not sure about other editors but AFAIK, IntelliJ IDEA doesn't help you to maintain the vertical alignment for switch cases (we have a similar option for variable declarations, probably we should support switch cases as well), so any edits of the surrounding code may require manual reformatting, and autoformatting accidentally applied to this code may screw the things up.

As for 5-8, there's also an option to align arrows vertically:

(9)

OuterWrap outer = switch (probableKind) {
    case IMPORT                 -> state.outerMap.wrapImport(Wrap.simpleWrap(compileSource), snip);
    case EXPRESSION             -> state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(compileSource));
    case VAR, TYPE_DECL, METHOD -> state.outerMap.wrapInTrialClass(Wrap.classMemberWrap(compileSource));
    default                     -> state.outerMap.wrapInTrialClass(Wrap.methodWrap(compileSource));
};

In this case, the longest line has 114 characters, which looks within the surrounding code style.
We can also put labels one per line, line in 4. This makes lines even shorter:
(10)

OuterWrap outer = switch (probableKind) {
    case IMPORT     -> state.outerMap.wrapImport(Wrap.simpleWrap(compileSource), snip);
    case EXPRESSION -> state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(compileSource));
    case VAR, 
         TYPE_DECL, 
         METHOD     -> state.outerMap.wrapInTrialClass(Wrap.classMemberWrap(compileSource));
    default         -> state.outerMap.wrapInTrialClass(Wrap.methodWrap(compileSource));
};

By the way, I found that there's a redundant "preview" warning suppression at line 230. If we remove it and inline the variable, there's another opportunity for an expression switch. I also changed to (4) and (10) and added a space after 'default' in ArgTokenizer::nextToken. See the updated PR.

@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Mar 28, 2021
@openjdk openjdk bot added ready Pull request is ready to be integrated rfr Pull request is ready for review labels Mar 28, 2021
@amaembo
Copy link
Contributor Author

amaembo commented Apr 1, 2021

/integrate

@openjdk openjdk bot closed this Apr 1, 2021
@openjdk openjdk bot added integrated Pull request has been integrated and removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Apr 1, 2021
@openjdk
Copy link

openjdk bot commented Apr 1, 2021

@amaembo Since your change was applied there have been 81 commits pushed to the master branch:

  • 39f0b27: 8176026: SA: Huge heap sizes cause a negative value to be displayed in the jhisto heap total
  • de495df: 8264413: Data is written to file header even if its CRC32 was calculated
  • 52d8a22: 8264054: Bad XMM performance on java.lang.MathBench.sqrtDouble
  • 16acfaf: 8012229: [lcms] Improve performance of color conversion for images with alpha channel
  • cb70ab0: 8263235: sanity/client/SwingSet/src/ColorChooserDemoTest.java failed throwing java.lang.NoClassDefFoundError
  • e2ec997: 8263551: Provide shared lock-free FIFO queue implementation
  • dec3447: 8264346: nullptr_t undefined in global namespace for clang+libstdc++
  • 0fa3572: 8264489: Add more logging to LargeCopyWithMark.java
  • f43d14a: 8264396: Use the blessed modifier order in jdk.internal.jvmstat
  • 6225ae6: 8264466: Cut-paste error in InterfaceCalls JMH
  • ... and 71 more: https://git.openjdk.java.net/jdk/compare/4e708e58dcd2cfa54807e8e412b7c6b472467efe...master

Your commit was automatically rebased without conflicts.

Pushed as commit 3997c99.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

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

Labels

integrated Pull request has been integrated kulla [email protected]

Development

Successfully merging this pull request may close these issues.

3 participants