IntelliJ IDEA 2018.2 EAP3: advanced @Contract annotations and more

We’ve just finished assembling a new EAP build of IntelliJ IDEA 2018.2. You’re very welcome to download it right now!

Java

IntelliJ IDEA supports the @org.jetbrains.annotations.Contract annotation, which allows you to specify a method reaction when a particular parameter is passed. This can help with nullability analysis and some other inspections.

We’ve introduced the @Contract annotation a while ago, and we’ve been improving it ever since. We added automatic inference, and today we are ready to take it even further.

Suppose you have a utility method:

Screen Shot 2018-05-29 at 19.06.21

Then the following method does not issue a warning on string dereference:

Screen Shot 2018-05-29 at 19.31.09

The contract says that if str is null, isNotEmptyString returns false. Therefore, str.charAt(0) is not executed.

In the upcoming IntelliJ IDEA 2018.2, we’re improving the Contract language to support more return values. New values include this, new and param1/2/3/....

The contract _ -> this means that the non-static method always returns the this object (like StringBuilder.append does).

A contract like _ -> param1 means that the method always returns its first parameter (unless it throws an exception).

If a contract returns a new value, it means that the method always returns a newly allocated object. New values allow you to describe the method behavior more precisely. Imagine a coalesce method:

Screen Shot 2018-05-29 at 19.47.21

Its contract can be described as null, _ -> param2; !null, _ -> param1. This reads as “if the first parameter is null, then parameter#2 is returned; if the first parameter is not null, then parameter#1 is returned”.

It’s not always necessary to specify the contract. Sometimes (for example, in the “coalesce” example above) it can be inferred automatically for static, private, or final methods.

The contract information is available for various inspections and actions which can use it to produce better warnings or remove false-positives. For example, in the upcoming IntelliJ IDEA 2018.2, you may get a warning in the code like stringBuilder = stringBuilder.append(something); because now the IDE knows that the append method will return its qualifier, so there’s no reason to reassign the stringBuilder variable.

Similarly, the inspection Result of method call ignored will not show a warning if a pure method is known to always return its argument. Likely it’s a validation method which either throws or returns an argument for convenience, so using a return value is unnecessary.

As a result, several new kinds of bugs can now be detected. For example, if you use the coalesce method described above, the IDE is now aware that the inner condition is always false in the following code:

2018-05-29 14_25_36

Actions like Analyze dataflow from here are also aware of new contracts now. For example, consider the following class:

Screen Shot 2018-05-29 at 19.49.00
When starting dataflow analysis from the constructor parameter, you can now eventually reach the getter, as analysis knows that Objects.requireNonNull returns its argument unless it fails (its contract is "null -> fail; _ -> param1"):

image6

Speaking of our annotations, IntelliJ IDEA now provides annotations.jar as a Maven repository library. Previously, for a project that didn’t use Maven or Gradle, IntelliJ IDEA would suggest adding annotations.jar (or junit.jar) from its own installation directory.

Now, when there are some unresolved references to annotations like @NotNull or @Contract, IntelliJ IDEA will provide a quick-fix to add annotations.jar from the Maven repository. After you download this JAR for the first time, it will be stored in the local .m2/repository.

Several libraries can now be added to a project as Maven repository libraries: JUnit 3 and JUnit4, Testng, JCIP, and our own annotations library.

Jump outside closing bracket/quote with Tab

In the upcoming IntelliJ IDEA 2018.2, you will be able to navigate outside the closing brackets, or closing quotes, by pressing Tab. To customize this behavior of Tab, go to Preferences | Editor | General | Smart keys and select Jump outside closing brackets/quote with Tab. This will work in Java, Kotlin, Groovy, SQL, and Python files.

2018-05-29 17_13_42

JVM debugger

Last week we introduced breakpoint intentions, which are available via Alt+Enter. This latest build further enhances this functionality: please welcome new breakpoint intentions that allow filtering by a caller method.

Sometimes it is important to stop at a breakpoint only when a certain condition applies to the call stack. Now, if you filter a breakpoint hit by the caller method, it will stop at a breakpoint only if it’s called from the specified method. (Or, vice versa, it will not stop at a breakpoint if it’s called from that method.)

Screen Shot 2018-05-28 at 12.26.53

You can also set a caller method filter by using the Caller filters field in the Breakpoint dialog.

image5

In other news, we’ve improved our support for SVG files so that now the IDE provides completion in them. Under the hood, it reuses the SVG part of RelaxNG schema from HTML5 support. Quick Documentation Lookup is also available for SVG files now.

Screen Shot 2018-05-28 at 18.12.21

Version Control System

We’ve also improved our Git integration with a small yet very useful feature: the IDE now autocompletes tags in the Checkout dialog.

Screen Shot 2018-05-29 at 16.09.39

We’d love to hear your feedback on these cool new features! Please share your thoughts with us in the discussion forum or on Twitter. If you stumble on a bug, please report it to our issue tracker. Thanks!

Happy Developing!

About Zlata Kalyuzhnaya

IntelliJ IDEA Marketing Manager at JetBrains. twitter: @ZlataKalyuzhnay ‏
This entry was posted in EAP Releases and tagged , . Bookmark the permalink.

12 Responses to IntelliJ IDEA 2018.2 EAP3: advanced @Contract annotations and more

  1. Elior Boukhobza says:

    Can you work on the Rebase dialog so it provide autocomplete for remote branches?

    • Zlata Kalyuzhnaya says:

      Hi Elior! We are thinking about improving the usability of the Rebase dialog. But I can’t give you any estimation date. Sorry

  2. Matt says:

    >Jump outside closing bracket/quote with Tab

    Nice, will this be coming to Typescript/Javascript soon?

    • Matt says:

      Also….so this is only supposed to work when you’re already at the character of the quotes or parenthesis? And it seems like it only works the first time when you first write the string or parenthesis, it doesn’t work if you go back and edit existing strings or parenthesis.

      • Dmitry Batrak says:

        Yes, it’s supposed to work only while you type/complete the code and not for the subsequent editing.

        • Edoardo says:

          Hi Dmitry! It would be cool to be able to jump outside multiple parenthesis. Take for example the IF example .gif you posted, it would be good to be able to jump outside the last two parenthesis, with two TAB strokes. What do you think?

          • Dmitry Batrak says:

            Hi Edoardo,
            Doesn’t it work already? In the GIF at the end of recording jumping outside the last two parenthesis is done using Tab presses.

            • Edoardo says:

              Yes you’re right, it works! I don’t remember the exact piece of code I had tried on. If I’m able to remember I’ll give it another try and eventually submit and issue. Thanks!

  3. Pingback: Java Weekly, Issue 231 | Baeldung

  4. Dmitry says:

    Is it possible to express with @Contract that some @Nullable method getFoo behaves as j.u.Map#get:
    – Returns not-null object if some other method containsFoo (similar to j.u.Map#contains) returns true;
    – Returns null otherwise?

    Currently IDEA issues warnings for foo in the impossible case:
    if (obj.containsFoo()) {
    var foo = obj.getFoo();
    }

    Is there a way to tell IDEA that containsFoo returning true implies getFoo producing non-null value? I haven’t seen any annotations in j.u.Map.

    • Tagir Valeev says:

      No, currently such kind of contracts is impossible. On the other hand if method returns null only if something is missing, then it seems that you don’t need a containsFoo method. You can write:

      var foo = obj.getFoo();
      if (foo != null) {
      ...
      }

      Now you have no warnings and only one method call.

      Note that Map.get("foo") can return null even if Map.contains("foo") returns true (if null values was stored to the Map).

Leave a Reply

Your email address will not be published. Required fields are marked *