Skip to content

Commit

Permalink
Merge pull request #6956 from lahodaj/annotation-processor-error-comp…
Browse files Browse the repository at this point in the history
…letions

Allow invocation of annotation Processor's getCompletions even if process fails.
  • Loading branch information
lahodaj authored Feb 16, 2024
2 parents a52aa9a + 94bd559 commit 6a9d501
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 55 deletions.
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

0 comments on commit 6a9d501

Please sign in to comment.