Bug 149768 - Annotations should be a compilation dependency
Summary: Annotations should be a compilation dependency
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.2   Edit
Hardware: PC Windows XP
: P3 enhancement with 5 votes (vote)
Target Milestone: 3.5 M5   Edit
Assignee: Kent Johnson CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 223497 (view as bug list)
Depends on:
Blocks:
 
Reported: 2006-07-05 19:31 EDT by Walter Harley CLA
Modified: 2009-01-27 05:55 EST (History)
5 users (show)

See Also:


Attachments
test case (1.85 KB, patch)
2008-12-23 03:10 EST, Walter Harley CLA
no flags Details | Diff
Proposed patch (3.55 KB, patch)
2008-12-23 12:15 EST, Kent Johnson CLA
no flags Details | Diff
Test cases (9.09 KB, patch)
2009-01-04 03:06 EST, Walter Harley CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Walter Harley CLA 2006-07-05 19:31:02 EDT
If class B extends A, then B will be recompiled when A's structure changes - for instance, when the return type or modifiers of a method change.

At present, changes to annotations are not considered structural changes.  So, if A has a method "public int foo()", changing it to "protected int foo()" will trigger recompilation of B; but changing it to "@Bar public int foo()" will not.

This causes problems for certain annotation processors.  Consider a processor that looks for classes annotated with @GetterTarget, and when it finds one produces a subclass containing getXXX() methods for every protected field in its superclasses that is annotated with a particular annotation.  That processor should be run whenever annotations change on any of its supertypes.

This is very similar to the idea that adding @Deprecated to a type or method, or @Retention to an annotation, should result in recompilation of its dependents.  Those examples already work in Eclipse, because they are special-cased.  The request here is for all annotations to be treated the same way.  Specifically, any aspect of a type's structure that can be accessed from a JSR-269 annotation processor should also be honored when evaluating structural changes, so that the annotation processor gets called when it should.
Comment 1 Walter Harley CLA 2008-03-27 13:17:38 EDT
*** Bug 223497 has been marked as a duplicate of this bug. ***
Comment 2 Vladimir Piskarev CLA 2008-12-19 02:42:46 EST
Please consider raising severity of this bug. This bug is a real show-stopper for us. 

Consider a very simple annotation processor validating that each field annotated with @Reference should have a type annotated with @Referable (a variation on 'dependency injection' theme). This basic processor is not going to work in incremental build mode. For example, if we remove @Referable annotation on some class the classes which depend on that class (including classes with @Reference fields referring that class) will not be recompiled and our validating processor will not even be called so no errors will be flagged to the user. Thus the referential integrity of our model will be compromised. The only workaround being the full build is really unacceptable as it ruins the user experience and will not scale on large projects.
Comment 3 Walter Harley CLA 2008-12-19 03:05:20 EST
DI is an interesting use case for this feature.

Can you clarify the expectation?  I'm imagining you have something like:

  @Reference(Foo.class)
  class Bar { ... }

and then you are expecting that if annotations on Foo change, the compiler should recompile Bar?

Presumably it would not work if the class were referred to by name alone, e.g.,

  @Reference("com.you.Foo")
  class Bar { ... }

Comment 4 Vladimir Piskarev CLA 2008-12-19 04:03:18 EST
Walter, I meant something like that:

@Referable
class Foo { ... }

class Bar {
    @Reference
    Foo foo;
}

in which case an instance of Foo will be injected by DI container into the field 'foo'. The invariant being 'each field annotated with @Reference should refer to a @Referable class'. So if @Referable is removed from 'Foo' our processor should flag an error on 'Bar.foo'.
Comment 5 Vladimir Piskarev CLA 2008-12-19 04:51:31 EST
Another use case. Parameter types of @WebMethod methods should be annotated with @XmlRootElement (from JAXB). I.e.:

@WebService
class Foo {
    @WebMethod
    void foo(Bar bar);
}

@XmlRootElement
class Bar { ... }

So if @XmlRootElement is removed from 'Bar' our processor should flag an error on 'bar' parameter of 'Foo#foo' method.
Comment 6 Walter Harley CLA 2008-12-19 11:33:15 EST
So, in all these cases you have a normal compilation dependency (that is, the compiler clearly knows it has a type dependency); no enhancement of the dependency computation is needed, rather it's a matter of broadcasting annotation changes in the same way that, e.g., changing the type hierarchy of the annotated type would be broadcast.
Comment 7 Vladimir Piskarev CLA 2008-12-19 14:22:46 EST
Exactly!
Comment 8 Walter Harley CLA 2008-12-23 03:10:25 EST
Created attachment 121111 [details]
test case

Here's a patch for org.eclipse.jdt.apt.tests, that demonstrates the problem.  The test case in this patch currently fails.

I'll try and write a jdt.core (not APT) test to verify this - I figured I'd start with something I knew well.
Comment 9 Kent Johnson CLA 2008-12-23 12:15:08 EST
Created attachment 121159 [details]
Proposed patch
Comment 10 Walter Harley CLA 2009-01-04 03:06:49 EST
Created attachment 121479 [details]
Test cases

Here's a suite of test cases that run in org.eclipse.jdt.core.tests.builder.  These tests fail without the proposed patch, and pass with it.

Note that they (of course) require a 1.5 VM.

I've also modified BuilderTests to include the new test cases, in the 1.5 section.

One question: I don't really understand why changing an annotation on an inner type X within type A causes type B to be recompiled, if B references A (but not A.X).  Is it because dependencies are managed on the basis of compilation units rather than types?  It's working fine, I'm just wondering why.
Comment 11 Kent Johnson CLA 2009-01-05 10:24:40 EST
> Is it because dependencies are managed on the basis of compilation units rather than types?

Correctamondo

Type dependencies take too much space & since we end up recompiling the entire compilation unit - why bother ?
Comment 12 Kent Johnson CLA 2009-01-07 12:29:44 EST
Fix and tests released for 3.5M5
Comment 13 David Audel CLA 2009-01-27 05:55:00 EST
Verified for 3.5M5 using I20090126-1300