diff options
Diffstat (limited to 'src/eclipseAgent')
7 files changed, 458 insertions, 50 deletions
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java index 2a7a6d51..1c7de88b 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java +++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java @@ -104,6 +104,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { patchRetrieveEllipsisStartPosition(sm); patchRetrieveRightBraceOrSemiColonPosition(sm); patchRetrieveProperRightBracketPosition(sm); + patchRetrieveStartBlockPosition(sm); patchSetGeneratedFlag(sm); patchDomAstReparseIssues(sm); patchHideGeneratedNodes(sm); @@ -126,6 +127,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { patchExtensionMethod(sm, ecjOnly); patchRenameField(sm); patchNullCheck(sm); + patchJavadoc(sm); if (reloadExistingClasses) sm.reloadClasses(instrumentation); } @@ -414,6 +416,13 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build()); } + private static void patchRetrieveStartBlockPosition(ScriptManager sm) { + sm.addScript(ScriptBuilder.wrapReturnValue() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveStartBlockPosition")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveStartBlockPosition", "int", "int", "int")) + .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build()); + } + private static void patchRetrieveRightBraceOrSemiColonPosition(ScriptManager sm) { sm.addScript(ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "boolean", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration")) @@ -450,7 +459,15 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { sm.addScript(ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "extractSubArrayType", "org.eclipse.jdt.core.dom.ArrayType", "org.eclipse.jdt.core.dom.ArrayType", "int", "int")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveProperRightBracketPosition", "int", "int", "int")) - .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveProperRightBracketPosition", "int", "int", "org.eclipse.jdt.core.dom.ArrayType")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveProperRightBracketPosition", "int", "int", "org.eclipse.jdt.core.dom.Type")) + .requestExtra(StackRequest.PARAM1) + .transplant() + .build()); + + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToArray", "org.eclipse.jdt.core.dom.ArrayType", "org.eclipse.jdt.core.dom.Type", "int", "int", "int", "org.eclipse.jdt.internal.compiler.ast.Annotation[][]")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveProperRightBracketPosition", "int", "int", "int")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveProperRightBracketPosition", "int", "int", "org.eclipse.jdt.core.dom.Type")) .requestExtra(StackRequest.PARAM1) .transplant() .build()); @@ -507,6 +524,8 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToVariableDeclarationFragment", "org.eclipse.jdt.core.dom.VariableDeclarationFragment", "org.eclipse.jdt.internal.compiler.ast.FieldDeclaration")) .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToVariableDeclarationFragment", "org.eclipse.jdt.core.dom.VariableDeclarationFragment", "org.eclipse.jdt.internal.compiler.ast.LocalDeclaration")) .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToVariableDeclarationStatement", "org.eclipse.jdt.core.dom.VariableDeclarationStatement", "org.eclipse.jdt.internal.compiler.ast.LocalDeclaration")) + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "createBaseType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference", "long[]", "org.eclipse.jdt.internal.compiler.ast.Annotation[][]", "char[][]", "int", "int", "boolean")) + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "createQualifiedType", "org.eclipse.jdt.core.dom.QualifiedType", "org.eclipse.jdt.internal.compiler.ast.TypeReference", "long[]", "org.eclipse.jdt.internal.compiler.ast.Annotation[][]", "char[][]", "int", "org.eclipse.jdt.core.dom.Type")) /* Targets above are only patched because the resulting dom nodes should be marked if generated. */ .request(StackRequest.PARAM1, StackRequest.RETURN_VALUE) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", @@ -542,6 +561,43 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void", "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); + + + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.PrimitiveType", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) + .requestExtra(StackRequest.PARAM1) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", + "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) + .transplant() + .build()); + + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleType", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) + .requestExtra(StackRequest.PARAM1) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", + "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) + .transplant() + .build()); + + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ParameterizedType", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) + .requestExtra(StackRequest.PARAM1) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", + "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) + .transplant() + .build()); + + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.QualifiedType", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) + .requestExtra(StackRequest.PARAM1) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", + "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) + .transplant() + .build()); /* Set generated flag for QualifiedNames */ sm.addScript(ScriptBuilder.wrapMethodCall() @@ -850,5 +906,37 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .request(StackRequest.PARAM1) .transplant().build()); } + + private static void patchJavadoc(ScriptManager sm) { + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2", "getHTMLContent", "java.lang.String", "org.eclipse.jdt.core.IJavaElement", "boolean")) + .methodToWrap(new Hook("org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2", "getHTMLContentFromSource", "java.lang.String", "org.eclipse.jdt.core.IJavaElement")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Javadoc", "getHTMLContentFromSource", "java.lang.String", "java.lang.String", "org.eclipse.jdt.core.IJavaElement")) + .requestExtra(StackRequest.PARAM1) + .build()); + + /* This is an older version that uses IMember instead of IJavaElement */ + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2", "getHTMLContent", "java.lang.String", "org.eclipse.jdt.core.IMember", "boolean")) + .methodToWrap(new Hook("org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2", "getHTMLContentFromSource", "java.lang.String", "org.eclipse.jdt.core.IMember")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Javadoc", "getHTMLContentFromSource", "java.lang.String", "java.lang.String", "org.eclipse.jdt.core.IJavaElement")) + .requestExtra(StackRequest.PARAM1) + .build()); + + sm.addScript(ScriptBuilder.replaceMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.compiler.ast.TypeDeclaration", "printBody", "java.lang.StringBuffer", "int", "java.lang.StringBuffer")) + .methodToReplace(new Hook("org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration", "print", "java.lang.StringBuffer", "int", "java.lang.StringBuffer")) + .replacementMethod(new Hook("lombok.launch.PatchFixesHider$Javadoc", "printMethod", "java.lang.StringBuffer", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration", "int", "java.lang.StringBuffer", "org.eclipse.jdt.internal.compiler.ast.TypeDeclaration")) + .requestExtra(StackRequest.THIS) + .build()); + + sm.addScript(ScriptBuilder.addField() + .fieldName("$javadoc") + .fieldType("Ljava/util/Map;") + .setPublic() + .setTransient() + .targetClass("org.eclipse.jdt.internal.core.CompilationUnit") + .build()); + } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/ExtensionMethodCompletionProposal.java b/src/eclipseAgent/lombok/eclipse/agent/ExtensionMethodCompletionProposal.java index 46ce63f9..833a8226 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/ExtensionMethodCompletionProposal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/ExtensionMethodCompletionProposal.java @@ -22,10 +22,15 @@ package lombok.eclipse.agent; import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccStatic; +import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccVarargs; import java.util.Arrays; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.CompletionProposal; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.codeassist.CompletionEngine; import org.eclipse.jdt.internal.codeassist.InternalCompletionProposal; @@ -35,16 +40,29 @@ import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleNameRefere import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.core.NameLookup; public class ExtensionMethodCompletionProposal extends InternalCompletionProposal { + private char[] fullSignature; + private char[][] parameterNames; + public ExtensionMethodCompletionProposal(final int replacementOffset) { super(CompletionProposal.METHOD_REF, replacementOffset - 1); } public void setMethodBinding(final MethodBinding method, final ASTNode node) { + // Add proposal parameter names, sometimes its empty... + if (method.parameterNames != null && method.parameterNames.length > 0) { + setParameterNames(Arrays.copyOfRange(method.parameterNames, 1, method.parameterNames.length)); + } else { + // Copy signature for parameter name resolution, this is more reliable but slower + fullSignature = CompletionEngine.getSignature(method); + } + MethodBinding original = method.original(); TypeBinding[] parameters = Arrays.copyOf(method.parameters, method.parameters.length); method.parameters = Arrays.copyOfRange(method.parameters, 1, method.parameters.length); + TypeBinding[] originalParameters = null; if (original != method) { originalParameters = Arrays.copyOf(method.original().parameters, method.original().parameters.length); @@ -76,6 +94,10 @@ public class ExtensionMethodCompletionProposal extends InternalCompletionProposa setName(method.selector); setCompletion(completion); setFlags(method.modifiers & (~AccStatic)); + // Remove varargs flag if it is the only parameter + if (method.isVarargs() && length == 0) { + setFlags(getFlags() & (~AccVarargs)); + } int index = node.sourceEnd + 1; if (node instanceof CompletionOnQualifiedNameReference) { index -= ((CompletionOnQualifiedNameReference) node).completionIdentifier.length; @@ -96,4 +118,58 @@ public class ExtensionMethodCompletionProposal extends InternalCompletionProposa method.original().parameters = originalParameters; } } + + @Override + public char[][] findParameterNames(IProgressMonitor monitor) { + if (parameterNames != null) { + return parameterNames; + } + + NameLookup.Answer answer = this.nameLookup.findType( + new String(this.declarationTypeName), + new String(this.declarationPackageName), + false, + NameLookup.ACCEPT_CLASSES & NameLookup.ACCEPT_INTERFACES, + true/* consider secondary types */, + false/* do NOT wait for indexes */, + false/*don't check restrictions*/, + null); + + if (answer != null && answer.type != null) { + char[][] parameterTypes = Signature.getParameterTypes(fullSignature); + + String[] args = new String[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + args[i] = new String(parameterTypes[i]); + } + IMethod method = answer.type.getMethod(new String(this.getName()), args); + IMethod[] methods = answer.type.findMethods(method); + if (methods != null && methods.length > 0) { + method = methods[0]; + } + if (method != null) { + try { + String[] parameterNames = method.getParameterNames(); + char[][] parameterNamesAsChar = new char[parameterNames.length - 1][]; + for (int i = 0; i < parameterNamesAsChar.length; i++) { + parameterNamesAsChar[i] = parameterNames[i + 1].toCharArray(); + } + setParameterNames(parameterNamesAsChar); + } catch (JavaModelException e) { + // Nope + } + } + } + // Seems like we failed, fallback + if (parameterNames == null) { + parameterNames = super.findParameterNames(monitor); + } + return parameterNames; + } + + @Override + public void setParameterNames(char[][] parameterNames) { + this.parameterNames = parameterNames; + super.setParameterNames(parameterNames); + } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java index fcc76059..0b33ff0a 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java @@ -24,6 +24,7 @@ package lombok.eclipse.agent; import static lombok.eclipse.handlers.EclipseHandlerUtil.createAnnotation; import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -58,6 +59,7 @@ import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.Scope; @@ -250,6 +252,14 @@ public class PatchExtensionMethod { Binding binding = ((NameReference)methodCall.receiver).binding; if (binding instanceof TypeBinding) skip = true; } + // It's impossible to resolve the right method without types + if (Reflection.argumentsHaveErrors != null) { + try { + if ((Boolean) Reflection.argumentsHaveErrors.get(methodCall)) skip = true; + } catch (IllegalAccessException ignore) { + // ignore + } + } if (!skip) for (Extension extension : extensions) { if (!extension.suppressBaseMethods && !(methodCall.binding instanceof ProblemMethodBinding)) continue; @@ -262,13 +272,30 @@ public class PatchExtensionMethod { List<Expression> arguments = new ArrayList<Expression>(); arguments.add(methodCall.receiver); if (methodCall.arguments != null) arguments.addAll(Arrays.asList(methodCall.arguments)); + Expression[] originalArgs = methodCall.arguments; + methodCall.arguments = arguments.toArray(new Expression[0]); + List<TypeBinding> argumentTypes = new ArrayList<TypeBinding>(); for (Expression argument : arguments) { - if (argument.resolvedType != null) argumentTypes.add(argument.resolvedType); - // TODO: Instead of just skipping nulls entirely, there is probably a 'unresolved type' placeholder. THAT is what we ought to be adding here! + TypeBinding argumentType = argument.resolvedType; + if (argumentType == null && Reflection.isFunctionalExpression(argument)) { + argumentType = Reflection.getPolyTypeBinding(argument); + } + if (argumentType == null) { + argumentType = TypeBinding.NULL; + } + argumentTypes.add(argumentType); } - Expression[] originalArgs = methodCall.arguments; - methodCall.arguments = arguments.toArray(new Expression[0]); + + // Copy generic information. This one covers a few simple cases, more complex cases are still broken + int typeVariables = extensionMethod.typeVariables.length; + if (typeVariables > 0 && methodCall.receiver.resolvedType instanceof ParameterizedTypeBinding) { + ParameterizedTypeBinding parameterizedTypeBinding = (ParameterizedTypeBinding) methodCall.receiver.resolvedType; + if (parameterizedTypeBinding.arguments != null && parameterizedTypeBinding.arguments.length == typeVariables) { + methodCall.genericTypeArguments = parameterizedTypeBinding.arguments; + } + } + MethodBinding fixedBinding = scope.getMethod(extensionMethod.declaringClass, methodCall.selector, argumentTypes.toArray(new TypeBinding[0]), methodCall); if (fixedBinding instanceof ProblemMethodBinding) { methodCall.arguments = originalArgs; @@ -276,18 +303,33 @@ public class PatchExtensionMethod { PostponedInvalidMethodError.invoke(scope.problemReporter(), methodCall, fixedBinding, scope); } } else { + // If the extension method uses varargs, the last fixed binding parameter is an array but + // the method arguments are not. Even thought we already know that the method is fine we still + // have to compare each parameter with the type of the array to support autoboxing/unboxing. + boolean isVarargs = fixedBinding.isVarargs(); for (int i = 0, iend = arguments.size(); i < iend; i++) { Expression arg = arguments.get(i); - if (fixedBinding.parameters[i].isArrayType() != arg.resolvedType.isArrayType()) break; - if (arg instanceof MessageSend) { - ((MessageSend) arg).valueCast = arg.resolvedType; + TypeBinding[] parameters = fixedBinding.parameters; + TypeBinding param; + if (isVarargs && i >= parameters.length - 1) { + // Extract the array element type for all vararg arguments + param = parameters[parameters.length - 1].leafComponentType(); + } else { + param = parameters[i]; + } + // Resolve types for lambdas + if (Reflection.isFunctionalExpression(arg)) { + arg.setExpectedType(param); + arg.resolveType(scope); } - if (!fixedBinding.parameters[i].isBaseType() && arg.resolvedType.isBaseType()) { - int id = arg.resolvedType.id; - arg.implicitConversion = TypeIds.BOXING | (id + (id << 4)); // magic see TypeIds - } else if (fixedBinding.parameters[i].isBaseType() && !arg.resolvedType.isBaseType()) { - int id = fixedBinding.parameters[i].id; - arg.implicitConversion = TypeIds.UNBOXING | (id + (id << 4)); // magic see TypeIds + if (arg.resolvedType != null) { + if (!param.isBaseType() && arg.resolvedType.isBaseType()) { + int id = arg.resolvedType.id; + arg.implicitConversion = TypeIds.BOXING | (id + (id << 4)); // magic see TypeIds + } else if (param.isBaseType() && !arg.resolvedType.isBaseType()) { + int id = parameters[i].id; + arg.implicitConversion = TypeIds.UNBOXING | (id + (id << 4)); // magic see TypeIds + } } } @@ -295,6 +337,7 @@ public class PatchExtensionMethod { methodCall.actualReceiverType = extensionMethod.declaringClass; methodCall.binding = fixedBinding; methodCall.resolvedType = methodCall.binding.returnType; + methodCall.statementEnd = methodCall.sourceEnd; if (Reflection.argumentTypes != null) { try { Reflection.argumentTypes.set(methodCall, argumentTypes.toArray(new TypeBinding[0])); @@ -340,16 +383,41 @@ public class PatchExtensionMethod { } private static final class Reflection { - public static final Field argumentTypes; + public static final Field argumentTypes = Permit.permissiveGetField(MessageSend.class, "argumentTypes"); + public static final Field argumentsHaveErrors = Permit.permissiveGetField(MessageSend.class, "argumentsHaveErrors"); + private static final Class<?> functionalExpression; + private static final Constructor<?> polyTypeBindingConstructor; static { - Field a = null; + Class<?> a = null; + Constructor<?> b = null; try { - a = Permit.getField(MessageSend.class, "argumentTypes"); - } catch (Throwable t) { - //ignore - old eclipse versions don't know this one + a = Class.forName("org.eclipse.jdt.internal.compiler.ast.FunctionalExpression"); + } catch (Exception e) { + // Ignore + } + try { + b = Permit.getConstructor(Class.forName("org.eclipse.jdt.internal.compiler.lookup.PolyTypeBinding"), Expression.class); + } catch (Exception e) { + // Ignore + } + functionalExpression = a; + polyTypeBindingConstructor = b; + } + + public static boolean isFunctionalExpression(Expression expression) { + if (functionalExpression == null) return false; + return functionalExpression.isInstance(expression); + } + + public static TypeBinding getPolyTypeBinding(Expression expression) { + if (polyTypeBindingConstructor == null) return null; + try { + return (TypeBinding) polyTypeBindingConstructor.newInstance(expression); + } catch (Exception e) { + // Ignore } - argumentTypes = a; + return null; } } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java index 085c903f..08a42d1c 100755 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java @@ -65,13 +65,14 @@ public class PatchExtensionMethodCompletionProposal { CompletionProposalCollector completionProposalCollector) { List<IJavaCompletionProposal> proposals = new ArrayList<IJavaCompletionProposal>(Arrays.asList(javaCompletionProposals)); - if (canExtendCodeAssist(proposals)) { - IJavaCompletionProposal firstProposal = proposals.get(0); - int replacementOffset = getReplacementOffset(firstProposal); + if (canExtendCodeAssist()) { for (Extension extension : getExtensionMethods(completionProposalCollector)) { for (MethodBinding method : extension.extensionMethods) { - ExtensionMethodCompletionProposal newProposal = new ExtensionMethodCompletionProposal(replacementOffset); - copyNameLookupAndCompletionEngine(completionProposalCollector, firstProposal, newProposal); + if (!isMatchingProposal(method, completionProposalCollector)) { + continue; + } + ExtensionMethodCompletionProposal newProposal = new ExtensionMethodCompletionProposal(0); + copyNameLookupAndCompletionEngine(completionProposalCollector, newProposal); ASTNode node = getAssistNode(completionProposalCollector); newProposal.setMethodBinding(method, node); createAndAddJavaCompletionProposal(completionProposalCollector, newProposal, proposals); @@ -81,7 +82,6 @@ public class PatchExtensionMethodCompletionProposal { return proposals.toArray(new IJavaCompletionProposal[0]); } - private static List<Extension> getExtensionMethods(CompletionProposalCollector completionProposalCollector) { List<Extension> extensions = new ArrayList<Extension>(); ClassScope classScope = getClassScope(completionProposalCollector); @@ -96,6 +96,17 @@ public class PatchExtensionMethodCompletionProposal { return extensions; } + private static boolean isMatchingProposal(MethodBinding method, CompletionProposalCollector completionProposalCollector) { + try { + InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector); + String searchToken = new String(context.getToken()); + String extensionMethodName = new String(method.selector); + return extensionMethodName.contains(searchToken); + } catch (IllegalAccessException e) { + return true; + } + } + static TypeBinding getFirstParameterType(TypeDeclaration decl, CompletionProposalCollector completionProposalCollector) { TypeBinding firstParameterType = null; ASTNode node = getAssistNode(completionProposalCollector); @@ -149,8 +160,7 @@ public class PatchExtensionMethodCompletionProposal { return scope; } - private static void copyNameLookupAndCompletionEngine(CompletionProposalCollector completionProposalCollector, IJavaCompletionProposal proposal, - InternalCompletionProposal newProposal) { + private static void copyNameLookupAndCompletionEngine(CompletionProposalCollector completionProposalCollector, InternalCompletionProposal newProposal) { try { InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector); @@ -173,17 +183,10 @@ public class PatchExtensionMethodCompletionProposal { } } - private static boolean canExtendCodeAssist(List<IJavaCompletionProposal> proposals) { - return !proposals.isEmpty() && Reflection.isComplete(); - } - - private static int getReplacementOffset(Object proposal) { - try { - return Reflection.replacementOffsetField.getInt(proposal); - } catch (Exception ignore) { - return 0; - } + private static boolean canExtendCodeAssist() { + return Reflection.isComplete(); } + static class Reflection { public static final Field replacementOffsetField; diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchJavadoc.java b/src/eclipseAgent/lombok/eclipse/agent/PatchJavadoc.java new file mode 100644 index 00000000..a91e4d8b --- /dev/null +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchJavadoc.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2020 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.eclipse.agent; + +import static lombok.eclipse.EclipseAugments.CompilationUnit_javadoc; + +import java.lang.reflect.Method; +import java.util.Map; + +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.core.CompilationUnit; +import org.eclipse.jdt.internal.core.SourceMethod; +import org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2; + +import lombok.eclipse.EclipseAugments; +import lombok.eclipse.handlers.EclipseHandlerUtil; +import lombok.permit.Permit; + +public class PatchJavadoc { + + public static String getHTMLContentFromSource(String original, IJavaElement member) { + if (original != null) { + return original; + } + + if (member instanceof SourceMethod) { + SourceMethod sourceMethod = (SourceMethod) member; + ICompilationUnit iCompilationUnit = sourceMethod.getCompilationUnit(); + if (iCompilationUnit instanceof CompilationUnit) { + CompilationUnit compilationUnit = (CompilationUnit) iCompilationUnit; + Map<String, String> docs = EclipseAugments.CompilationUnit_javadoc.get(compilationUnit); + + String signature = getSignature(sourceMethod); + String rawJavadoc = docs.get(signature); + if (rawJavadoc == null) return null; + + return Reflection.javadoc2HTML((IMember) member, member, rawJavadoc); + } + } + + return null; + } + + public static StringBuffer printMethod(AbstractMethodDeclaration methodDeclaration, Integer tab, StringBuffer output, TypeDeclaration type) { + if (methodDeclaration.compilationResult.compilationUnit instanceof CompilationUnit) { + Map<String, String> docs = CompilationUnit_javadoc.get((CompilationUnit) methodDeclaration.compilationResult.compilationUnit); + if (docs != null) { + String signature = EclipseHandlerUtil.getSignature(type, methodDeclaration); + String rawJavadoc = docs.get(signature); + if (rawJavadoc != null) { + for (String line : rawJavadoc.split("\r?\n")) { + ASTNode.printIndent(tab, output).append(line).append("\n"); + } + } + } + } + return methodDeclaration.print(tab, output); + } + + private static String getSignature(SourceMethod sourceMethod) { + StringBuilder sb = new StringBuilder(); + sb.append(sourceMethod.getParent().getElementName()); + sb.append("."); + sb.append(sourceMethod.getElementName()); + sb.append("("); + for (String type : sourceMethod.getParameterTypes()) { + sb.append(type); + } + sb.append(")"); + + return sb.toString(); + } + + /** + * The method <code>javadoc2HTML</code> changed 2014-12 to accept an + * additional IJavaElement parameter. To support older versions, try to + * find that one too. + */ + private static class Reflection { + private static final Method javadoc2HTML; + private static final Method oldJavadoc2HTML; + static { + Method a = null, b = null; + + try { + a = Permit.getMethod(JavadocContentAccess2.class, "javadoc2HTML", IMember.class, IJavaElement.class, String.class); + } catch (Throwable t) {} + try { + b = Permit.getMethod(JavadocContentAccess2.class, "javadoc2HTML", IMember.class, String.class); + } catch (Throwable t) {} + + javadoc2HTML = a; + oldJavadoc2HTML = b; + } + + private static String javadoc2HTML(IMember member, IJavaElement element, String rawJavadoc) { + if (javadoc2HTML != null) { + try { + return (String) javadoc2HTML.invoke(null, member, element, rawJavadoc); + } catch (Throwable t) { + return null; + } + } + if (oldJavadoc2HTML != null) { + try { + return (String) oldJavadoc2HTML.invoke(null, member, rawJavadoc); + } catch (Throwable t) { + return null; + } + } + return null; + } + } +} diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java index 056852c8..f22e78a8 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java @@ -35,11 +35,13 @@ import org.eclipse.jdt.internal.compiler.ast.FunctionalExpression; import org.eclipse.jdt.internal.compiler.ast.ImportReference; import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; import org.eclipse.jdt.internal.compiler.lookup.Binding; @@ -227,14 +229,16 @@ public class PatchVal { boolean var = isVar(local, scope); if (!(val || var)) return false; - StackTraceElement[] st = new Throwable().getStackTrace(); - for (int i = 0; i < st.length - 2 && i < 10; i++) { - if (st[i].getClassName().equals("lombok.launch.PatchFixesHider$Val")) { - boolean valInForStatement = val && - st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") && - st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement"); - if (valInForStatement) return false; - break; + if (val) { + StackTraceElement[] st = new Throwable().getStackTrace(); + for (int i = 0; i < st.length - 2 && i < 10; i++) { + if (st[i].getClassName().equals("lombok.launch.PatchFixesHider$Val")) { + boolean valInForStatement = + st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") && + st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement"); + if (valInForStatement) return false; + break; + } } } @@ -264,6 +268,7 @@ public class PatchVal { } TypeBinding resolved = null; + Constant oldConstant = init.constant; try { resolved = decomponent ? getForEachComponentType(init, scope) : resolveForExpression(init, scope); } catch (NullPointerException e) { @@ -280,6 +285,10 @@ public class PatchVal { } catch (Exception e) { // Some type thing failed. } + } else { + if (init instanceof MessageSend && ((MessageSend) init).actualReceiverType == null) { + init.constant = oldConstant; + } } } diff --git a/src/eclipseAgent/lombok/launch/PatchFixesHider.java b/src/eclipseAgent/lombok/launch/PatchFixesHider.java index 708824dd..76f30527 100755 --- a/src/eclipseAgent/lombok/launch/PatchFixesHider.java +++ b/src/eclipseAgent/lombok/launch/PatchFixesHider.java @@ -35,12 +35,13 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IAnnotatable; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.core.dom.ArrayType; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; @@ -50,6 +51,7 @@ import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.MessageSend; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.Scope; @@ -303,6 +305,26 @@ final class PatchFixesHider { } } + /** Contains patch code to support Javadoc for generated methods */ + public static final class Javadoc { + private static final Method GET_HTML; + private static final Method PRINT_METHOD; + + static { + Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchJavadoc"); + GET_HTML = Util.findMethod(shadowed, "getHTMLContentFromSource", String.class, IJavaElement.class); + PRINT_METHOD = Util.findMethod(shadowed, "printMethod", AbstractMethodDeclaration.class, Integer.class, StringBuffer.class, TypeDeclaration.class); + } + + public static String getHTMLContentFromSource(String original, IJavaElement member) { + return (String) Util.invokeMethod(GET_HTML, original, member); + } + + public static StringBuffer printMethod(AbstractMethodDeclaration methodDeclaration, int tab, StringBuffer output, TypeDeclaration type) { + return (StringBuffer) Util.invokeMethod(PRINT_METHOD, methodDeclaration, tab, output, type); + } + } + /** * Contains a mix of methods: ecj only, ecj+eclipse, and eclipse only. As a consequence, _EVERY_ method from here used for ecj MUST be * transplanted, as ecj itself cannot load this class (signatures refer to things that don't exist in ecj-only mode). @@ -503,6 +525,10 @@ final class PatchFixesHider { return original == -1 ? end : original; } + public static int fixRetrieveStartBlockPosition(int original, int start) { + return original == -1 ? start : original; + } + public static int fixRetrieveRightBraceOrSemiColonPosition(int original, int end) { // if (original == -1) { // Thread.dumpStack(); @@ -524,9 +550,9 @@ final class PatchFixesHider { return -1; } - public static int fixRetrieveProperRightBracketPosition(int retVal, ArrayType arrayType) { - if (retVal != -1 || arrayType == null) return retVal; - if (isGenerated(arrayType)) return arrayType.getStartPosition() + arrayType.getLength() - 1; + public static int fixRetrieveProperRightBracketPosition(int retVal, Type type) { + if (retVal != -1 || type == null) return retVal; + if (isGenerated(type)) return type.getStartPosition() + type.getLength() - 1; return -1; } |