IDE で注釈処理を動かすときにRetentionがSOURCEのアノテーションの情報が取れないことがある

IDE で注釈処理を動かすときに、 `@Retention(RetentionPolicy.SOURCE)` で定義しているアノテーションの情報が、特定の条件下で取得できないことがある。

# 環境
**OS**

`Windows 10`

**Java**

`openjdk 10.0.2`

**Eclipse**

`Photon (4.8.0)` ※[Pleiades](http://mergedoc.osdn.jp/)

**IntelliJ**

`IntelliJ IDEA 2018.2.3 (Community Edition)`

**Gradle**

`Gradle 4.10`

# 症状
## ソースコード
```text:プロジェクト構成
|-foo/
| `-src/main/java/
|   `-sample/foo/
|     |-Fizz.java
|     `-Buzz.java
|
|-bar/
| |-src/main/java/
| | `-sample/bar/
| |   |-MyRuntimeRetention.java
| |   |-MyClassRetention.java
| |   |-MySourceRetention.java
| |   `-MyProcessor.java
| |
| `-src/main/resources/
|   `-META-INF/services
|     `-javax.annotation.processing.Processor
|
|-build.gradle
`-settings.gradle
```

- `foo`, `bar` のマルチプロジェクト
- `bar` の方が注釈処理を実装したプロジェクト
- `foo` は `bar` の注釈処理を利用しているプロジェクト
- `MyRuntimeRetention.java`, `MyClassRetention.java`, `MySourceRetention.java` は、それぞれ自作のアノテーションで `Retention` に名前に対応する値を設定している

```java:MyRuntimeRetention.java
package sample.bar;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRuntimeRetention {
}
```

```java:MyClassRetention.java
package sample.bar;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface MyClassRetention {
}
```

```java:MySourceRetention.java
package sample.bar;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface MySourceRetention {
}
```

- `MyProcessor.java` が注釈処理を実装したクラス

```java:MyProcessor.java
package sample.bar;

import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic.Kind;

public class MyProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Elements elements = this.processingEnv.getElementUtils();
        
        if (!annotations.isEmpty()) {
            this.printAnnotations(elements.getTypeElement("sample.foo.Fizz"));
            this.printAnnotations(elements.getTypeElement("sample.foo.Buzz"));
        }
        
        return true;
    }
    
    private void printAnnotations(TypeElement element) {
        element.getAnnotationMirrors().forEach(annotation -> {
            this.processingEnv.getMessager().printMessage(Kind.NOTE, element.getSimpleName() + " : " + annotation);
        });
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.RELEASE_10;
    }
    
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return Set.of(MySourceRetention.class.getCanonicalName());
    }
}
```

- `Fizz`, `Buzz` クラスの `TypeElement` を取得して、クラスに設定されているアノテーションを一覧で出力している

```java:Fizz.java
package sample.foo;

import sample.bar.MyClassRetention;
import sample.bar.MyRuntimeRetention;
import sample.bar.MySourceRetention;

@MyRuntimeRetention
@MyClassRetention
@MySourceRetention
public class Fizz {
}
```

```java:Buzz.java
package sample.foo;

import sample.bar.MyClassRetention;
import sample.bar.MyRuntimeRetention;
import sample.bar.MySourceRetention;

@MyRuntimeRetention
@MyClassRetention
@MySourceRetention
public class Buzz {
}
```

- `foo` プロジェクトの `Fizz`, `Buzz` は、クラスに３つの自作アノテーションを設定している

## 実施する処理
この状態で **`Fizz` クラスだけを修正して**コンパイルを実行する。
すると、 Gradle で実行したときと IDE (Eclipse, IntelliJ) で実行したときで異なる結果になる。

## Gradle で動かしたとき
```
> gradle :foo:compileJava

注意:Fizz : @sample.bar.MyRuntimeRetention
注意:Fizz : @sample.bar.MyClassRetention
注意:Fizz : @sample.bar.MySourceRetention
注意:Buzz : @sample.bar.MyRuntimeRetention
注意:Buzz : @sample.bar.MyClassRetention
注意:Buzz : @sample.bar.MySourceRetention
```

- 普通にすべてのアノテーションの情報が取得できている

## Eclipse で動かしたとき
![apt.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/2bcf30ad-56fa-2960-a72c-34eec06e3e62.jpeg)

- `Fizz` クラスのアノテーションはすべて取得できている
- しかし、 `Buzz` クラスのアノテーションは `@MySourceRetention` が取得できていない

## IntelliJ で動かしたとき
![apt.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/78f5dc5b-7b63-9624-869d-3308779d8a06.jpeg)

- こちらも Eclipse と同じで、 `Buzz` クラスのアノテーション情報から `@MySourceRetention` が取得できていない

# Buzz も修正すると IDE でも取得できる
- `Fizz` クラスだけでなく `Buzz` クラスも修正してからビルドを実行して注釈処理を動かす
- すると、次のような結果になる

## Eclipse
![apt.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/0034c6c4-acb8-7436-9f76-c1ce9a9b765d.jpeg)

- `Buzz` に設定された `@MySourceRetention` も取得できている

## IntelliJ
![apt.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/e5107652-68a3-fd3a-8eeb-a0285e095c7f.jpeg)

- こちらも同様で、 `@MySourceRetention` を取得できている

# 何が起こったか推測
- `Retention` を `SOURCE` にした場合、[アノテーションの情報はクラスファイルには残らない](https://docs.oracle.com/javase/jp/10/docs/api/java/lang/annotation/RetentionPolicy.html#SOURCE)
- 修正していない場合に `SOURCE` 指定したアノテーションの情報だけが取得できていなかったことを鑑みるに、おそらく IDE (Eclipse, IntelliJ) で注釈処理が動いた場合は、修正が入っていないクラスの情報は以前のコンパイル結果（class ファイル）が利用されている気がする
- ちなみに、 NetBeans は試していない

# 結論（推測）
- IDE (Eclipse, IntelliJ) で注釈処理が動くとき、修正が入っていないクラスに設定されている `Retention` が `SOURCE` のアノテーションの情報は取得できないっぽい
- 理由はおそらく、 IDE は修正が入っていないクラスの情報を以前のコンパイル結果(class ファイル)から取得しているっぽいから
- 自作で注釈処理を書くときは、 `Retention` が `SOURCE` のアノテーションの扱いには注意が必要
- アノテーションを自作しているのであれば、 `Retention` を `CLASS` 以上にしておくのが良さげ