From 3ed67fc8aecf50757d85bdfb55075015c6627740 Mon Sep 17 00:00:00 2001 From: Robbert Jan Grootjans Date: Mon, 18 Jun 2012 23:58:51 +0200 Subject: Fixed Help Content in Eclipse / ecj support. - Moved Completion proposal code to the PatchExtensionMethodCompletionProposal class - Moved out error reporting from EclipseAST. - Fixed error reporting of the portals. --- src/core/lombok/eclipse/EclipseAST.java | 82 ++++++------ src/core/lombok/eclipse/EclipseAstProblemView.java | 56 ++++++++ .../lombok/eclipse/agent/PatchDelegatePortal.java | 11 +- .../lombok/eclipse/agent/PatchExtensionMethod.java | 134 +------------------- .../PatchExtensionMethodCompletionProposal.java | 141 ++++++++++++++++++++- ...tchExtensionMethodCompletionProposalPortal.java | 15 ++- .../eclipse/agent/PatchExtensionMethodPortal.java | 103 +++++++++++++++ .../eclipse/agent/PatchValEclipsePortal.java | 48 ++++--- 8 files changed, 391 insertions(+), 199 deletions(-) create mode 100644 src/core/lombok/eclipse/EclipseAstProblemView.java create mode 100644 src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodPortal.java diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java index 4b009469..8ab42140 100644 --- a/src/core/lombok/eclipse/EclipseAST.java +++ b/src/core/lombok/eclipse/EclipseAST.java @@ -21,15 +21,16 @@ */ package lombok.eclipse; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import lombok.Lombok; import lombok.core.AST; -import org.eclipse.jdt.core.compiler.CategorizedProblem; -import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; @@ -42,9 +43,6 @@ import org.eclipse.jdt.internal.compiler.ast.Initializer; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; -import org.eclipse.jdt.internal.compiler.problem.DefaultProblem; -import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; -import org.eclipse.jdt.internal.compiler.util.Util; /** * Wraps around Eclipse's internal AST view to add useful features as well as the ability to visit parents from children, @@ -147,44 +145,25 @@ public class EclipseAST extends AST { public static void addProblemToCompilationResult(CompilationUnitDeclaration ast, boolean isWarning, String message, int sourceStart, int sourceEnd) { if (ast.compilationResult == null) return; - char[] fileNameArray = ast.getFileName(); - if (fileNameArray == null) fileNameArray = "(unknown).java".toCharArray(); - int lineNumber = 0; - int columnNumber = 1; - CompilationResult result = ast.compilationResult; - int[] lineEnds = null; - lineNumber = sourceStart >= 0 - ? Util.getLineNumber(sourceStart, lineEnds = result.getLineSeparatorPositions(), 0, lineEnds.length-1) - : 0; - columnNumber = sourceStart >= 0 - ? Util.searchColumnNumber(result.getLineSeparatorPositions(), lineNumber,sourceStart) - : 0; - - CategorizedProblem ecProblem = new LombokProblem( - fileNameArray, message, 0, new String[0], - isWarning ? ProblemSeverities.Warning : ProblemSeverities.Error, - sourceStart, sourceEnd, lineNumber, columnNumber); - ast.compilationResult.record(ecProblem, null); - } - - private static class LombokProblem extends DefaultProblem { - private static final String MARKER_ID = "org.eclipse.jdt.apt.pluggable.core.compileProblem"; //$NON-NLS-1$ - - public LombokProblem(char[] originatingFileName, String message, int id, - String[] stringArguments, int severity, - int startPosition, int endPosition, int line, int column) { - super(originatingFileName, message, id, stringArguments, severity, startPosition, endPosition, line, column); - } - - @Override public int getCategoryID() { - return CAT_UNSPECIFIED; - } - - @Override public String getMarkerType() { - return MARKER_ID; + try { + EcjReflectionCheck.addProblemToCompilationResult.invoke(null, ast, isWarning, message, sourceStart, sourceEnd); + } catch (NoClassDefFoundError e) { + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + } catch (IllegalAccessException e) { + throw Lombok.sneakyThrow(e); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e); + } catch (NullPointerException e) { + if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { + e.initCause(EcjReflectionCheck.problem); + throw e; + } + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. } } - + private final CompilationUnitDeclaration compilationUnitDeclaration; private boolean completeParse; @@ -372,4 +351,25 @@ public class EclipseAST extends AST { @Override protected Collection> getStatementTypes() { return Collections.>singleton(Statement.class); } + + private static class EcjReflectionCheck { + private static final String CUD_TYPE = "org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration"; + + public static Method addProblemToCompilationResult; + public static final Throwable problem; + + static { + Throwable problem_ = null; + Method m = null; + try { + m = EclipseAstProblemView.class.getMethod("addProblemToCompilationResult", Class.forName(CUD_TYPE), boolean.class, String.class, int.class, int.class); + } catch (Throwable t) { + // That's problematic, but as long as no local classes are used we don't actually need it. + // Better fail on local classes than crash altogether. + problem_ = t; + } + addProblemToCompilationResult = m; + problem = problem_; + } + } } diff --git a/src/core/lombok/eclipse/EclipseAstProblemView.java b/src/core/lombok/eclipse/EclipseAstProblemView.java new file mode 100644 index 00000000..a2d5b833 --- /dev/null +++ b/src/core/lombok/eclipse/EclipseAstProblemView.java @@ -0,0 +1,56 @@ +package lombok.eclipse; + + +import org.eclipse.jdt.core.compiler.CategorizedProblem; +import org.eclipse.jdt.internal.compiler.CompilationResult; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.problem.DefaultProblem; +import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; +import org.eclipse.jdt.internal.compiler.util.Util; + +public class EclipseAstProblemView { + /** + * Adds a problem to the provided CompilationResult object so that it will show up + * in the Problems/Warnings view. + */ + public static void addProblemToCompilationResult(CompilationUnitDeclaration ast, + boolean isWarning, String message, int sourceStart, int sourceEnd) { + if (ast.compilationResult == null) return; + char[] fileNameArray = ast.getFileName(); + if (fileNameArray == null) fileNameArray = "(unknown).java".toCharArray(); + int lineNumber = 0; + int columnNumber = 1; + CompilationResult result = ast.compilationResult; + int[] lineEnds = null; + lineNumber = sourceStart >= 0 + ? Util.getLineNumber(sourceStart, lineEnds = result.getLineSeparatorPositions(), 0, lineEnds.length-1) + : 0; + columnNumber = sourceStart >= 0 + ? Util.searchColumnNumber(result.getLineSeparatorPositions(), lineNumber,sourceStart) + : 0; + + CategorizedProblem ecProblem = new LombokProblem( + fileNameArray, message, 0, new String[0], + isWarning ? ProblemSeverities.Warning : ProblemSeverities.Error, + sourceStart, sourceEnd, lineNumber, columnNumber); + ast.compilationResult.record(ecProblem, null); + } + + private static class LombokProblem extends DefaultProblem { + private static final String MARKER_ID = "org.eclipse.jdt.apt.pluggable.core.compileProblem"; //$NON-NLS-1$ + + public LombokProblem(char[] originatingFileName, String message, int id, + String[] stringArguments, int severity, + int startPosition, int endPosition, int line, int column) { + super(originatingFileName, message, id, stringArguments, severity, startPosition, endPosition, line, column); + } + + @Override public int getCategoryID() { + return CAT_UNSPECIFIED; + } + + @Override public String getMarkerType() { + return MARKER_ID; + } + } +} diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java index 1620014c..49083df0 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java @@ -39,10 +39,15 @@ public class PatchDelegatePortal { } catch (IllegalAccessException e) { throw Lombok.sneakyThrow(e); } catch (InvocationTargetException e) { - throw Lombok.sneakyThrow(e); + throw Lombok.sneakyThrow(e.getCause()); } catch (NullPointerException e) { - e.initCause(Reflection.problem); - throw e; + if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { + e.initCause(Reflection.problem); + throw e; + } + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + return false; } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java index 1b244234..36d884b9 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java @@ -23,9 +23,6 @@ package lombok.eclipse.agent; import static lombok.eclipse.handlers.EclipseHandlerUtil.createAnnotation; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -41,39 +38,25 @@ import lombok.eclipse.TransformEclipseAST; import lombok.eclipse.handlers.EclipseHandlerUtil; import lombok.experimental.ExtensionMethod; -import org.eclipse.jdt.core.CompletionProposal; -import org.eclipse.jdt.internal.codeassist.InternalCompletionContext; -import org.eclipse.jdt.internal.codeassist.InternalCompletionProposal; -import org.eclipse.jdt.internal.codeassist.InternalExtendedCompletionContext; -import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMemberAccess; -import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedNameReference; -import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleNameReference; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.Expression; -import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.NameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; -import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; -import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; -import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; -import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; -import org.eclipse.jdt.internal.ui.text.java.AbstractJavaCompletionProposal; -import org.eclipse.jdt.ui.text.java.CompletionProposalCollector; public class PatchExtensionMethod { static class Extension { @@ -138,7 +121,7 @@ public class PatchExtensionMethod { return null; } - private static EclipseNode upToType(EclipseNode typeNode) { + static EclipseNode upToType(EclipseNode typeNode) { EclipseNode node = typeNode; do { node = node.up(); @@ -146,7 +129,7 @@ public class PatchExtensionMethod { return node; } - private static List getApplicableExtensionMethods(EclipseNode typeNode, Annotation ann, TypeBinding receiverType) { + static List getApplicableExtensionMethods(EclipseNode typeNode, Annotation ann, TypeBinding receiverType) { List extensions = new ArrayList(); if ((typeNode != null) && (ann != null) && (receiverType != null)) { BlockScope blockScope = ((TypeDeclaration) typeNode.get()).initializerScope; @@ -295,118 +278,7 @@ public class PatchExtensionMethod { return new QualifiedNameReference(sources, poss, source.sourceStart, source.sourceEnd); } } - - static List getExtensionMethods(CompletionProposalCollector completionProposalCollector) { - List extensions = new ArrayList(); - ClassScope classScope = getClassScope(completionProposalCollector); - if (classScope != null) { - TypeDeclaration decl = classScope.referenceContext; - TypeBinding firstParameterType = getFirstParameterType(decl, completionProposalCollector); - for (EclipseNode typeNode = getTypeNode(decl); typeNode != null; typeNode = upToType(typeNode)) { - Annotation ann = getAnnotation(ExtensionMethod.class, typeNode); - extensions.addAll(0, getApplicableExtensionMethods(typeNode, ann, firstParameterType)); - } - } - return extensions; - } - - private static ClassScope getClassScope(CompletionProposalCollector completionProposalCollector) { - ClassScope scope = null; - try { - InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector); - InternalExtendedCompletionContext extendedContext = (InternalExtendedCompletionContext) Reflection.extendedContextField.get(context); - if (extendedContext != null) { - Scope assistScope = ((Scope) Reflection.assistScopeField.get(extendedContext)); - if (assistScope != null) { - scope = assistScope.classScope(); - } - } - } catch (IllegalAccessException ignore) { - // ignore - } - return scope; - } - - private static TypeBinding getFirstParameterType(TypeDeclaration decl, CompletionProposalCollector completionProposalCollector) { - TypeBinding firstParameterType = null; - ASTNode node = getAssistNode(completionProposalCollector); - if (node == null) return null; - if (!(node instanceof CompletionOnQualifiedNameReference) && !(node instanceof CompletionOnSingleNameReference) && !(node instanceof CompletionOnMemberAccess)) return null; - - if (node instanceof NameReference) { - Binding binding = ((NameReference) node).binding; - if ((node instanceof SingleNameReference) && (((SingleNameReference) node).token.length == 0)) { - firstParameterType = decl.binding; - } else if (binding instanceof VariableBinding) { - firstParameterType = ((VariableBinding) binding).type; - } else if (binding instanceof TypeBinding) { - firstParameterType = (TypeBinding) binding; - } - } else if (node instanceof FieldReference) { - firstParameterType = ((FieldReference) node).actualReceiverType; - } - return firstParameterType; - } - - static ASTNode getAssistNode(CompletionProposalCollector completionProposalCollector) { - try { - InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector); - InternalExtendedCompletionContext extendedContext = (InternalExtendedCompletionContext) Reflection.extendedContextField.get(context); - if (extendedContext == null) return null; - return (ASTNode) Reflection.assistNodeField.get(extendedContext); - } catch (Exception ignore) { - return null; - } - } - - static class Reflection { - public static final Field replacementOffsetField; - public static final Field contextField; - public static final Field extendedContextField; - public static final Field assistNodeField; - public static final Field assistScopeField; - public static final Field lookupEnvironmentField; - public static final Field completionEngineField; - public static final Field nameLookupField; - public static final Method createJavaCompletionProposalMethod; - static { - replacementOffsetField = accessField(AbstractJavaCompletionProposal.class, "fReplacementOffset"); - contextField = accessField(CompletionProposalCollector.class, "fContext"); - extendedContextField = accessField(InternalCompletionContext.class, "extendedContext"); - assistNodeField = accessField(InternalExtendedCompletionContext.class, "assistNode"); - assistScopeField = accessField(InternalExtendedCompletionContext.class, "assistScope"); - lookupEnvironmentField = accessField(InternalExtendedCompletionContext.class, "lookupEnvironment"); - completionEngineField = accessField(InternalCompletionProposal.class, "completionEngine"); - nameLookupField = accessField(InternalCompletionProposal.class, "nameLookup"); - createJavaCompletionProposalMethod = accessMethod(CompletionProposalCollector.class, "createJavaCompletionProposal", CompletionProposal.class); - } - - static boolean isComplete() { - Object[] requiredFieldsAndMethods = { replacementOffsetField, contextField, extendedContextField, assistNodeField, assistScopeField, lookupEnvironmentField, completionEngineField, nameLookupField, createJavaCompletionProposalMethod }; - for (Object o : requiredFieldsAndMethods) if (o == null) return false; - return true; - } - - private static Field accessField(Class clazz, String fieldName) { - try { - return makeAccessible(clazz.getDeclaredField(fieldName)); - } catch (Exception e) { - return null; - } - } - - private static Method accessMethod(Class clazz, String methodName, Class parameter) { - try { - return makeAccessible(clazz.getDeclaredMethod(methodName, parameter)); - } catch (Exception e) { - return null; - } - } + - private static T makeAccessible(T object) { - object.setAccessible(true); - return object; - } - } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java index bd6e328c..701a2029 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java @@ -21,21 +21,41 @@ */ package lombok.eclipse.agent; +import static lombok.eclipse.agent.PatchExtensionMethod.*; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import lombok.eclipse.EclipseNode; import lombok.eclipse.agent.PatchExtensionMethod.Extension; -import lombok.eclipse.agent.PatchExtensionMethod.Reflection; +import lombok.experimental.ExtensionMethod; import org.eclipse.jdt.core.CompletionProposal; import org.eclipse.jdt.internal.codeassist.InternalCompletionContext; import org.eclipse.jdt.internal.codeassist.InternalCompletionProposal; import org.eclipse.jdt.internal.codeassist.InternalExtendedCompletionContext; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMemberAccess; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedNameReference; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleNameReference; import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.FieldReference; +import org.eclipse.jdt.internal.compiler.ast.NameReference; +import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.lookup.Binding; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.Scope; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; import org.eclipse.jdt.internal.core.SearchableEnvironment; +import org.eclipse.jdt.internal.ui.text.java.AbstractJavaCompletionProposal; import org.eclipse.jdt.ui.text.java.CompletionProposalCollector; import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal; @@ -49,11 +69,11 @@ public class PatchExtensionMethodCompletionProposal { if (canExtendCodeAssist(proposals)) { IJavaCompletionProposal firstProposal = proposals.get(0); int replacementOffset = getReplacementOffset(firstProposal); - for (Extension extension : PatchExtensionMethod.getExtensionMethods(completionProposalCollector)) { + for (Extension extension : getExtensionMethods(completionProposalCollector)) { for (MethodBinding method : extension.extensionMethods) { ExtensionMethodCompletionProposal newProposal = new ExtensionMethodCompletionProposal(replacementOffset); copyNameLookupAndCompletionEngine(completionProposalCollector, firstProposal, newProposal); - ASTNode node = PatchExtensionMethod.getAssistNode(completionProposalCollector); + ASTNode node = getAssistNode(completionProposalCollector); newProposal.setMethodBinding(method, node); createAndAddJavaCompletionProposal(completionProposalCollector, newProposal, proposals); } @@ -62,6 +82,70 @@ public class PatchExtensionMethodCompletionProposal { return proposals.toArray(new IJavaCompletionProposal[proposals.size()]); } + + private static List getExtensionMethods(CompletionProposalCollector completionProposalCollector) { + List extensions = new ArrayList(); + ClassScope classScope = getClassScope(completionProposalCollector); + if (classScope != null) { + TypeDeclaration decl = classScope.referenceContext; + TypeBinding firstParameterType = getFirstParameterType(decl, completionProposalCollector); + for (EclipseNode typeNode = getTypeNode(decl); typeNode != null; typeNode = upToType(typeNode)) { + Annotation ann = getAnnotation(ExtensionMethod.class, typeNode); + extensions.addAll(0, getApplicableExtensionMethods(typeNode, ann, firstParameterType)); + } + } + return extensions; + } + + static TypeBinding getFirstParameterType(TypeDeclaration decl, CompletionProposalCollector completionProposalCollector) { + TypeBinding firstParameterType = null; + ASTNode node = getAssistNode(completionProposalCollector); + if (node == null) return null; + if (!(node instanceof CompletionOnQualifiedNameReference) && !(node instanceof CompletionOnSingleNameReference) && !(node instanceof CompletionOnMemberAccess)) return null; + + if (node instanceof NameReference) { + Binding binding = ((NameReference) node).binding; + if ((node instanceof SingleNameReference) && (((SingleNameReference) node).token.length == 0)) { + firstParameterType = decl.binding; + } else if (binding instanceof VariableBinding) { + firstParameterType = ((VariableBinding) binding).type; + } else if (binding instanceof TypeBinding) { + firstParameterType = (TypeBinding) binding; + } + } else if (node instanceof FieldReference) { + firstParameterType = ((FieldReference) node).actualReceiverType; + } + return firstParameterType; + } + + private static ASTNode getAssistNode(CompletionProposalCollector completionProposalCollector) { + try { + InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector); + InternalExtendedCompletionContext extendedContext = (InternalExtendedCompletionContext) Reflection.extendedContextField.get(context); + if (extendedContext == null) return null; + return (ASTNode) Reflection.assistNodeField.get(extendedContext); + } catch (Exception ignore) { + return null; + } + } + + private static ClassScope getClassScope(CompletionProposalCollector completionProposalCollector) { + ClassScope scope = null; + try { + InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector); + InternalExtendedCompletionContext extendedContext = (InternalExtendedCompletionContext) Reflection.extendedContextField.get(context); + if (extendedContext != null) { + Scope assistScope = ((Scope) Reflection.assistScopeField.get(extendedContext)); + if (assistScope != null) { + scope = assistScope.classScope(); + } + } + } catch (IllegalAccessException ignore) { + // ignore + } + return scope; + } + private static void copyNameLookupAndCompletionEngine(CompletionProposalCollector completionProposalCollector, IJavaCompletionProposal proposal, InternalCompletionProposal newProposal) { @@ -97,4 +181,55 @@ public class PatchExtensionMethodCompletionProposal { return 0; } } + + static class Reflection { + public static final Field replacementOffsetField; + public static final Field contextField; + public static final Field extendedContextField; + public static final Field assistNodeField; + public static final Field assistScopeField; + public static final Field lookupEnvironmentField; + public static final Field completionEngineField; + public static final Field nameLookupField; + public static final Method createJavaCompletionProposalMethod; + + static { + replacementOffsetField = accessField(AbstractJavaCompletionProposal.class, "fReplacementOffset"); + contextField = accessField(CompletionProposalCollector.class, "fContext"); + extendedContextField = accessField(InternalCompletionContext.class, "extendedContext"); + assistNodeField = accessField(InternalExtendedCompletionContext.class, "assistNode"); + assistScopeField = accessField(InternalExtendedCompletionContext.class, "assistScope"); + lookupEnvironmentField = accessField(InternalExtendedCompletionContext.class, "lookupEnvironment"); + completionEngineField = accessField(InternalCompletionProposal.class, "completionEngine"); + nameLookupField = accessField(InternalCompletionProposal.class, "nameLookup"); + createJavaCompletionProposalMethod = accessMethod(CompletionProposalCollector.class, "createJavaCompletionProposal", CompletionProposal.class); + } + + static boolean isComplete() { + Object[] requiredFieldsAndMethods = { replacementOffsetField, contextField, extendedContextField, assistNodeField, assistScopeField, lookupEnvironmentField, completionEngineField, nameLookupField, createJavaCompletionProposalMethod }; + for (Object o : requiredFieldsAndMethods) if (o == null) return false; + return true; + } + + private static Field accessField(Class clazz, String fieldName) { + try { + return makeAccessible(clazz.getDeclaredField(fieldName)); + } catch (Exception e) { + return null; + } + } + + private static Method accessMethod(Class clazz, String methodName, Class parameter) { + try { + return makeAccessible(clazz.getDeclaredMethod(methodName, parameter)); + } catch (Exception e) { + return null; + } + } + + private static T makeAccessible(T object) { + object.setAccessible(true); + return object; + } + } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java index 29aedc81..6dca1901 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java @@ -24,10 +24,10 @@ package lombok.eclipse.agent; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal; - import lombok.Lombok; +import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal; + public class PatchExtensionMethodCompletionProposalPortal { private static final String COMPLETION_PROPOSAL_COLLECTOR = "org.eclipse.jdt.ui.text.java.CompletionProposalCollector"; @@ -44,10 +44,15 @@ public class PatchExtensionMethodCompletionProposalPortal { } catch (IllegalAccessException e) { throw Lombok.sneakyThrow(e); } catch (InvocationTargetException e) { - throw Lombok.sneakyThrow(e); + throw Lombok.sneakyThrow(e.getCause()); } catch (NullPointerException e) { - e.initCause(ReflectionForUi.problem); - throw e; + if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { + e.initCause(ReflectionForUi.problem); + throw e; + } + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + return (IJavaCompletionProposal[])javaCompletionProposals; } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodPortal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodPortal.java new file mode 100644 index 00000000..02e4e123 --- /dev/null +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodPortal.java @@ -0,0 +1,103 @@ +package lombok.eclipse.agent; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import lombok.Lombok; + +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; + +public class PatchExtensionMethodPortal { + private static final String TYPE_BINDING = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding"; + private static final String TYPE_BINDING_ARRAY = "[Lorg.eclipse.jdt.internal.compiler.lookup.TypeBinding;"; + private static final String MESSAGE_SEND = "org.eclipse.jdt.internal.compiler.ast.MessageSend"; + private static final String BLOCK_SCOPE = "org.eclipse.jdt.internal.compiler.lookup.BlockScope"; + private static final String METHOD_BINDING = "org.eclipse.jdt.internal.compiler.lookup.MethodBinding"; + private static final String PROBLEM_REPORTER = "org.eclipse.jdt.internal.compiler.problem.ProblemReporter"; + + public static TypeBinding resolveType(Object resolvedType, Object methodCall, Object scope) { + try { + return (TypeBinding) Reflection.resolveType.invoke(null, resolvedType, methodCall, scope); + } catch (NoClassDefFoundError e) { + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + return (TypeBinding) resolvedType; + } catch (IllegalAccessException e) { + throw Lombok.sneakyThrow(e); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e); + } catch (NullPointerException e) { + if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { + e.initCause(Reflection.problem); + throw e; + } + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + return (TypeBinding)resolvedType; + } + } + + public static void errorNoMethodFor(Object problemReporter, Object messageSend, Object recType, Object params) { + try { + Reflection.errorNoMethodFor.invoke(null, problemReporter, messageSend, recType, params); + } catch (NoClassDefFoundError e) { + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + } catch (IllegalAccessException e) { + throw Lombok.sneakyThrow(e); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e.getCause()); + } catch (NullPointerException e) { + if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { + e.initCause(Reflection.problem); + throw e; + } + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + } + } + + public static void invalidMethod(Object problemReporter, Object messageSend, Object method) { + try { + Reflection.invalidMethod.invoke(null, problemReporter, messageSend, method); + } catch (NoClassDefFoundError e) { + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + } catch (IllegalAccessException e) { + Lombok.sneakyThrow(e); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e.getCause()); + } catch (NullPointerException e) { + if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { + e.initCause(Reflection.problem); + throw e; + } + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + } + } + + private static final class Reflection { + public static final Method resolveType, errorNoMethodFor, invalidMethod; + public static final Throwable problem; + + static { + Method m = null, n = null, o = null; + Throwable problem_ = null; + try { + m = PatchExtensionMethod.class.getMethod("resolveType", Class.forName(TYPE_BINDING), Class.forName(MESSAGE_SEND), Class.forName(BLOCK_SCOPE)); + n = PatchExtensionMethod.class.getMethod("errorNoMethodFor", Class.forName(PROBLEM_REPORTER), + Class.forName(MESSAGE_SEND), Class.forName(TYPE_BINDING), Class.forName(TYPE_BINDING_ARRAY)); + o = PatchExtensionMethod.class.getMethod("invalidMethod", Class.forName(PROBLEM_REPORTER), Class.forName(MESSAGE_SEND), Class.forName(METHOD_BINDING)); + } catch (Throwable t) { + // That's problematic, but as long as no local classes are used we don't actually need it. + // Better fail on local classes than crash altogether. + problem_ = t; + } + resolveType = m; + errorNoMethodFor = n; + invalidMethod = o; + problem = problem_; + } + } +} diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipsePortal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipsePortal.java index dacd81b4..5342ecab 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipsePortal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipsePortal.java @@ -40,12 +40,16 @@ public class PatchValEclipsePortal { //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly //do anything useful here. } catch (IllegalAccessException e) { - Lombok.sneakyThrow(e); + throw Lombok.sneakyThrow(e); } catch (InvocationTargetException e) { - Lombok.sneakyThrow(e); + throw Lombok.sneakyThrow(e.getCause()); } catch (NullPointerException e) { - e.initCause(Reflection.problem); - throw e; + if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { + e.initCause(Reflection.problem); + throw e; + } + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. } } @@ -56,12 +60,16 @@ public class PatchValEclipsePortal { //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly //do anything useful here. } catch (IllegalAccessException e) { - Lombok.sneakyThrow(e); + throw Lombok.sneakyThrow(e); } catch (InvocationTargetException e) { - Lombok.sneakyThrow(e); + throw Lombok.sneakyThrow(e.getCause()); } catch (NullPointerException e) { - e.initCause(Reflection.problem); - throw e; + if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { + e.initCause(Reflection.problem); + throw e; + } + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. } } @@ -72,12 +80,16 @@ public class PatchValEclipsePortal { //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly //do anything useful here. } catch (IllegalAccessException e) { - Lombok.sneakyThrow(e); + throw Lombok.sneakyThrow(e); } catch (InvocationTargetException e) { - Lombok.sneakyThrow(e); + throw Lombok.sneakyThrow(e.getCause()); } catch (NullPointerException e) { - e.initCause(Reflection.problem); - throw e; + if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { + e.initCause(Reflection.problem); + throw e; + } + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. } } @@ -88,12 +100,16 @@ public class PatchValEclipsePortal { //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly //do anything useful here. } catch (IllegalAccessException e) { - Lombok.sneakyThrow(e); + throw Lombok.sneakyThrow(e); } catch (InvocationTargetException e) { - Lombok.sneakyThrow(e); + throw Lombok.sneakyThrow(e.getCause()); } catch (NullPointerException e) { - e.initCause(Reflection.problem); - throw e; + if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { + e.initCause(Reflection.problem); + throw e; + } + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. } } -- cgit