diff options
Diffstat (limited to 'src/eclipseAgent/lombok')
9 files changed, 331 insertions, 97 deletions
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java index 6e5871e6..a6d745b6 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java +++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java @@ -23,12 +23,16 @@ package lombok.eclipse.agent; import static lombok.patcher.scripts.ScriptBuilder.*; +import java.io.File; import java.lang.instrument.Instrumentation; +import java.net.URLClassLoader; +import java.security.ProtectionDomain; import java.util.Collection; import java.util.Collections; import java.util.List; import lombok.core.AgentLauncher; +import lombok.patcher.Filter; import lombok.patcher.Hook; import lombok.patcher.MethodTarget; import lombok.patcher.ScriptManager; @@ -74,9 +78,19 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { private static void registerPatchScripts(Instrumentation instrumentation, boolean reloadExistingClasses, boolean ecjOnly, Class<?> launchingContext) { ScriptManager sm = new ScriptManager(); sm.registerTransformer(instrumentation); + sm.setFilter(new Filter() { + @Override public boolean shouldTransform(ClassLoader loader, String className, Class<?> classBeingDefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { + if (!(loader instanceof URLClassLoader)) return true; + ClassLoader parent = loader.getParent(); + if (parent == null) return true; + return !parent.getClass().getName().startsWith("org.eclipse.jdt.apt.core.internal.AnnotationProcessorFactoryLoader"); + } + }); + + final boolean forceBaseResourceNames = shouldForceBaseResourceNames(); sm.setTransplantMapper(new TransplantMapper() { public String mapResourceName(int classFileFormatVersion, String resourceName) { - if (classFileFormatVersion < 50) return resourceName; + if (classFileFormatVersion < 50 || forceBaseResourceNames) return resourceName; return "Class50/" + resourceName; } }); @@ -92,7 +106,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { patchHideGeneratedNodes(sm); patchPostCompileHookEclipse(sm); patchFixSourceTypeConverter(sm); - patchDisableLombokForCodeFormatterAndCleanup(sm); + patchDisableLombokForCodeCleanup(sm); patchListRewriteHandleGeneratedMethods(sm); patchSyntaxAndOccurrencesHighlighting(sm); patchSortMembersOperation(sm); @@ -107,10 +121,37 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { patchLombokizeAST(sm); patchEcjTransformers(sm, ecjOnly); patchExtensionMethod(sm, ecjOnly); + patchRenameField(sm); if (reloadExistingClasses) sm.reloadClasses(instrumentation); } + private static boolean shouldForceBaseResourceNames() { + String shadowOverride = System.getProperty("shadow.override.lombok", ""); + if (shadowOverride == null || shadowOverride.length() == 0) return false; + for (String part : shadowOverride.split("\\s*" + (File.pathSeparatorChar == ';' ? ";" : ":") + "\\s*")) { + if (part.equalsIgnoreCase("lombok.jar")) return false; + } + return true; + } + + private static void patchRenameField(ScriptManager sm) { + /* RefactoringSearchEngine.search will not return results when renaming field and Data Annotation is present. Return a fake Element to make checks pass */ + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.RenameFieldProcessor", "checkAccessorDeclarations", "org.eclipse.ltk.core.refactoring.RefactoringStatus", "org.eclipse.core.runtime.IProgressMonitor", "org.eclipse.jdt.core.IMethod")) + .methodToWrap(new Hook("org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine", "search", "org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup[]", "org.eclipse.jdt.core.search.SearchPattern","org.eclipse.jdt.core.search.IJavaSearchScope","org.eclipse.core.runtime.IProgressMonitor","org.eclipse.ltk.core.refactoring.RefactoringStatus")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "createFakeSearchResult", "org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup[]", "org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup[]", "java.lang.Object")) + .requestExtra(StackRequest.THIS) + .transplant().build()); + + /* Filter search results which are Generated and based on Fields, e.g. Generated getters/setters */ + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.RenameFieldProcessor", "addAccessorOccurrences", "void", "org.eclipse.core.runtime.IProgressMonitor", "org.eclipse.jdt.core.IMethod", "java.lang.String","java.lang.String","org.eclipse.ltk.core.refactoring.RefactoringStatus")) + .methodToWrap(new Hook("org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup", "getSearchResults", "org.eclipse.jdt.core.search.SearchMatch[]")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGenerated", "org.eclipse.jdt.core.search.SearchMatch[]", "org.eclipse.jdt.core.search.SearchMatch[]")) + .transplant().build()); + } + private static void patchExtractInterface(ScriptManager sm) { /* Fix sourceEnding for generated nodes to avoid null pointer */ sm.addScript(ScriptBuilder.wrapMethodCall() @@ -183,13 +224,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .build()); } - private static void patchDisableLombokForCodeFormatterAndCleanup(ScriptManager sm) { - sm.addScript(ScriptBuilder.setSymbolDuringMethodCall() - .target(new MethodTarget("org.eclipse.jdt.internal.formatter.DefaultCodeFormatter", "formatCompilationUnit")) - .callToWrap(new Hook("org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil", "parseCompilationUnit", "org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration", "char[]", "java.util.Map", "boolean")) - .symbol("lombok.disable") - .build()); - + private static void patchDisableLombokForCodeCleanup(ScriptManager sm) { sm.addScript(ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.ControlStatementsFix$ControlStatementFinder", "visit", "boolean", "org.eclipse.jdt.core.dom.DoStatement")) .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.ControlStatementsFix$ControlStatementFinder", "visit", "boolean", "org.eclipse.jdt.core.dom.EnhancedForStatement")) @@ -308,18 +343,19 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchFormatters(ScriptManager sm) { + // before Eclipse Mars sm.addScript(ScriptBuilder.setSymbolDuringMethodCall() - .target(new MethodTarget("org.eclipse.jdt.internal.ui.text.java.JavaFormattingStrategy", "format", "void")) - .callToWrap(new Hook("org.eclipse.jdt.internal.corext.util.CodeFormatterUtil", "reformat", "org.eclipse.text.edits.TextEdit", - "int", "java.lang.String", "int", "int", "int", "java.lang.String", "java.util.Map")) - .symbol("lombok.disable").build()); + .target(new MethodTarget("org.eclipse.jdt.internal.formatter.DefaultCodeFormatter", "formatCompilationUnit")) + .callToWrap(new Hook("org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil", "parseCompilationUnit", "org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration", "char[]", "java.util.Map", "boolean")) + .symbol("lombok.disable") + .build()); + // Eclipse Mars and beyond sm.addScript(ScriptBuilder.setSymbolDuringMethodCall() - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeFormatFix", "createCleanUp", "org.eclipse.jdt.ui.cleanup.ICleanUpFix", - "org.eclipse.jdt.core.ICompilationUnit", "org.eclipse.jface.text.IRegion[]", "boolean", "boolean", "boolean", "boolean")) - .callToWrap(new Hook("org.eclipse.jdt.internal.corext.util.CodeFormatterUtil", "reformat", "org.eclipse.text.edits.TextEdit", - "int", "java.lang.String", "int", "java.lang.String", "java.util.Map")) - .symbol("lombok.disable").build()); + .target(new MethodTarget("org.eclipse.jdt.internal.formatter.DefaultCodeFormatter", "parseSourceCode")) + .callToWrap(new Hook("org.eclipse.jdt.core.dom.ASTParser", "createAST", "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.core.runtime.IProgressMonitor")) + .symbol("lombok.disable") + .build()); } private static void patchRefactorScripts(ScriptManager sm) { @@ -368,11 +404,35 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchRetrieveRightBraceOrSemiColonPosition(ScriptManager sm) { - sm.addScript(ScriptBuilder.wrapReturnValue() - .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBraceOrSemiColonPosition")) - .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBrace")) - .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "int")) - .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build()); + 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")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBraceOrSemiColonPosition", "int", "int", "int")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration")) + .requestExtra(StackRequest.PARAM2) + .transplant() + .build()); + + 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")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBrace", "int", "int", "int")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration")) + .requestExtra(StackRequest.PARAM2) + .transplant() + .build()); + + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.FieldDeclaration")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBrace", "int", "int", "int")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "org.eclipse.jdt.internal.compiler.ast.FieldDeclaration")) + .requestExtra(StackRequest.PARAM1) + .transplant() + .build()); + +// sm.addScript(ScriptBuilder.wrapReturnValue() +// .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBraceOrSemiColonPosition")) +// .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBrace")) +// .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "int")) +// .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build()); } private static void patchSetGeneratedFlag(ScriptManager sm) { @@ -670,6 +730,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { final String PATCH_EXTENSIONMETHOD_COMPLETIONPROPOSAL_PORTAL = "lombok.eclipse.agent.PatchExtensionMethodCompletionProposalPortal"; final String MESSAGE_SEND_SIG = "org.eclipse.jdt.internal.compiler.ast.MessageSend"; final String TYPE_BINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding"; + final String SCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.Scope"; final String BLOCK_SCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.BlockScope"; final String TYPE_BINDINGS_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding[]"; final String PROBLEM_REPORTER_SIG = "org.eclipse.jdt.internal.compiler.problem.ProblemReporter"; @@ -697,6 +758,13 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "invalidMethod", "void", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, METHOD_BINDING_SIG)) .build()); + // Since eclipse mars; they added a param. + sm.addScript(replaceMethodCall() + .target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG)) + .methodToReplace(new Hook(PROBLEM_REPORTER_SIG, "invalidMethod", "void", MESSAGE_SEND_SIG, METHOD_BINDING_SIG, SCOPE_SIG)) + .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "invalidMethod", "void", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, METHOD_BINDING_SIG, SCOPE_SIG)) + .build()); + if (!ecj) { sm.addScript(wrapReturnValue() .target(new MethodTarget(COMPLETION_PROPOSAL_COLLECTOR_SIG, "getJavaCompletionProposals", I_JAVA_COMPLETION_PROPOSAL_SIG)) diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java index b1f5a43a..02760e35 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java @@ -699,7 +699,10 @@ public class PatchDelegate { } private static void addAllMethodBindings0(List<BindingTuple> list, TypeBinding binding, Set<String> banList, char[] fieldName, ASTNode responsible) throws DelegateRecursion { - if (binding instanceof SourceTypeBinding) ((SourceTypeBinding) binding).scope.environment().globalOptions.storeAnnotations = true; + if (binding instanceof SourceTypeBinding) { + ClassScope scope = ((SourceTypeBinding) binding).scope; + if (scope != null) scope.environment().globalOptions.storeAnnotations = true; + } if (binding == null) return; TypeBinding inner; @@ -721,42 +724,44 @@ public class PatchDelegate { } } - if (binding instanceof ReferenceBinding) { - ReferenceBinding rb = (ReferenceBinding) binding; - MethodBinding[] availableMethods = rb.availableMethods(); - FieldBinding[] availableFields = rb.availableFields(); - failIfContainsAnnotation(binding, availableMethods); - failIfContainsAnnotation(binding, availableFields); - - MethodBinding[] parameterizedSigs = availableMethods; - MethodBinding[] baseSigs = parameterizedSigs; - if (binding instanceof ParameterizedTypeBinding) { - baseSigs = ((ParameterizedTypeBinding)binding).genericType().availableMethods(); - if (baseSigs.length != parameterizedSigs.length) { - // The last known state of eclipse source says this can't happen, so we rely on it, - // but if this invariant is broken, better to go with 'arg0' naming instead of crashing. - baseSigs = parameterizedSigs; - } - } - for (int i = 0; i < parameterizedSigs.length; i++) { - MethodBinding mb = parameterizedSigs[i]; - String sig = printSig(mb); - if (mb.isStatic()) continue; - if (mb.isBridge()) continue; - if (mb.isConstructor()) continue; - if (mb.isDefaultAbstract()) continue; - if (!mb.isPublic()) continue; - if (mb.isSynthetic()) continue; - if (!banList.add(sig)) continue; // If add returns false, it was already in there. - BindingTuple pair = new BindingTuple(mb, baseSigs[i], fieldName, responsible); - list.add(pair); - } - addAllMethodBindings0(list, rb.superclass(), banList, fieldName, responsible); - ReferenceBinding[] interfaces = rb.superInterfaces(); - if (interfaces != null) { - for (ReferenceBinding iface : interfaces) addAllMethodBindings0(list, iface, banList, fieldName, responsible); + if (!(binding instanceof ReferenceBinding)) { + return; + } + + ReferenceBinding rb = (ReferenceBinding) binding; + MethodBinding[] availableMethods = rb.availableMethods(); + FieldBinding[] availableFields = rb.availableFields(); + failIfContainsAnnotation(binding, availableMethods); + failIfContainsAnnotation(binding, availableFields); + + MethodBinding[] parameterizedSigs = availableMethods; + MethodBinding[] baseSigs = parameterizedSigs; + if (binding instanceof ParameterizedTypeBinding) { + baseSigs = ((ParameterizedTypeBinding)binding).genericType().availableMethods(); + if (baseSigs.length != parameterizedSigs.length) { + // The last known state of eclipse source says this can't happen, so we rely on it, + // but if this invariant is broken, better to go with 'arg0' naming instead of crashing. + baseSigs = parameterizedSigs; } } + for (int i = 0; i < parameterizedSigs.length; i++) { + MethodBinding mb = parameterizedSigs[i]; + String sig = printSig(mb); + if (mb.isStatic()) continue; + if (mb.isBridge()) continue; + if (mb.isConstructor()) continue; + if (mb.isDefaultAbstract()) continue; + if (!mb.isPublic()) continue; + if (mb.isSynthetic()) continue; + if (!banList.add(sig)) continue; // If add returns false, it was already in there. + BindingTuple pair = new BindingTuple(mb, baseSigs[i], fieldName, responsible); + list.add(pair); + } + addAllMethodBindings0(list, rb.superclass(), banList, fieldName, responsible); + ReferenceBinding[] interfaces = rb.superInterfaces(); + if (interfaces != null) { + for (ReferenceBinding iface : interfaces) addAllMethodBindings0(list, iface, banList, fieldName, responsible); + } } private static final char[] STRING_LOMBOK = new char[] {'l', 'o', 'm', 'b', 'o', 'k'}; diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java index ca0933fb..5d586dff 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java @@ -24,6 +24,8 @@ package lombok.eclipse.agent; import static lombok.eclipse.handlers.EclipseHandlerUtil.createAnnotation; import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -56,6 +58,7 @@ 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.problem.ProblemReporter; @@ -89,16 +92,47 @@ public class PatchExtensionMethod { private final ProblemReporter problemReporter; private final WeakReference<MessageSend> messageSendRef; private final MethodBinding method; + private final Scope scope; - PostponedInvalidMethodError(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) { + private static final Method shortMethod = getMethod("invalidMethod", MessageSend.class, MethodBinding.class); + private static final Method longMethod = getMethod("invalidMethod", MessageSend.class, MethodBinding.class, Scope.class); + + private static Method getMethod(String name, Class<?>... types) { + try { + Method m = ProblemReporter.class.getMethod(name, types); + m.setAccessible(true); + return m; + } catch (Exception e) { + return null; + } + } + + PostponedInvalidMethodError(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) { this.problemReporter = problemReporter; this.messageSendRef = new WeakReference<MessageSend>(messageSend); this.method = method; + this.scope = scope; + } + + static void invoke(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) { + if (messageSend != null) { + try { + if (shortMethod != null) shortMethod.invoke(problemReporter, messageSend, method); + else if (longMethod != null) longMethod.invoke(problemReporter, messageSend, method, scope); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + Throwable t = e.getCause(); + if (t instanceof Error) throw (Error) t; + if (t instanceof RuntimeException) throw (RuntimeException) t; + throw new RuntimeException(t); + } + } } public void fire() { MessageSend messageSend = messageSendRef.get(); - if (messageSend != null) problemReporter.invalidMethod(messageSend, method); + invoke(problemReporter, messageSend, method, scope); } } @@ -185,7 +219,11 @@ public class PatchExtensionMethod { } public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) { - MessageSend_postponedErrors.set(messageSend, new PostponedInvalidMethodError(problemReporter, messageSend, method)); + MessageSend_postponedErrors.set(messageSend, new PostponedInvalidMethodError(problemReporter, messageSend, method, null)); + } + + public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) { + MessageSend_postponedErrors.set(messageSend, new PostponedInvalidMethodError(problemReporter, messageSend, method, scope)); } public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) { @@ -233,7 +271,7 @@ public class PatchExtensionMethod { if (fixedBinding instanceof ProblemMethodBinding) { methodCall.arguments = originalArgs; if (fixedBinding.declaringClass != null) { - scope.problemReporter().invalidMethod(methodCall, fixedBinding); + PostponedInvalidMethodError.invoke(scope.problemReporter(), methodCall, fixedBinding, scope); } } else { for (int i = 0, iend = arguments.size(); i < iend; i++) { diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java index 97ca5a7e..c11a49cd 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java @@ -60,8 +60,6 @@ import org.eclipse.jdt.ui.text.java.CompletionProposalCollector; import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal; public class PatchExtensionMethodCompletionProposal { - - public static IJavaCompletionProposal[] getJavaCompletionProposals(IJavaCompletionProposal[] javaCompletionProposals, CompletionProposalCollector completionProposalCollector) { @@ -178,7 +176,7 @@ public class PatchExtensionMethodCompletionProposal { return !proposals.isEmpty() && Reflection.isComplete(); } - private static int getReplacementOffset(IJavaCompletionProposal proposal) { + private static int getReplacementOffset(Object proposal) { try { return Reflection.replacementOffsetField.getInt(proposal); } catch (Exception ignore) { diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java index 6dca1901..19e1952e 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java @@ -33,7 +33,6 @@ public class PatchExtensionMethodCompletionProposalPortal { private static final String COMPLETION_PROPOSAL_COLLECTOR = "org.eclipse.jdt.ui.text.java.CompletionProposalCollector"; private static final String I_JAVA_COMPLETION_PROPOSAL_ARRAY = "[Lorg.eclipse.jdt.ui.text.java.IJavaCompletionProposal;"; - public static IJavaCompletionProposal[] getJavaCompletionProposals(Object[] javaCompletionProposals, Object completionProposalCollector) { try { return (IJavaCompletionProposal[]) ReflectionForUi.getJavaCompletionProposals.invoke(null, javaCompletionProposals, completionProposalCollector); @@ -52,7 +51,7 @@ public class PatchExtensionMethodCompletionProposalPortal { } //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly //do anything useful here. - return (IJavaCompletionProposal[])javaCompletionProposals; + return (IJavaCompletionProposal[]) javaCompletionProposals; } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java b/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java index 6685b6bb..52f63765 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java @@ -32,7 +32,7 @@ import lombok.core.Version; public class PatchFixesShadowLoaded { public static String addLombokNotesToEclipseAboutDialog(String origReturnValue, String key) { if ("aboutText".equals(key)) { - return origReturnValue + "\n\nLombok " + Version.getFullVersion() + " is installed. http://projectlombok.org/"; + return origReturnValue + "\n\nLombok " + Version.getFullVersion() + " is installed. https://projectlombok.org/"; } return origReturnValue; } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java index 30574ea6..3da37869 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2011 The Project Lombok Authors. + * Copyright (C) 2010-2018 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 @@ -21,11 +21,6 @@ */ package lombok.eclipse.agent; -import static lombok.eclipse.handlers.EclipseHandlerUtil.*; -import static lombok.eclipse.Eclipse.*; - -import java.lang.reflect.Field; - import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; @@ -43,6 +38,11 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; +import java.lang.reflect.Field; + +import static lombok.eclipse.Eclipse.poss; +import static lombok.eclipse.handlers.EclipseHandlerUtil.makeType; + public class PatchVal { // This is half of the work for 'val' support - the other half is in PatchValEclipse. This half is enough for ecj. @@ -57,6 +57,9 @@ public class PatchVal { return expr.resolveType(scope); } catch (NullPointerException e) { return null; + } catch (ArrayIndexOutOfBoundsException e) { + // This will occur internally due to for example 'val x = mth("X");', where mth takes 2 arguments. + return null; } } @@ -66,6 +69,9 @@ public class PatchVal { return expr.resolveType(scope); } catch (NullPointerException e) { return null; + } catch (ArrayIndexOutOfBoundsException e) { + // This will occur internally due to for example 'val x = mth("X");', where mth takes 2 arguments. + return null; } } @@ -78,31 +84,44 @@ public class PatchVal { return true; } - public static boolean couldBeVal(TypeReference ref) { + public static boolean couldBe(String key, TypeReference ref) { + String[] keyParts = key.split("\\."); if (ref instanceof SingleTypeReference) { char[] token = ((SingleTypeReference)ref).token; - return matches("val", token); + return matches(keyParts[keyParts.length - 1], token); } if (ref instanceof QualifiedTypeReference) { char[][] tokens = ((QualifiedTypeReference)ref).tokens; - if (tokens == null || tokens.length != 2) return false; - return matches("lombok", tokens[0]) && matches("val", tokens[1]); + if (keyParts.length != tokens.length) return false; + for(int i = 0; i < tokens.length; ++i) { + String part = keyParts[i]; + char[] token = tokens[i]; + if (!matches(part, token)) return false; + } + return true; } return false; } - - private static boolean isVal(TypeReference ref, BlockScope scope) { - if (!couldBeVal(ref)) return false; - + + private static boolean is(TypeReference ref, BlockScope scope, String key) { + if (!couldBe(key, ref)) return false; + TypeBinding resolvedType = ref.resolvedType; if (resolvedType == null) resolvedType = ref.resolveType(scope, false); if (resolvedType == null) return false; char[] pkg = resolvedType.qualifiedPackageName(); char[] nm = resolvedType.qualifiedSourceName(); - return matches("lombok", pkg) && matches("val", nm); + int pkgFullLength = pkg.length > 0 ? pkg.length + 1: 0; + char[] fullName = new char[pkgFullLength + nm.length]; + if(pkg.length > 0) { + System.arraycopy(pkg, 0, fullName, 0, pkg.length); + fullName[pkg.length] = '.'; + } + System.arraycopy(nm, 0, fullName, pkgFullLength, nm.length); + return matches(key, fullName); } public static final class Reflection { @@ -126,13 +145,17 @@ public class PatchVal { if (local == null || !LocalDeclaration.class.equals(local.getClass())) return false; boolean decomponent = false; - if (!isVal(local.type, scope)) return false; + boolean val = isVal(local, scope); + 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")) { - if (st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") && - st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement")) return false; + 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; } } @@ -164,7 +187,7 @@ public class PatchVal { TypeBinding resolved = null; try { - resolved = decomponent ? getForEachComponentType(init, scope) : init.resolveType(scope); + resolved = decomponent ? getForEachComponentType(init, scope) : resolveForExpression(init, scope); } catch (NullPointerException e) { // This definitely occurs if as part of resolving the initializer expression, a // lambda expression in it must also be resolved (such as when lambdas are part of @@ -181,23 +204,33 @@ public class PatchVal { } } - local.modifiers |= ClassFileConstants.AccFinal; + if(val) local.modifiers |= ClassFileConstants.AccFinal; local.annotations = addValAnnotation(local.annotations, local.type, scope); local.type = replacement != null ? replacement : new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(local.type, 3)); return false; } + private static boolean isVar(LocalDeclaration local, BlockScope scope) { + return is(local.type, scope, "lombok.experimental.var") || is(local.type, scope, "lombok.var"); + } + + private static boolean isVal(LocalDeclaration local, BlockScope scope) { + return is(local.type, scope, "lombok.val"); + } + public static boolean handleValForForEach(ForeachStatement forEach, BlockScope scope) { if (forEach.elementVariable == null) return false; - if (!isVal(forEach.elementVariable.type, scope)) return false; + boolean val = isVal(forEach.elementVariable, scope); + boolean var = isVar(forEach.elementVariable, scope); + if (!(val || var)) return false; TypeBinding component = getForEachComponentType(forEach.collection, scope); if (component == null) return false; TypeReference replacement = makeType(component, forEach.elementVariable.type, false); - forEach.elementVariable.modifiers |= ClassFileConstants.AccFinal; + if (val) forEach.elementVariable.modifiers |= ClassFileConstants.AccFinal; forEach.elementVariable.annotations = addValAnnotation(forEach.elementVariable.annotations, forEach.elementVariable.type, scope); forEach.elementVariable.type = replacement != null ? replacement : new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(forEach.elementVariable.type, 3)); @@ -222,7 +255,7 @@ public class PatchVal { private static TypeBinding getForEachComponentType(Expression collection, BlockScope scope) { if (collection != null) { TypeBinding resolved = collection.resolvedType; - if (resolved == null) resolved = collection.resolveType(scope); + if (resolved == null) resolved = resolveForExpression(collection, scope); if (resolved == null) return null; if (resolved.isArrayType()) { resolved = ((ArrayBinding) resolved).elementsType(); @@ -250,4 +283,13 @@ public class PatchVal { return null; } + + private static TypeBinding resolveForExpression(Expression collection, BlockScope scope) { + try { + return collection.resolveType(scope); + } catch (ArrayIndexOutOfBoundsException e) { + // Known cause of issues; for example: val e = mth("X"), where mth takes 2 arguments. + return null; + } + } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java index 7d5f36f4..46237dcf 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2011 The Project Lombok Authors. + * Copyright (C) 2010-2018 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 @@ -47,6 +47,7 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.parser.Parser; @@ -65,7 +66,9 @@ public class PatchValEclipse { ForeachStatement foreachDecl = (ForeachStatement) astStack[astPtr]; ASTNode init = foreachDecl.collection; if (init == null) return; - if (foreachDecl.elementVariable == null || !PatchVal.couldBeVal(foreachDecl.elementVariable.type)) return; + boolean val = couldBeVal(foreachDecl.elementVariable.type); + boolean var = couldBeVar(foreachDecl.elementVariable.type); + if (foreachDecl.elementVariable == null || !(val || var)) return; try { if (Reflection.iterableCopyField != null) Reflection.iterableCopyField.set(foreachDecl.elementVariable, init); @@ -88,7 +91,9 @@ public class PatchValEclipse { if (!(variableDecl instanceof LocalDeclaration)) return; ASTNode init = variableDecl.initialization; if (init == null) return; - if (!PatchVal.couldBeVal(variableDecl.type)) return; + boolean val = couldBeVal(variableDecl.type); + boolean var = couldBeVar(variableDecl.type); + if (!(val || var)) return; try { if (Reflection.initCopyField != null) Reflection.initCopyField.set(variableDecl, init); @@ -97,6 +102,10 @@ public class PatchValEclipse { } } + private static boolean couldBeVar(TypeReference type) { + return PatchVal.couldBe("lombok.experimental.var", type) || PatchVal.couldBe("lombok.var", type); + } + public static void addFinalAndValAnnotationToSingleVariableDeclaration(Object converter, SingleVariableDeclaration out, LocalDeclaration in) { @SuppressWarnings("unchecked") List<IExtendedModifier> modifiers = out.modifiers(); addFinalAndValAnnotationToModifierList(converter, modifiers, out.getAST(), in); @@ -115,7 +124,7 @@ public class PatchValEclipse { Annotation valAnnotation = null; for (Annotation ann : in.annotations) { - if (PatchVal.couldBeVal(ann.type)) { + if (couldBeVal(ann.type)) { found = true; valAnnotation = ann; break; @@ -167,6 +176,10 @@ public class PatchValEclipse { } } + private static boolean couldBeVal(TypeReference type) { + return PatchVal.couldBe("lombok.val", type); + } + public static Modifier createModifier(AST ast, ModifierKeyword keyword, int start, int end) { Modifier modifier = null; try { diff --git a/src/eclipseAgent/lombok/launch/PatchFixesHider.java b/src/eclipseAgent/lombok/launch/PatchFixesHider.java index 2472ca3c..b1d73352 100644 --- a/src/eclipseAgent/lombok/launch/PatchFixesHider.java +++ b/src/eclipseAgent/lombok/launch/PatchFixesHider.java @@ -36,25 +36,32 @@ import lombok.eclipse.EclipseAugments; 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.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.search.SearchMatch; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.Expression; +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.lookup.BlockScope; 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.parser.Parser; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; +import org.eclipse.jdt.internal.core.SourceField; import org.eclipse.jdt.internal.core.dom.rewrite.NodeRewriteEvent; import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent; import org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner; +import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup; import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil; /** These contain a mix of the following: @@ -258,13 +265,14 @@ final class PatchFixesHider { public static final class ExtensionMethod { private static final Method RESOLVE_TYPE; private static final Method ERROR_NO_METHOD_FOR; - private static final Method INVALID_METHOD; + private static final Method INVALID_METHOD, INVALID_METHOD2; static { Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchExtensionMethod"); RESOLVE_TYPE = Util.findMethod(shadowed, "resolveType", TypeBinding.class, MessageSend.class, BlockScope.class); ERROR_NO_METHOD_FOR = Util.findMethod(shadowed, "errorNoMethodFor", ProblemReporter.class, MessageSend.class, TypeBinding.class, TypeBinding[].class); INVALID_METHOD = Util.findMethod(shadowed, "invalidMethod", ProblemReporter.class, MessageSend.class, MethodBinding.class); + INVALID_METHOD2 = Util.findMethod(shadowed, "invalidMethod", ProblemReporter.class, MessageSend.class, MethodBinding.class, Scope.class); } public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) { @@ -278,6 +286,10 @@ final class PatchFixesHider { public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) { Util.invokeMethod(INVALID_METHOD, problemReporter, messageSend, method); } + + public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) { + Util.invokeMethod(INVALID_METHOD2, problemReporter, messageSend, method, scope); + } } /** @@ -469,7 +481,24 @@ final class PatchFixesHider { } public static int fixRetrieveRightBraceOrSemiColonPosition(int original, int end) { - return original == -1 ? end : original; // Need to fix: see issue 325. +// if (original == -1) { +// Thread.dumpStack(); +// } + return original == -1 ? end : original; + } + + public static int fixRetrieveRightBraceOrSemiColonPosition(int retVal, AbstractMethodDeclaration amd) { + if (retVal != -1 || amd == null) return retVal; + boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(amd) != null; + if (isGenerated) return amd.declarationSourceEnd; + return -1; + } + + public static int fixRetrieveRightBraceOrSemiColonPosition(int retVal, FieldDeclaration fd) { + if (retVal != -1 || fd == null) return retVal; + boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(fd) != null; + if (isGenerated) return fd.declarationSourceEnd; + return -1; } public static final int ALREADY_PROCESSED_FLAG = 0x800000; //Bit 24 @@ -540,6 +569,48 @@ final class PatchFixesHider { return result.size() == methods.length ? methods : result.toArray(new IMethod[result.size()]); } + public static SearchMatch[] removeGenerated(SearchMatch[] returnValue) { + List<SearchMatch> result = new ArrayList<SearchMatch>(); + for (int j = 0; j < returnValue.length; j++) { + SearchMatch searchResult = returnValue[j]; + if (searchResult.getElement() instanceof IField) { + IField field = (IField) searchResult.getElement(); + + // can not check for value=lombok because annotation is + // not fully resolved + IAnnotation annotation = field.getAnnotation("Generated"); + if (annotation != null) { + // Method generated at field location, skip + continue; + } + + } + result.add(searchResult); + } + return result.toArray(new SearchMatch[result.size()]); + } + + public static SearchResultGroup[] createFakeSearchResult(SearchResultGroup[] returnValue, + Object/* + * org.eclipse.jdt.internal.corext.refactoring.rename. + * RenameFieldProcessor + */ processor) throws Exception { + if (returnValue == null || returnValue.length == 0) { + // if no matches were found, check if Data annotation is present on the class + Field declaredField = processor.getClass().getDeclaredField("fField"); + if (declaredField != null) { + declaredField.setAccessible(true); + SourceField fField = (SourceField) declaredField.get(processor); + IAnnotation dataAnnotation = fField.getDeclaringType().getAnnotation("Data"); + if (dataAnnotation != null) { + // add fake item, to make refactoring checks pass + return new SearchResultGroup[] {new SearchResultGroup(null, new SearchMatch[1])}; + } + } + } + return returnValue; + } + public static SimpleName[] removeGeneratedSimpleNames(SimpleName[] in) throws Exception { Field f = SimpleName.class.getField("$isGenerated"); |