question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Var/LazyInit/etc annotations can cause problems for Android builds due to reliance on javax.lang.model.element.Modifier

See original GitHub issue

Description of the problem / feature request:

Multiple annotations inside com.google.errorprone:error_prone_annotations use the meta-annotation @IncompatibleModifiers(FINAL).

Here, FINAL references javax.lang.model.element.Modifier.FINAL. However, the android.jar file used to build Android projects does not include javax.lang.model.element.Modifier.

This results in the following error whenever code using @Var, @LazyInit, @ForOverride, etc. is compiled for Android with a -Werror flag (our default setup):

warning: unknown enum constant Modifier.FINAL
  reason: class file for javax.lang.model.element.Modifier not found
error: warnings found and -Werror specified
1 error
1 warning

For an example, here is a simple repro as a standalone Android project using gradle (see below for instructions).

Bugs: what’s the simplest, easiest way to reproduce this bug? Please provide a minimal example if possible.

git clone https://github.com/lazaroclapp/ep_modifiers_android_repro.git
gradle build

Observe:

warning: unknown enum constant Modifier.FINAL
  reason: class file for javax.lang.model.element.Modifier not found
error: warnings found and -Werror specified
1 error
1 warning

What version of Error Prone are you using?

Can repro on 2.5.1, 2.4, and 2.3.3

Have you found anything relevant by searching the web?

I posted on the mailing list and got prompt help from Liam figuring out the root cause of the issue. Thanks!

My recommended solution

The real problem here is @IncompatibleModifiers and its use of javax.lang.model.element.Modifier’s enum values as annotation parameters.

Could com.google.errorprone:error_prone_annotations perhaps include a separate Modifier enum that doesn’t depend on javax.lang.model.element.Modifier, and use that for the public API of the @IncompatibleModifiers meta-annotation and for annotating the relevant Error Prone annotations?

This will likely complicate IncompatibleModifiersChecker a bit, or require a mapping from the new custom Modifier enum to javax.lang.model.element.Modifier, but it will make the annotation fully compatible with Android builds.

FYI, if you agree that supporting Android with -Werror is important, and that the above is a reasonable solution, I’d be more than happy to give it a first try at a PR that replaces javax.lang.model.element.Modifier with e.g. com.google.errorprone.annotations.Modifier.

Issue Analytics

  • State:open
  • Created 3 years ago
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
cushoncommented, May 24, 2021

I went ahead and deprecated {Required,Incompatible}Modifiers.value, and migrated uses within core Error Prone to a new modifier enum in 26f1f5498321c9e2c25f5546dc9aa026affde16a. I hope to remove the deprecated attributes eventually, but hopefully there’s no rush for that.

2reactions
lazaroclappcommented, Feb 23, 2021

So, I think - but I am not sure - that this might just work immediately for us:

public @interface IncompatibleModifiers {
  /** @deprecated use {@link #modifier} instead. */
  @Deprecated
  javax.lang.model.element.Modifier[] value();
  com.google.errorprone.annotations.Modifier[] modifier();
}

But only if, at the same time the modifier() ‘field’ is introduced, all usages of @IncompatibleModifiers within Error Prone first party annotations switch to using the modifier = version.

I think it might be worth testing if that would be enough to prevent the compiler from trying to complete e.g. Modifier.FINAL when analyzing @Var, or if just the act of trying to load @IncompatibleModifiers will surface the same problem.

I might do a quick internal experiment on this. If that works, we might even be fine with value() lingering there forever for third-party Error Prone users (although I imagine everyone will be happier if there is a path to having a single way of doing things there eventually, anyways).

I understand this is not super high priority on your end, but would you guys consider a PR implementing the solution you detailed above if that fixed the issue for us?

Given that, my current thinking as to internal workarounds has evolved to favor forking error_prone_annotations and either doing my proposed patch or just removing the definition and usages of @IncompatibleModifiers, since what we really want to use is @Var and other annotations that use @IncompatibleModifiers as a meta-annotation.

That makes sense to me in the short term, FWIW

We are currently doing this, and it does mitigate the problem for us. But we’d love to be able to go back to not forking any part of Error Prone and just being on mainline! (Plus, I suspect we aren’t the only ones using EP for Android outside of Google…)


I’m just curious if you have opinions about the rate of breaking changes in the API, we don’t get a lot of feedback on that generally.

So, this is separate from the issue at hand, but, since you ask…

We did run into an issue recently where to get NullAway to work with Error Prone 2.5.1, we had to drop compatibility with 2.3.x (https://github.com/uber/NullAway/pull/447). So far, that hasn’t been too painful a decision, to be honest. Internally, we are currently on Error Prone 2.4.0. Would have been more complicated if we were lagging further behind, though, and we might yet hear of some NullAway third-party user who has issues upgrading to 0.9.0 because of it.

As for why we aren’t on 2.5.1 yet, we build massive monorepos with -Werror, so EP upgrades are unsurprisingly painful 😃

However, the main reasons are usually not any changes to core or the APIs (we modify our own checkers to match whichever EP version we are on and that’s often <1hr for all of them at worst), or new checkers being introduced (we can always set them to :OFF on the initial code change and then enable them independently).

The true upgrade pain points for us are:

a) dependency changes where upgrading Error Prone can easily involve a cascading upgrade of core deps for us (e.g. Guava), and

b) existing checkers that have been updated to be more precise.

The later is often a trade-off between adding a dozen to a few hundred suppressions or setting to :OFF a checker which we were running before (albeit possibly in a less comprehensive version). For 2.4.0, the javadoc checkers were notorious for us here.

The first issue, (a), just seems like a fact of life to me. Not sure (b) has an easy solution either, but at least in theory could be avoided if big changes to checkers resulted to checkers or in a world where you could say something like -Xep:MissingSummary:WARN:3.3.4 while keeping all other checkers on 2.4.0 by default and upgrading that one as a follow up 😉

We can simulate the later by forking a checker internally and renaming, but this is rarely worth it as an intermediate/mitigation step.

Just to be clear, though, I am not asking you to address any of this stuff. I am more concerned by the original issue. But you asked for feedback on breaking changes and EP versions, hence the brain-dump.

Read more comments on GitHub >

github_iconTop Results From Across the Web

JavaPoet + Android Studio "addModifiers(Modifier) cannot be ...
this question is like To this one How to get a reference to Modifier.PUBLIC that can not be applied in builder in MethodSpec's...
Read more >
java/dagger/hilt/processor/internal/Processors.java - Google Git
* Returns a multimap from attribute name to the values that are an array of annotation mirrors. * The returned map will not...
Read more >
Modifier (Java Platform SE 8 ) - Oracle Help Center
Represents a modifier on a program element such as a class, method, or field. Not all modifiers are applicable to all kinds of...
Read more >
Java module - can't reference javax.lang.model.* types in ...
3. Try creating a new method spec: MethodSpec.methodBuilder("main").addModifiers(Modifier.PUBLIC, Modifier.STATIC); Android Studio ...
Read more >
[JDK-8244367] javax.lang.model for sealed classes
Problem. Sealed classes, see (JEP 360), will be previewed in Java SE 15 and support will be needed ... Add two new modifiers...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found