Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow invocation of annotation Processor's getCompletions even if process fails. #6956

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -958,31 +958,32 @@ private static final class ErrorToleratingProcessor implements Processor {

private final Processor delegate;
private ProcessingEnvironment processingEnv;
private boolean valid = true;
private boolean initFailed = false;
private boolean processFailed = false;

public ErrorToleratingProcessor(Processor delegate) {
this.delegate = delegate;
}

@Override
public Set<String> getSupportedOptions() {
if (!valid) {
if (initFailed) {
return Collections.emptySet();
}
return delegate.getSupportedOptions();
}

@Override
public Set<String> getSupportedAnnotationTypes() {
if (!valid) {
if (initFailed) {
return Collections.emptySet();
}
return delegate.getSupportedAnnotationTypes();
}

@Override
public SourceVersion getSupportedSourceVersion() {
if (!valid) {
if (initFailed) {
return SourceVersion.latest();
}
return delegate.getSupportedSourceVersion();
Expand All @@ -993,10 +994,10 @@ public void init(ProcessingEnvironment processingEnv) {
try {
delegate.init(processingEnv);
} catch (ClientCodeException | ThreadDeath | Abort err) {
valid = false;
initFailed = true;
throw err;
} catch (Throwable t) {
valid = false;
initFailed = true;
StringBuilder exception = new StringBuilder();
exception.append(t.getMessage()).append("\n");
for (StackTraceElement ste : t.getStackTrace()) {
Expand All @@ -1010,16 +1011,16 @@ public void init(ProcessingEnvironment processingEnv) {
@Override
@Messages("ERR_ProcessorException=Annotation processor {0} failed with an exception: {1}")
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (!valid) {
if (initFailed || processFailed) {
return false;
}
try {
return delegate.process(annotations, roundEnv);
} catch (ClientCodeException | ThreadDeath | Abort err) {
valid = false;
processFailed = true;
throw err;
} catch (Throwable t) {
valid = false;
processFailed = true;
Element el = roundEnv.getRootElements().isEmpty() ? null : roundEnv.getRootElements().iterator().next();
StringBuilder exception = new StringBuilder();
exception.append(t.getMessage()).append("\n");
Expand All @@ -1033,12 +1034,11 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment

@Override
public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
if (!valid) {
if (initFailed) {
return Collections.emptySet();
}
return delegate.getCompletions(element, annotation, member, userText);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URL;
Expand All @@ -31,10 +32,15 @@
import junit.framework.*;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Completion;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.swing.event.ChangeListener;
import org.netbeans.api.annotations.common.CheckForNull;
Expand All @@ -47,6 +53,7 @@
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.JavaSource.Phase;
import org.netbeans.api.java.source.SourceUtils;
import org.netbeans.api.java.source.Task;
import org.netbeans.junit.NbTestCase;
import org.netbeans.modules.java.source.TestUtil;
Expand All @@ -55,7 +62,6 @@
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.netbeans.spi.java.queries.AnnotationProcessingQueryImplementation;
import org.netbeans.spi.java.queries.SourceLevelQueryImplementation;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Lookup;
Expand All @@ -71,7 +77,7 @@ public class CrashingAPTest extends NbTestCase {
CrashingAPTest.class.getClassLoader().setDefaultAssertionStatus(true);
System.setProperty("org.openide.util.Lookup", CrashingAPTest.Lkp.class.getName());
Assert.assertEquals(CrashingAPTest.Lkp.class, Lookup.getDefault().getClass());
}
}

public static class Lkp extends ProxyLookup {

Expand All @@ -87,7 +93,6 @@ public Lkp () {
Lookups.singleton(l),
Lookups.singleton(ClassPathProviderImpl.getDefault()),
Lookups.singleton(SourceLevelQueryImpl.getDefault()),
Lookups.singleton(new APQImpl()),
});
}

Expand All @@ -108,39 +113,79 @@ protected void setUp() throws Exception {
assertNotNull(wd);
this.src = wd.createFolder("src");
this.data = src.createData("Test","java");
FileLock lock = data.lock();
try {
PrintWriter out = new PrintWriter ( new OutputStreamWriter (data.getOutputStream(lock)));
try {
out.println ("public class Test {}");
} finally {
out.close ();
}
} finally {
lock.releaseLock();
}
ClassPathProviderImpl.getDefault().setClassPaths(TestUtil.getBootClassPath(),
ClassPathSupport.createClassPath(new URL[0]),
ClassPathSupport.createClassPath(new FileObject[]{this.src}),
ClassPathSupport.createClassPath(System.getProperty("java.class.path")));
}

public void testElementHandle() throws Exception {
final JavaSource js = JavaSource.forFileObject(data);
assertNotNull(js);

js.runUserActionTask(new Task<CompilationController>() {
public void run(CompilationController parameter) throws IOException {
parameter.toPhase(Phase.RESOLVED);
List<String> messages = parameter.getDiagnostics()
.stream()
.map(d -> d.getMessage(null))
.map(m -> firstLine(m))
.collect(Collectors.toList());
List<String> expected = Arrays.asList(Bundle.ERR_ProcessorException("org.netbeans.modules.java.source.indexing.CrashingAPTest$TestAP", "Crash"));
assertEquals(expected, messages);
try (OutputStream dataOut = data.getOutputStream();
PrintWriter out = new PrintWriter ( new OutputStreamWriter (dataOut))) {
out.println ("public class Test {}");
}

runWithProcessors(Arrays.asList(TestAP.class.getName()), () -> {
final JavaSource js = JavaSource.forFileObject(data);
assertNotNull(js);

js.runUserActionTask(new Task<CompilationController>() {
public void run(CompilationController parameter) throws IOException {
parameter.toPhase(Phase.RESOLVED);
List<String> messages = parameter.getDiagnostics()
.stream()
.map(d -> d.getMessage(null))
.map(m -> firstLine(m))
.collect(Collectors.toList());
List<String> expected = Arrays.asList(Bundle.ERR_ProcessorException("org.netbeans.modules.java.source.indexing.CrashingAPTest$TestAP", "Crash"));
assertEquals(expected, messages);
}
},true);

return null;
});
}

public void testCompletionQuery() throws Exception {
try (OutputStream dataOut = data.getOutputStream();
PrintWriter out = new PrintWriter ( new OutputStreamWriter (dataOut))) {
out.println ("@I(g=) public class Test {} @interface I { public String g(); }");
}

runWithProcessors(Arrays.asList(TestAP.class.getName()), () -> {
final JavaSource js = JavaSource.forFileObject(data);
assertNotNull(js);

js.runUserActionTask(new Task<CompilationController>() {
public void run(CompilationController parameter) throws IOException {
parameter.toPhase(Phase.RESOLVED);
TypeElement clazz = parameter.getTopLevelElements().get(0);
AnnotationMirror am = clazz.getAnnotationMirrors().get(0);
ExecutableElement method = am.getElementValues().keySet().iterator().next();
List<String> result =
SourceUtils.getAttributeValueCompletions(parameter, clazz, am, method, "")
.stream()
.map(c -> c.getValue() + "-" + c.getMessage())
.collect(Collectors.toList());

assertEquals(Arrays.asList("value-message"), result);
}
},true);

return null;
});
}

private void runWithProcessors(Iterable<? extends String> processors, Callable<Void> toRun) {
ProxyLookup newLookup = new ProxyLookup(Lookup.getDefault(),
Lookups.fixed(new APQImpl(processors)));
Lookups.executeWith(newLookup, () -> {
try {
toRun.call();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
},true);
});
}

private String firstLine(String m) {
Expand Down Expand Up @@ -216,27 +261,32 @@ public static synchronized SourceLevelQueryImpl getDefault () {

private static class APQImpl implements AnnotationProcessingQueryImplementation {

private final Result result = new Result() {
public @NonNull Set<? extends Trigger> annotationProcessingEnabled() {
return EnumSet.allOf(Trigger.class);
}
private final Result result;

public @CheckForNull Iterable<? extends String> annotationProcessorsToRun() {
return Arrays.asList(TestAP.class.getName());
}
public APQImpl(Iterable<? extends String> processors) {
result = new Result() {
public @NonNull Set<? extends Trigger> annotationProcessingEnabled() {
return EnumSet.allOf(Trigger.class);
}

public @CheckForNull URL sourceOutputDirectory() {
return null;
}
public @CheckForNull Iterable<? extends String> annotationProcessorsToRun() {
return processors;
}

public @NonNull Map<? extends String, ? extends String> processorOptions() {
return Collections.emptyMap();
}
public @CheckForNull URL sourceOutputDirectory() {
return null;
}

public @NonNull Map<? extends String, ? extends String> processorOptions() {
return Collections.emptyMap();
}

public void addChangeListener(@NonNull ChangeListener l) {}
public void addChangeListener(@NonNull ChangeListener l) {}

public void removeChangeListener(@NonNull ChangeListener l) {}
};
}

public void removeChangeListener(@NonNull ChangeListener l) {}
};
@Override
public AnnotationProcessingQuery.Result getAnnotationProcessingOptions(FileObject file) {
return result;
Expand All @@ -250,6 +300,22 @@ public static class TestAP extends AbstractProcessor {
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
throw new IllegalStateException("Crash");
}

@Override
public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
return Arrays.asList(new Completion() {
@Override
public String getValue() {
return "value";
}

@Override
public String getMessage() {
return "message";
}
});
}

}

static {
Expand Down
Loading