diff options
author | Reinier Zwitserloot <reinier@tipit.to> | 2009-06-20 23:46:17 +0200 |
---|---|---|
committer | Reinier Zwitserloot <reinier@tipit.to> | 2009-06-21 00:10:58 +0200 |
commit | 255bd5907176cb5d2c4fb1cf6a9b48b14af0b4ba (patch) | |
tree | 347c4b1cf5b34b3975cc00e26a42c75ee1295b76 /src/lombok | |
parent | 2e8e43a12e21151ff470a2729373b4af4980d113 (diff) | |
download | lombok-255bd5907176cb5d2c4fb1cf6a9b48b14af0b4ba.tar.gz lombok-255bd5907176cb5d2c4fb1cf6a9b48b14af0b4ba.tar.bz2 lombok-255bd5907176cb5d2c4fb1cf6a9b48b14af0b4ba.zip |
Due to a java bug, constants in enums don't work, so instead the default access level for @Getter and @Setter have now just been hardcoded in GetterHandler and SetterHandler.
Added ability to look up the Node object for any given AST object on Node itself, as you don't usually have the AST object.
Added toString() method generating to @Data, and this required some fancy footwork in finding if we've already generated methods, and editing a generated method to fill in binding and type resolutions. HandleGetter and HandleSetter have been updated to use these features.
Exceptions caused by lombok handlers show up in the eclipse error log, but now, if they are related to a CompilationUnit, also as a problem (error) on the CUD - those error log entries are easy to miss!
Our ASTs can now be appended to. When you generate a new AST node, you should add it to the AST, obviously. Getter/Setter have been updated to use this.
Diffstat (limited to 'src/lombok')
-rw-r--r-- | src/lombok/Getter.java | 2 | ||||
-rw-r--r-- | src/lombok/Setter.java | 2 | ||||
-rw-r--r-- | src/lombok/core/AST.java | 19 | ||||
-rw-r--r-- | src/lombok/eclipse/Eclipse.java | 23 | ||||
-rw-r--r-- | src/lombok/eclipse/EclipseAST.java | 70 | ||||
-rw-r--r-- | src/lombok/eclipse/EclipseASTVisitor.java | 4 | ||||
-rw-r--r-- | src/lombok/eclipse/HandlerLibrary.java | 12 | ||||
-rw-r--r-- | src/lombok/eclipse/TransformEclipseAST.java | 11 | ||||
-rw-r--r-- | src/lombok/eclipse/handlers/HandleData.java | 127 | ||||
-rw-r--r-- | src/lombok/eclipse/handlers/HandleGetter.java | 66 | ||||
-rw-r--r-- | src/lombok/eclipse/handlers/HandleSetter.java | 92 | ||||
-rw-r--r-- | src/lombok/eclipse/handlers/PKG.java | 64 | ||||
-rw-r--r-- | src/lombok/javac/JavacAST.java | 30 | ||||
-rw-r--r-- | src/lombok/javac/handlers/HandleGetter.java | 23 | ||||
-rw-r--r-- | src/lombok/javac/handlers/HandleSetter.java | 25 | ||||
-rw-r--r-- | src/lombok/javac/handlers/PKG.java | 12 |
16 files changed, 450 insertions, 132 deletions
diff --git a/src/lombok/Getter.java b/src/lombok/Getter.java index 7510bf24..d59ea672 100644 --- a/src/lombok/Getter.java +++ b/src/lombok/Getter.java @@ -8,7 +8,5 @@ import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface Getter { - lombok.AccessLevel DEFAULT_ACCESS_LEVEL = lombok.AccessLevel.PUBLIC; - lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC; } diff --git a/src/lombok/Setter.java b/src/lombok/Setter.java index acd1d180..b00d4158 100644 --- a/src/lombok/Setter.java +++ b/src/lombok/Setter.java @@ -8,7 +8,5 @@ import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface Setter { - lombok.AccessLevel DEFAULT_ACCESS_LEVEL = lombok.AccessLevel.PUBLIC; - lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC; } diff --git a/src/lombok/core/AST.java b/src/lombok/core/AST.java index 2229f9ef..c1186d24 100644 --- a/src/lombok/core/AST.java +++ b/src/lombok/core/AST.java @@ -97,6 +97,10 @@ public abstract class AST<N> { protected abstract boolean calculateIsStructurallySignificant(); + public Node getNodeFor(N obj) { + return AST.this.get(obj); + } + public N get() { return node; } @@ -150,6 +154,19 @@ public abstract class AST<N> { return fileName; } + public Node add(N newChild, Kind kind) { + Node n = buildTree(newChild, kind); + if ( n == null ) return null; + n.parent = this; + return n; + } + + public Node recursiveSetHandled() { + this.handled = true; + for ( Node child : children ) child.recursiveSetHandled(); + return this; + } + public abstract void addError(String message); public abstract void addWarning(String message); @@ -164,6 +181,8 @@ public abstract class AST<N> { } } + protected abstract Node buildTree(N item, Kind kind); + protected static class FieldAccess { public final Field field; public final int dim; diff --git a/src/lombok/eclipse/Eclipse.java b/src/lombok/eclipse/Eclipse.java index baac26a9..a7286058 100644 --- a/src/lombok/eclipse/Eclipse.java +++ b/src/lombok/eclipse/Eclipse.java @@ -19,37 +19,45 @@ import org.eclipse.core.runtime.ILog; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; 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.Literal; import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; +import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.TypeReference; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.osgi.framework.Bundle; public class Eclipse { + public static final int ECLIPSE_DO_NOT_TOUCH_FLAG = ASTNode.Bit24; private Eclipse() { //Prevent instantiation } private static final String DEFAULT_BUNDLE = "org.eclipse.jdt.core"; - public static void error(String message) { - error(message, DEFAULT_BUNDLE, null); + public static final TypeReference TYPEREF_JAVA_LANG_STRING = new QualifiedTypeReference( + TypeConstants.JAVA_LANG_STRING, new long[] {0, 0, 0}); + + public static void error(CompilationUnitDeclaration cud, String message) { + error(cud, message, DEFAULT_BUNDLE, null); } - public static void error(String message, Throwable error) { - error(message, DEFAULT_BUNDLE, error); + public static void error(CompilationUnitDeclaration cud, String message, Throwable error) { + error(cud, message, DEFAULT_BUNDLE, error); } - public static void error(String message, String bundleName) { - error(message, bundleName, null); + public static void error(CompilationUnitDeclaration cud, String message, String bundleName) { + error(cud, message, bundleName, null); } - public static void error(String message, String bundleName, Throwable error) { + public static void error(CompilationUnitDeclaration cud, String message, String bundleName, Throwable error) { Bundle bundle = Platform.getBundle(bundleName); if ( bundle == null ) { System.err.printf("Can't find bundle %s while trying to report error:\n%s\n", bundleName, message); @@ -59,6 +67,7 @@ public class Eclipse { ILog log = Platform.getLog(bundle); log.log(new Status(IStatus.ERROR, bundleName, message, error)); + if ( cud != null ) EclipseAST.addProblemToCompilationResult(cud, false, message + " - See error log.", 0, 0); } static String toQualifiedName(char[][] typeName) { diff --git a/src/lombok/eclipse/EclipseAST.java b/src/lombok/eclipse/EclipseAST.java index 6c4ce211..850fb8dc 100644 --- a/src/lombok/eclipse/EclipseAST.java +++ b/src/lombok/eclipse/EclipseAST.java @@ -70,21 +70,19 @@ public class EclipseAST extends AST<ASTNode> { private class ParseProblem { final boolean isWarning; final String message; - final Node node; final int sourceStart; final int sourceEnd; - public ParseProblem(boolean isWarning, String message, Node node, int sourceStart, int sourceEnd) { + public ParseProblem(boolean isWarning, String message, int sourceStart, int sourceEnd) { this.isWarning = isWarning; this.message = message; - this.node = node; this.sourceStart = sourceStart; this.sourceEnd = sourceEnd; } void addToCompilationResult() { - addProblemToCompilationResult(getFileName(), (CompilationUnitDeclaration) top().get(), - isWarning, message, node.get(), sourceStart, sourceEnd); + addProblemToCompilationResult((CompilationUnitDeclaration) top().get(), + isWarning, message, sourceStart, sourceEnd); } } @@ -103,9 +101,11 @@ public class EclipseAST extends AST<ASTNode> { propagateProblems(); } - static void addProblemToCompilationResult(String fileName, CompilationUnitDeclaration ast, - boolean isWarning, String message, ASTNode node, int sourceStart, int sourceEnd) { - char[] fileNameArray = fileName.toCharArray(); + 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; @@ -218,7 +218,7 @@ public class EclipseAST extends AST<ASTNode> { } public void addError(String message, int sourceStart, int sourceEnd) { - addProblem(new ParseProblem(false, message, this, sourceStart, sourceEnd)); + addProblem(new ParseProblem(false, message, sourceStart, sourceEnd)); } @Override public void addWarning(String message) { @@ -226,7 +226,7 @@ public class EclipseAST extends AST<ASTNode> { } public void addWarning(String message, int sourceStart, int sourceEnd) { - addProblem(new ParseProblem(true, message, this, sourceStart, sourceEnd)); + addProblem(new ParseProblem(true, message, sourceStart, sourceEnd)); } /** {@inheritDoc} */ @@ -244,6 +244,11 @@ public class EclipseAST extends AST<ASTNode> { } /** {@inheritDoc} */ + @Override public Node getNodeFor(ASTNode obj) { + return (Node) super.getNodeFor(obj); + } + + /** {@inheritDoc} */ public Node directUp() { return (Node) super.directUp(); } @@ -300,6 +305,31 @@ public class EclipseAST extends AST<ASTNode> { return (unit.bits & ASTNode.HasAllMethodBodies) > 0; } + @Override protected Node buildTree(ASTNode node, Kind kind) { + switch ( kind ) { + case COMPILATION_UNIT: + return buildCompilationUnit((CompilationUnitDeclaration) node); + case TYPE: + return buildType((TypeDeclaration) node); + case FIELD: + return buildField((FieldDeclaration) node); + case INITIALIZER: + return buildInitializer((Initializer) node); + case METHOD: + return buildMethod((AbstractMethodDeclaration) node); + case ARGUMENT: + return buildLocal((Argument) node, kind); + case LOCAL: + return buildLocal((LocalDeclaration) node, kind); + case STATEMENT: + return buildStatement((Statement) node); + case ANNOTATION: + return buildAnnotation((Annotation) node); + default: + throw new AssertionError("Did not expect to arrive here: " + kind); + } + } + private Node buildCompilationUnit(CompilationUnitDeclaration top) { Collection<Node> children = buildTypes(top.types); return putInMap(new Node(top, children, Kind.COMPILATION_UNIT)); @@ -373,30 +403,32 @@ public class EclipseAST extends AST<ASTNode> { if ( children == null ) return Collections.emptyList(); List<Node> childNodes = new ArrayList<Node>(); for ( LocalDeclaration local : children ) { - addIfNotNull(childNodes, buildLocal(local)); + addIfNotNull(childNodes, buildLocal(local, Kind.ARGUMENT)); } return childNodes; } - private Node buildLocal(LocalDeclaration local) { + private Node buildLocal(LocalDeclaration local, Kind kind) { if ( alreadyHandled(local) ) return null; List<Node> childNodes = new ArrayList<Node>(); addIfNotNull(childNodes, buildStatement(local.initialization)); childNodes.addAll(buildAnnotations(local.annotations)); - return putInMap(new Node(local, childNodes, Kind.LOCAL)); + return putInMap(new Node(local, childNodes, kind)); } private Collection<Node> buildAnnotations(Annotation[] annotations) { if ( annotations == null ) return Collections.emptyList(); List<Node> elements = new ArrayList<Node>(); - for ( Annotation an : annotations ) { - if ( an == null ) continue; - if ( alreadyHandled(an) ) continue; - elements.add(putInMap(new Node(an, null, Kind.ANNOTATION))); - } + for ( Annotation an : annotations ) addIfNotNull(elements, buildAnnotation(an)); return elements; } + private Node buildAnnotation(Annotation annotation) { + if ( annotation == null ) return null; + if ( alreadyHandled(annotation) ) return null; + return putInMap(new Node(annotation, null, Kind.ANNOTATION)); + } + private Collection<Node> buildStatements(Statement[] children) { if ( children == null ) return Collections.emptyList(); List<Node> childNodes = new ArrayList<Node>(); @@ -409,7 +441,7 @@ public class EclipseAST extends AST<ASTNode> { if ( child == null || alreadyHandled(child) ) return null; if ( child instanceof TypeDeclaration ) return buildType((TypeDeclaration)child); - if ( child instanceof LocalDeclaration ) return buildLocal((LocalDeclaration)child); + if ( child instanceof LocalDeclaration ) return buildLocal((LocalDeclaration)child, Kind.LOCAL); //We drill down because LocalDeclarations and TypeDeclarations can occur anywhere, even in, say, //an if block, or even the expression on an assert statement! diff --git a/src/lombok/eclipse/EclipseASTVisitor.java b/src/lombok/eclipse/EclipseASTVisitor.java index d5fece8f..aac0d8ed 100644 --- a/src/lombok/eclipse/EclipseASTVisitor.java +++ b/src/lombok/eclipse/EclipseASTVisitor.java @@ -122,8 +122,8 @@ public interface EclipseASTVisitor { } @Override public void visitCompilationUnit(Node node, CompilationUnitDeclaration unit) { - System.out.println("---------------------------------------------------------"); - System.out.println(node.isCompleteParse() ? "COMPLETE" : "incomplete"); + out.println("---------------------------------------------------------"); + out.println(node.isCompleteParse() ? "COMPLETE" : "incomplete"); print("<CUD %s>", node.getFileName()); indent++; diff --git a/src/lombok/eclipse/HandlerLibrary.java b/src/lombok/eclipse/HandlerLibrary.java index 39864e8e..10180963 100644 --- a/src/lombok/eclipse/HandlerLibrary.java +++ b/src/lombok/eclipse/HandlerLibrary.java @@ -63,11 +63,11 @@ public class HandlerLibrary { SpiLoadUtil.findAnnotationClass(handler.getClass(), EclipseAnnotationHandler.class); AnnotationHandlerContainer<?> container = new AnnotationHandlerContainer(handler, annotationClass); if ( lib.annotationHandlers.put(container.annotationClass.getName(), container) != null ) { - Eclipse.error("Duplicate handlers for annotation type: " + container.annotationClass.getName()); + Eclipse.error(null, "Duplicate handlers for annotation type: " + container.annotationClass.getName()); } lib.typeLibrary.addType(container.annotationClass.getName()); } catch ( ServiceConfigurationError e ) { - Eclipse.error("Can't load Lombok annotation handler for eclipse: ", e); + Eclipse.error(null, "Can't load Lombok annotation handler for eclipse: ", e); } } } @@ -78,7 +78,7 @@ public class HandlerLibrary { try { lib.visitorHandlers.add(it.next()); } catch ( ServiceConfigurationError e ) { - Eclipse.error("Can't load Lombok visitor handler for eclipse: ", e); + Eclipse.error(null, "Can't load Lombok visitor handler for eclipse: ", e); } } } @@ -94,6 +94,7 @@ public class HandlerLibrary { boolean handled = false; for ( String fqn : resolver.findTypeMatches(annotationNode, toQualifiedName(annotation.type.getTypeName())) ) { AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn); + if ( container == null ) continue; try { @@ -101,7 +102,7 @@ public class HandlerLibrary { } catch ( AnnotationValueDecodeFail fail ) { fail.owner.setError(fail.getMessage(), fail.idx); } catch ( Throwable t ) { - Eclipse.error(String.format("Lombok annotation handler %s failed", container.handler.getClass()), t); + Eclipse.error(ast, String.format("Lombok annotation handler %s failed", container.handler.getClass()), t); } } @@ -112,7 +113,8 @@ public class HandlerLibrary { for ( EclipseASTVisitor visitor : visitorHandlers ) try { ast.traverse(visitor); } catch ( Throwable t ) { - Eclipse.error(String.format("Lombok visitor handler %s failed", visitor.getClass()), t); + Eclipse.error((CompilationUnitDeclaration) ast.top().get(), + String.format("Lombok visitor handler %s failed", visitor.getClass()), t); } } } diff --git a/src/lombok/eclipse/TransformEclipseAST.java b/src/lombok/eclipse/TransformEclipseAST.java index b56c594a..ccb29a8c 100644 --- a/src/lombok/eclipse/TransformEclipseAST.java +++ b/src/lombok/eclipse/TransformEclipseAST.java @@ -41,7 +41,7 @@ public class TransformEclipseAST { l = HandlerLibrary.load(); f = CompilationUnitDeclaration.class.getDeclaredField("$lombokAST"); } catch ( Throwable t ) { - Eclipse.error("Problem initializing lombok", t); + Eclipse.error(null, "Problem initializing lombok", t); disableLombok = true; } astCacheField = f; @@ -70,17 +70,12 @@ public class TransformEclipseAST { new TransformEclipseAST(existing).go(); } catch ( Throwable t ) { try { - String fileName = "(unknown)"; - if ( ast.compilationResult != null && ast.compilationResult.fileName != null ) { - fileName = new String(ast.compilationResult.fileName); - } - String message = "Lombok can't parse this source: " + t.toString(); - EclipseAST.addProblemToCompilationResult(fileName, ast, false, message, ast, 0, 0); + EclipseAST.addProblemToCompilationResult(ast, false, message, 0, 0); t.printStackTrace(); } catch ( Throwable t2 ) { - Eclipse.error("Can't create an error in the problems dialog while adding: " + t.toString(), t2); + Eclipse.error(ast, "Can't create an error in the problems dialog while adding: " + t.toString(), t2); } } } diff --git a/src/lombok/eclipse/handlers/HandleData.java b/src/lombok/eclipse/handlers/HandleData.java index 64540e95..6c77644f 100644 --- a/src/lombok/eclipse/handlers/HandleData.java +++ b/src/lombok/eclipse/handlers/HandleData.java @@ -1,20 +1,38 @@ package lombok.eclipse.handlers; +import static lombok.eclipse.handlers.PKG.*; + import java.util.ArrayList; +import java.util.Collection; import java.util.List; -import org.eclipse.jdt.internal.compiler.ast.Annotation; -import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; -import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; -import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.mangosdk.spi.ProviderFor; - +import lombok.AccessLevel; import lombok.Data; import lombok.core.AnnotationValues; import lombok.core.AST.Kind; +import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseAST.Node; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.BinaryExpression; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.OperatorIds; +import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; +import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; +import org.eclipse.jdt.internal.compiler.ast.Statement; +import org.eclipse.jdt.internal.compiler.ast.StringLiteral; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.MethodScope; +import org.mangosdk.spi.ProviderFor; + @ProviderFor(EclipseAnnotationHandler.class) public class HandleData implements EclipseAnnotationHandler<Data> { @Override public boolean handle(AnnotationValues<Data> annotation, Annotation ast, Node annotationNode) { @@ -32,18 +50,111 @@ public class HandleData implements EclipseAnnotationHandler<Data> { } List<Node> nodesForEquality = new ArrayList<Node>(); + List<Node> nodesForConstructorAndToString = new ArrayList<Node>(); for ( Node child : typeNode.down() ) { if ( child.getKind() != Kind.FIELD ) continue; FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); //Skip static fields. if ( (fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0 ) continue; if ( (fieldDecl.modifiers & ClassFileConstants.AccTransient) == 0 ) nodesForEquality.add(child); + nodesForConstructorAndToString.add(child); new HandleGetter().generateGetterForField(child, annotationNode.get()); if ( (fieldDecl.modifiers & ClassFileConstants.AccFinal) == 0 ) new HandleSetter().generateSetterForField(child, annotationNode.get()); } - //TODO generate constructor, hashCode, equals, toString. - return true; + switch ( methodExists("toString", typeNode) ) { + case NOT_EXISTS: + MethodDeclaration toString = createToString(typeNode, nodesForConstructorAndToString, ast); + injectMethod(typeNode, toString); + break; + case EXISTS_BY_LOMBOK: + injectScopeIntoToString((MethodDeclaration) getExistingLombokMethod("toString", typeNode).get(), typeDecl); + } + + //TODO generate constructor, hashCode, equals. + return false; + } + + private void injectScopeIntoToString(MethodDeclaration method, TypeDeclaration typeDecl) { + if ( typeDecl.scope != null ) { + method.scope = new MethodScope(typeDecl.scope, method, false); + method.returnType.resolvedType = typeDecl.scope.getJavaLangString(); + method.binding = new MethodBinding(method.modifiers, + method.selector, typeDecl.scope.getJavaLangString(), null, null, typeDecl.binding); + } + } + + private MethodDeclaration createToString(Node type, Collection<Node> fields, ASTNode pos) { + char[] rawTypeName = ((TypeDeclaration)type.get()).name; + String typeName = rawTypeName == null ? "" : new String(rawTypeName); + char[] prefix = (typeName + "(").toCharArray(); + char[] suffix = ")".toCharArray(); + char[] infix = ", ".toCharArray(); + long p = (long)pos.sourceStart << 32 | pos.sourceEnd; + final int PLUS = OperatorIds.PLUS; + + boolean first = true; + Expression current = new StringLiteral(prefix, 0, 0, 0); + for ( Node field : fields ) { + char[] fName = ((FieldDeclaration)field.get()).name; + if ( fName == null ) continue; + if ( !first ) { + current = new BinaryExpression(current, new StringLiteral(infix, 0, 0, 0), PLUS); + } + else first = false; + current = new BinaryExpression(current, new SingleNameReference(fName, p), PLUS); + } + current = new BinaryExpression(current, new StringLiteral(suffix, 0, 0, 0), PLUS); + + ReturnStatement returnStatement = new ReturnStatement(current, (int)(p >> 32), (int)p); + + MethodDeclaration method = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); + method.modifiers = PKG.toModifier(AccessLevel.PUBLIC); + method.returnType = Eclipse.TYPEREF_JAVA_LANG_STRING; + method.annotations = null; + method.arguments = null; + method.selector = "toString".toCharArray(); + method.binding = null; + method.thrownExceptions = null; + method.typeParameters = null; + injectScopeIntoToString(method, (TypeDeclaration) type.get()); + method.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + method.bodyStart = method.declarationSourceStart = method.sourceStart = pos.sourceStart; + method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = pos.sourceEnd; + method.statements = new Statement[] { returnStatement }; + return method; + } + + private MethodDeclaration createEquals(Collection<Node> fields) { + return null; + } + + private ConstructorDeclaration createConstructor(Collection<Node> fields) { + //If using an of() constructor, make private. + //method params + //on loop: Assignment(FieldReference(ThisReference, "x"), SingleNameReference("x")) + return null; + } + + private MethodDeclaration createStaticConstructor(Collection<Node> fields) { + //Return(AllocationExpression(SingleTypeReference("Bar"), namesOfFields); + return null; + } + + private MethodDeclaration createHashCode(Collection<Node> fields) { + //booleans: conditionalexpression that bounces between 1231 and 1237. + //longs: (int) (lng ^ (lng >>> 32)); + //doubles and floats: Double.doubleToLongBits, then as long. + + //local final var PRIME = IntLiteral(primeNumber) + //local final var RESULT = IntLiteral(1) + + // Assignment("RESULT", BinaryExpression("+", BinaryExpression("*", "PRIME", "RESULT"), "name") + + // add = ConditionalExpression(EqualExpression("name", NullLiteral), IntLiteral(0), MessageSend("name", "hashCode()")) + // Assignment("RESULT", BinaryExpression("+", BinaryExpression("*", "PRIME", "RESULT"), add); + + return null; } } diff --git a/src/lombok/eclipse/handlers/HandleGetter.java b/src/lombok/eclipse/handlers/HandleGetter.java index 798705da..5b3fbf90 100644 --- a/src/lombok/eclipse/handlers/HandleGetter.java +++ b/src/lombok/eclipse/handlers/HandleGetter.java @@ -1,18 +1,17 @@ package lombok.eclipse.handlers; import static lombok.eclipse.handlers.PKG.*; - import lombok.AccessLevel; import lombok.Getter; import lombok.core.AnnotationValues; import lombok.core.TransformationsUtil; import lombok.core.AST.Kind; import lombok.eclipse.Eclipse; +import lombok.eclipse.EclipseASTVisitor; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseAST.Node; import org.eclipse.jdt.internal.compiler.ast.ASTNode; -import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; @@ -23,14 +22,16 @@ import org.eclipse.jdt.internal.compiler.ast.Statement; 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.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.mangosdk.spi.ProviderFor; @ProviderFor(EclipseAnnotationHandler.class) public class HandleGetter implements EclipseAnnotationHandler<Getter> { public void generateGetterForField(Node fieldNode, ASTNode pos) { - AccessLevel level = Getter.DEFAULT_ACCESS_LEVEL; + AccessLevel level = AccessLevel.PUBLIC; Node errorNode = fieldNode; + boolean whineIfExists = false; for ( Node child : fieldNode.down() ) { if ( child.getKind() == Kind.ANNOTATION ) { @@ -38,21 +39,22 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> { level = Eclipse.createAnnotation(Getter.class, child).getInstance().value(); errorNode = child; pos = child.get(); + whineIfExists = true; break; } } } - createGetterForField(level, fieldNode, errorNode, pos); + createGetterForField(level, fieldNode, errorNode, pos, whineIfExists); } @Override public boolean handle(AnnotationValues<Getter> annotation, Annotation ast, Node annotationNode) { Node fieldNode = annotationNode.up(); AccessLevel level = annotation.getInstance().value(); - return createGetterForField(level, fieldNode, annotationNode, annotationNode.get()); + return createGetterForField(level, fieldNode, annotationNode, annotationNode.get(), true); } - private boolean createGetterForField(AccessLevel level, Node fieldNode, Node errorNode, ASTNode pos) { + private boolean createGetterForField(AccessLevel level, Node fieldNode, Node errorNode, ASTNode pos, boolean whineIfExists) { if ( fieldNode.getKind() != Kind.FIELD ) { errorNode.addError("@Getter is only supported on a field."); return false; @@ -63,30 +65,40 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> { String getterName = TransformationsUtil.toGetterName( new String(field.name), nameEquals(fieldType.getTypeName(), "boolean")); - TypeDeclaration parent = (TypeDeclaration) fieldNode.up().get(); - if ( parent.methods != null ) for ( AbstractMethodDeclaration method : parent.methods ) { - if ( method.selector != null && new String(method.selector).equals(getterName) ) { - errorNode.addWarning(String.format( - "Not generating %s(): A method with that name already exists", getterName)); - return false; - } + int modifier = toModifier(level) | (field.modifiers & ClassFileConstants.AccStatic); + + switch ( methodExists(getterName, fieldNode) ) { + case EXISTS_BY_LOMBOK: + Node methodNode = getExistingLombokMethod(getterName, fieldNode); + injectScopeIntoGetter(modifier, field, (MethodDeclaration)methodNode.get(), (TypeDeclaration) methodNode.up().get()); + return false; + case EXISTS_BY_USER: + if ( whineIfExists ) errorNode.addWarning( + String.format("Not generating %s(): A method with that name already exists", getterName)); + return false; + default: + case NOT_EXISTS: + //continue with creating the getter } - int modifier = toModifier(level) | (field.modifiers & ClassFileConstants.AccStatic); + if ( new String(field.name).equals("a") ) fieldNode.up().traverse(new EclipseASTVisitor.Printer()); + MethodDeclaration method = generateGetter((TypeDeclaration) fieldNode.up().get(), field, getterName, modifier, pos); - MethodDeclaration method = generateGetter(parent, field, getterName, modifier, pos); + injectMethod(fieldNode.up(), method); - if ( parent.methods == null ) { - parent.methods = new AbstractMethodDeclaration[1]; - parent.methods[0] = method; - } else { - AbstractMethodDeclaration[] newArray = new AbstractMethodDeclaration[parent.methods.length + 1]; - System.arraycopy(parent.methods, 0, newArray, 0, parent.methods.length); - newArray[parent.methods.length] = method; - parent.methods = newArray; + return false; + } + + private void injectScopeIntoGetter(int modifier, FieldDeclaration field, MethodDeclaration method, TypeDeclaration parent) { + if ( parent.scope != null ) { + if ( method.binding != null ) { + method.binding.returnType = field.type.resolvedType; + } else { + method.scope = new MethodScope(parent.scope, method, false); + method.returnType.resolvedType = field.type.resolvedType; + method.binding = new MethodBinding(modifier, method.selector, method.returnType.resolvedType, null, null, parent.binding); + } } - - return true; } private MethodDeclaration generateGetter(TypeDeclaration parent, FieldDeclaration field, String name, @@ -100,8 +112,8 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> { method.binding = null; method.thrownExceptions = null; method.typeParameters = null; - method.scope = parent.scope == null ? null : new MethodScope(parent.scope, method, false); - method.bits |= ASTNode.Bit24; + injectScopeIntoGetter(modifier, field, method, parent); + method.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; Expression fieldExpression = new SingleNameReference(field.name, (field.declarationSourceStart << 32) | field.declarationSourceEnd); Statement returnStatement = new ReturnStatement(fieldExpression, field.sourceStart, field.sourceEnd); method.bodyStart = method.declarationSourceStart = method.sourceStart = pos.sourceStart; diff --git a/src/lombok/eclipse/handlers/HandleSetter.java b/src/lombok/eclipse/handlers/HandleSetter.java index d0d0d902..c5f87a93 100644 --- a/src/lombok/eclipse/handlers/HandleSetter.java +++ b/src/lombok/eclipse/handlers/HandleSetter.java @@ -1,9 +1,16 @@ package lombok.eclipse.handlers; import static lombok.eclipse.handlers.PKG.*; +import lombok.AccessLevel; +import lombok.Setter; +import lombok.core.AnnotationValues; +import lombok.core.TransformationsUtil; +import lombok.core.AST.Kind; +import lombok.eclipse.Eclipse; +import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.EclipseAST.Node; import org.eclipse.jdt.internal.compiler.ast.ASTNode; -import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.Assignment; @@ -16,24 +23,19 @@ import org.eclipse.jdt.internal.compiler.ast.ThisReference; 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.lookup.BaseTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.mangosdk.spi.ProviderFor; -import lombok.AccessLevel; -import lombok.Setter; -import lombok.core.AnnotationValues; -import lombok.core.TransformationsUtil; -import lombok.core.AST.Kind; -import lombok.eclipse.Eclipse; -import lombok.eclipse.EclipseAnnotationHandler; -import lombok.eclipse.EclipseAST.Node; - @ProviderFor(EclipseAnnotationHandler.class) public class HandleSetter implements EclipseAnnotationHandler<Setter> { public void generateSetterForField(Node fieldNode, ASTNode pos) { - AccessLevel level = Setter.DEFAULT_ACCESS_LEVEL; + AccessLevel level = AccessLevel.PUBLIC; Node errorNode = fieldNode; + boolean whineIfExists = false; for ( Node child : fieldNode.down() ) { if ( child.getKind() == Kind.ANNOTATION ) { @@ -41,51 +43,68 @@ public class HandleSetter implements EclipseAnnotationHandler<Setter> { level = Eclipse.createAnnotation(Setter.class, child).getInstance().value(); errorNode = child; pos = child.get(); + whineIfExists = true; break; } } } - createSetterForField(level, fieldNode, errorNode, pos); + createSetterForField(level, fieldNode, errorNode, pos, whineIfExists); } @Override public boolean handle(AnnotationValues<Setter> annotation, Annotation ast, Node annotationNode) { Node fieldNode = annotationNode.up(); if ( fieldNode.getKind() != Kind.FIELD ) return false; AccessLevel level = annotation.getInstance().value(); - return createSetterForField(level, fieldNode, annotationNode, annotationNode.get()); + return createSetterForField(level, fieldNode, annotationNode, annotationNode.get(), true); } - private boolean createSetterForField(AccessLevel level, Node fieldNode, Node errorNode, ASTNode pos) { - if ( fieldNode.getKind() != Kind.FIELD ) return false; + private boolean createSetterForField(AccessLevel level, Node fieldNode, Node errorNode, ASTNode pos, boolean whineIfExists) { + if ( fieldNode.getKind() != Kind.FIELD ) { + errorNode.addError("@Setter is only supported on a field."); + return false; + } + FieldDeclaration field = (FieldDeclaration) fieldNode.get(); String setterName = TransformationsUtil.toSetterName(new String(field.name)); - TypeDeclaration parent = (TypeDeclaration) fieldNode.up().get(); - if ( parent.methods != null ) for ( AbstractMethodDeclaration method : parent.methods ) { - if ( method.selector != null && new String(method.selector).equals(setterName) ) { - errorNode.addWarning(String.format( - "Not generating %s(%s %s): A method with that name already exists", - setterName, field.type, new String(field.name))); - return false; - } + int modifier = toModifier(level) | (field.modifiers & ClassFileConstants.AccStatic); + + switch ( methodExists(setterName, fieldNode) ) { + case EXISTS_BY_LOMBOK: + Node methodNode = getExistingLombokMethod(setterName, fieldNode); + injectScopeIntoSetter(modifier, field, (MethodDeclaration)methodNode.get(), (TypeDeclaration) methodNode.up().get()); + return false; + case EXISTS_BY_USER: + if ( whineIfExists ) errorNode.addWarning( + String.format("Not generating %s(%s %s): A method with that name already exists", + setterName, field.type, new String(field.name))); + return false; + default: + case NOT_EXISTS: + //continue with creating the setter } - int modifier = toModifier(level) | (field.modifiers & ClassFileConstants.AccStatic); - MethodDeclaration method = generateSetter(parent, field, setterName, modifier, pos); + MethodDeclaration method = generateSetter((TypeDeclaration) fieldNode.up().get(), field, setterName, modifier, pos); - if ( parent.methods == null ) { - parent.methods = new AbstractMethodDeclaration[1]; - parent.methods[0] = method; - } else { - AbstractMethodDeclaration[] newArray = new AbstractMethodDeclaration[parent.methods.length + 1]; - System.arraycopy(parent.methods, 0, newArray, 0, parent.methods.length); - newArray[parent.methods.length] = method; - parent.methods = newArray; - } + injectMethod(fieldNode.up(), method); - return true; + return false; + } + + private void injectScopeIntoSetter(int modifier, FieldDeclaration field, MethodDeclaration method, TypeDeclaration parent) { + if ( parent.scope != null ) { + TypeBinding[] params = new TypeBinding[] { field.type.resolvedType }; + + if ( method.binding == null ) { + method.scope = new MethodScope(parent.scope, method, false); + method.binding = new MethodBinding(modifier, method.selector, BaseTypeBinding.VOID, params, null, parent.binding); + } + method.binding.parameters = params; + method.binding.returnType = BaseTypeBinding.VOID; + method.returnType.resolvedType = BaseTypeBinding.VOID; + } } private MethodDeclaration generateSetter(TypeDeclaration parent, FieldDeclaration field, String name, @@ -102,7 +121,8 @@ public class HandleSetter implements EclipseAnnotationHandler<Setter> { method.thrownExceptions = null; method.typeParameters = null; method.scope = parent.scope == null ? null : new MethodScope(parent.scope, method, false); - method.bits |= ASTNode.Bit24; + method.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + injectScopeIntoSetter(modifier, field, method, parent); FieldReference thisX = new FieldReference(("this." + new String(field.name)).toCharArray(), pos); thisX.receiver = new ThisReference(ast.sourceStart, ast.sourceEnd); thisX.token = field.name; diff --git a/src/lombok/eclipse/handlers/PKG.java b/src/lombok/eclipse/handlers/PKG.java index 61f71140..d93807a7 100644 --- a/src/lombok/eclipse/handlers/PKG.java +++ b/src/lombok/eclipse/handlers/PKG.java @@ -3,6 +3,11 @@ package lombok.eclipse.handlers; import java.lang.reflect.Modifier; import lombok.AccessLevel; +import lombok.core.AST.Kind; +import lombok.eclipse.EclipseAST; + +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; class PKG { private PKG() {} @@ -33,4 +38,63 @@ class PKG { return string.contentEquals(sb); } + + enum MethodExistsResult { + NOT_EXISTS, EXISTS_BY_USER, EXISTS_BY_LOMBOK; + } + + static MethodExistsResult methodExists(String methodName, EclipseAST.Node node) { + while ( node != null && !(node.get() instanceof TypeDeclaration) ) { + node = node.up(); + } + + if ( node.get() instanceof TypeDeclaration ) { + TypeDeclaration typeDecl = (TypeDeclaration)node.get(); + if ( typeDecl.methods != null ) for ( AbstractMethodDeclaration def : typeDecl.methods ) { + char[] mName = ((AbstractMethodDeclaration)def).selector; + if ( mName == null ) continue; + if ( methodName.equals(new String(mName)) ) { + EclipseAST.Node existing = node.getNodeFor(def); + if ( existing == null || !existing.isHandled() ) return MethodExistsResult.EXISTS_BY_USER; + return MethodExistsResult.EXISTS_BY_LOMBOK; + } + } + } + + return MethodExistsResult.NOT_EXISTS; + } + + static EclipseAST.Node getExistingLombokMethod(String methodName, EclipseAST.Node node) { + while ( node != null && !(node.get() instanceof TypeDeclaration) ) { + node = node.up(); + } + + if ( node.get() instanceof TypeDeclaration ) { + for ( AbstractMethodDeclaration def : ((TypeDeclaration)node.get()).methods ) { + char[] mName = ((AbstractMethodDeclaration)def).selector; + if ( mName == null ) continue; + if ( methodName.equals(new String(mName)) ) { + EclipseAST.Node existing = node.getNodeFor(def); + if ( existing.isHandled() ) return existing; + } + } + } + + return null; + } + + static void injectMethod(EclipseAST.Node type, AbstractMethodDeclaration method) { + TypeDeclaration parent = (TypeDeclaration) type.get(); + if ( parent.methods == null ) { + parent.methods = new AbstractMethodDeclaration[1]; + parent.methods[0] = method; + } else { + AbstractMethodDeclaration[] newArray = new AbstractMethodDeclaration[parent.methods.length + 1]; + System.arraycopy(parent.methods, 0, newArray, 0, parent.methods.length); + newArray[parent.methods.length] = method; + parent.methods = newArray; + } + + type.add(method, Kind.METHOD).recursiveSetHandled(); + } } diff --git a/src/lombok/javac/JavacAST.java b/src/lombok/javac/JavacAST.java index 7050df75..f0544256 100644 --- a/src/lombok/javac/JavacAST.java +++ b/src/lombok/javac/JavacAST.java @@ -94,6 +94,31 @@ public class JavacAST extends AST<JCTree> { return symtab; } + @Override protected Node buildTree(JCTree node, Kind kind) { + switch ( kind ) { + case COMPILATION_UNIT: + return buildCompilationUnit((JCCompilationUnit) node); + case TYPE: + return buildType((JCClassDecl) node); + case FIELD: + return buildField((JCVariableDecl) node); + case INITIALIZER: + return buildInitializer((JCBlock) node); + case METHOD: + return buildMethod((JCMethodDecl) node); + case ARGUMENT: + return buildLocalVar((JCVariableDecl) node, kind); + case LOCAL: + return buildLocalVar((JCVariableDecl) node, kind); + case STATEMENT: + return buildStatementOrExpression(node); + case ANNOTATION: + return buildAnnotation((JCAnnotation) node); + default: + throw new AssertionError("Did not expect: " + kind); + } + } + private Node buildCompilationUnit(JCCompilationUnit top) { List<Node> childNodes = new ArrayList<Node>(); for ( JCTree s : top.defs ) { @@ -310,6 +335,11 @@ public class JavacAST extends AST<JCTree> { } /** {@inheritDoc} */ + @Override public Node getNodeFor(JCTree obj) { + return (Node) super.getNodeFor(obj); + } + + /** {@inheritDoc} */ @Override public Node directUp() { return (Node) super.directUp(); } diff --git a/src/lombok/javac/handlers/HandleGetter.java b/src/lombok/javac/handlers/HandleGetter.java index 651bc018..3405810b 100644 --- a/src/lombok/javac/handlers/HandleGetter.java +++ b/src/lombok/javac/handlers/HandleGetter.java @@ -29,8 +29,9 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; @ProviderFor(JavacAnnotationHandler.class) public class HandleGetter implements JavacAnnotationHandler<Getter> { public void generateGetterForField(Node fieldNode, DiagnosticPosition pos) { - AccessLevel level = Getter.DEFAULT_ACCESS_LEVEL; + AccessLevel level = AccessLevel.PUBLIC; Node errorNode = fieldNode; + boolean whineIfExists = false; for ( Node child : fieldNode.down() ) { if ( child.getKind() == Kind.ANNOTATION ) { @@ -38,21 +39,22 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> { level = Javac.createAnnotation(Getter.class, child).getInstance().value(); errorNode = child; pos = child.get(); + whineIfExists = true; break; } } } - createGetterForField(level, fieldNode, errorNode, pos); + createGetterForField(level, fieldNode, errorNode, pos, whineIfExists); } @Override public boolean handle(AnnotationValues<Getter> annotation, JCAnnotation ast, Node annotationNode) { Node fieldNode = annotationNode.up(); AccessLevel level = annotation.getInstance().value(); - return createGetterForField(level, fieldNode, annotationNode, annotationNode.get()); + return createGetterForField(level, fieldNode, annotationNode, annotationNode.get(), true); } - private boolean createGetterForField(AccessLevel level, Node fieldNode, Node errorNode, DiagnosticPosition pos) { + private boolean createGetterForField(AccessLevel level, Node fieldNode, Node errorNode, DiagnosticPosition pos, boolean whineIfExists) { if ( fieldNode.getKind() != Kind.FIELD ) { errorNode.addError("@Getter is only supported on a field."); return false; @@ -61,10 +63,16 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> { JCVariableDecl fieldDecl = (JCVariableDecl)fieldNode.get(); String methodName = toGetterName(fieldDecl); - if ( methodExists(methodName, fieldNode) ) { - errorNode.addWarning( + switch ( methodExists(methodName, fieldNode) ) { + case EXISTS_BY_LOMBOK: + return true; + case EXISTS_BY_USER: + if ( whineIfExists ) errorNode.addWarning( String.format("Not generating %s(): A method with that name already exists", methodName)); return false; + default: + case NOT_EXISTS: + //continue with creating the getter } JCClassDecl javacClassTree = (JCClassDecl) fieldNode.up().get(); @@ -73,6 +81,9 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> { JCMethodDecl getterMethod = createGetter(access, fieldNode, fieldNode.getTreeMaker()); javacClassTree.defs = javacClassTree.defs.append(getterMethod); + + fieldNode.up().add(getterMethod, Kind.METHOD).recursiveSetHandled(); + return true; } diff --git a/src/lombok/javac/handlers/HandleSetter.java b/src/lombok/javac/handlers/HandleSetter.java index f1e73489..4999b5d8 100644 --- a/src/lombok/javac/handlers/HandleSetter.java +++ b/src/lombok/javac/handlers/HandleSetter.java @@ -31,8 +31,9 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; @ProviderFor(JavacAnnotationHandler.class) public class HandleSetter implements JavacAnnotationHandler<Setter> { public void generateSetterForField(Node fieldNode, DiagnosticPosition pos) { - AccessLevel level = Setter.DEFAULT_ACCESS_LEVEL; + AccessLevel level = AccessLevel.PUBLIC; Node errorNode = fieldNode; + boolean whineIfExists = false; for ( Node child : fieldNode.down() ) { if ( child.getKind() == Kind.ANNOTATION ) { @@ -40,21 +41,22 @@ public class HandleSetter implements JavacAnnotationHandler<Setter> { level = Javac.createAnnotation(Setter.class, child).getInstance().value(); errorNode = child; pos = child.get(); + whineIfExists = true; break; } } } - createSetterForField(level, fieldNode, errorNode, pos); + createSetterForField(level, fieldNode, errorNode, pos, whineIfExists); } @Override public boolean handle(AnnotationValues<Setter> annotation, JCAnnotation ast, Node annotationNode) { Node fieldNode = annotationNode.up(); AccessLevel level = annotation.getInstance().value(); - return createSetterForField(level, fieldNode, annotationNode, annotationNode.get()); + return createSetterForField(level, fieldNode, annotationNode, annotationNode.get(), true); } - private boolean createSetterForField(AccessLevel level, Node fieldNode, Node errorNode, DiagnosticPosition pos) { + private boolean createSetterForField(AccessLevel level, Node fieldNode, Node errorNode, DiagnosticPosition pos, boolean whineIfExists) { if ( fieldNode.getKind() != Kind.FIELD ) { fieldNode.addError("@Setter is only supported on a field."); return false; @@ -63,11 +65,17 @@ public class HandleSetter implements JavacAnnotationHandler<Setter> { JCVariableDecl fieldDecl = (JCVariableDecl)fieldNode.get(); String methodName = toSetterName(fieldDecl); - if ( methodExists(methodName, fieldNode) ) { - errorNode.addWarning( + switch ( methodExists(methodName, fieldNode) ) { + case EXISTS_BY_LOMBOK: + return true; + case EXISTS_BY_USER: + if ( whineIfExists ) errorNode.addWarning( String.format("Not generating %s(%s %s): A method with that name already exists", - methodName, fieldDecl.vartype, fieldDecl.name)); + methodName, fieldDecl.vartype, fieldDecl.name)); return false; + default: + case NOT_EXISTS: + //continue with creating the setter } JCClassDecl javacClassTree = (JCClassDecl) fieldNode.up().get(); @@ -76,6 +84,9 @@ public class HandleSetter implements JavacAnnotationHandler<Setter> { JCMethodDecl setterMethod = createSetter(access, fieldNode, fieldNode.getTreeMaker()); javacClassTree.defs = javacClassTree.defs.append(setterMethod); + + fieldNode.up().add(setterMethod, Kind.METHOD).recursiveSetHandled(); + return true; } diff --git a/src/lombok/javac/handlers/PKG.java b/src/lombok/javac/handlers/PKG.java index 2c038b2d..138559a9 100644 --- a/src/lombok/javac/handlers/PKG.java +++ b/src/lombok/javac/handlers/PKG.java @@ -28,7 +28,11 @@ class PKG { return TransformationsUtil.toSetterName(fieldName); } - static boolean methodExists(String methodName, JavacAST.Node node) { + enum MethodExistsResult { + NOT_EXISTS, EXISTS_BY_USER, EXISTS_BY_LOMBOK; + } + + static MethodExistsResult methodExists(String methodName, JavacAST.Node node) { while ( node != null && !(node.get() instanceof JCClassDecl) ) { node = node.up(); } @@ -37,13 +41,15 @@ class PKG { for ( JCTree def : ((JCClassDecl)node.get()).defs ) { if ( def instanceof JCMethodDecl ) { if ( ((JCMethodDecl)def).name.contentEquals(methodName) ) { - return true; + JavacAST.Node existing = node.getNodeFor(def); + if ( existing == null || !existing.isHandled() ) return MethodExistsResult.EXISTS_BY_USER; + return MethodExistsResult.EXISTS_BY_LOMBOK; } } } } - return false; + return MethodExistsResult.NOT_EXISTS; } static int toJavacModifier(AccessLevel accessLevel) { |