diff options
author | Reinier Zwitserloot <reinier@zwitserloot.com> | 2010-11-09 20:37:25 +0100 |
---|---|---|
committer | Reinier Zwitserloot <reinier@zwitserloot.com> | 2010-11-09 20:37:25 +0100 |
commit | 46d471e9c3dc32b03c34804df1819739a4dffc50 (patch) | |
tree | 9c31d75426bf8fdb1943bef2a996485640f7bf5e /src/core/lombok/javac | |
parent | 92b7efac48c18f22b81098cf1d844a891bb71648 (diff) | |
parent | 98d8a9f63b3183005174abb7691a1692347b9a2e (diff) | |
download | lombok-46d471e9c3dc32b03c34804df1819739a4dffc50.tar.gz lombok-46d471e9c3dc32b03c34804df1819739a4dffc50.tar.bz2 lombok-46d471e9c3dc32b03c34804df1819739a4dffc50.zip |
Merge branch 'master' into annoGetSet
Diffstat (limited to 'src/core/lombok/javac')
-rw-r--r-- | src/core/lombok/javac/HandlerLibrary.java | 8 | ||||
-rw-r--r-- | src/core/lombok/javac/Javac.java | 5 | ||||
-rw-r--r-- | src/core/lombok/javac/JavacASTAdapter.java | 5 | ||||
-rw-r--r-- | src/core/lombok/javac/JavacASTVisitor.java | 10 | ||||
-rw-r--r-- | src/core/lombok/javac/JavacResolution.java | 414 | ||||
-rw-r--r-- | src/core/lombok/javac/TreeMirrorMaker.java | 50 | ||||
-rw-r--r-- | src/core/lombok/javac/apt/Processor.java | 2 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleCleanup.java | 16 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java | 86 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleLog.java | 203 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleSynchronized.java | 2 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleVal.java | 113 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/JavacHandlerUtil.java | 30 |
13 files changed, 911 insertions, 33 deletions
diff --git a/src/core/lombok/javac/HandlerLibrary.java b/src/core/lombok/javac/HandlerLibrary.java index bbbdacd0..5b792874 100644 --- a/src/core/lombok/javac/HandlerLibrary.java +++ b/src/core/lombok/javac/HandlerLibrary.java @@ -183,7 +183,13 @@ public class HandlerLibrary { */ public void callASTVisitors(JavacAST ast) { for (JavacASTVisitor visitor : visitorHandlers) try { - ast.traverse(visitor); + if (!visitor.isResolutionBased()) ast.traverse(visitor); + } catch (Throwable t) { + javacError(String.format("Lombok visitor handler %s failed", visitor.getClass()), t); + } + + for (JavacASTVisitor visitor : visitorHandlers) try { + if (visitor.isResolutionBased()) ast.traverse(visitor); } catch (Throwable t) { javacError(String.format("Lombok visitor handler %s failed", visitor.getClass()), t); } diff --git a/src/core/lombok/javac/Javac.java b/src/core/lombok/javac/Javac.java index 58a24207..6d9800ab 100644 --- a/src/core/lombok/javac/Javac.java +++ b/src/core/lombok/javac/Javac.java @@ -90,6 +90,7 @@ public class Javac { String name = m.getName(); List<String> raws = new ArrayList<String>(); List<Object> guesses = new ArrayList<Object>(); + List<Object> expressions = new ArrayList<Object>(); final List<DiagnosticPosition> positions = new ArrayList<DiagnosticPosition>(); boolean isExplicit = false; @@ -112,17 +113,19 @@ public class Javac { List<JCExpression> elems = ((JCNewArray)rhs).elems; for (JCExpression inner : elems) { raws.add(inner.toString()); + expressions.add(inner); guesses.add(calculateGuess(inner)); positions.add(inner.pos()); } } else { raws.add(rhs.toString()); + expressions.add(rhs); guesses.add(calculateGuess(rhs)); positions.add(rhs.pos()); } } - values.put(name, new AnnotationValue(node, raws, guesses, isExplicit) { + values.put(name, new AnnotationValue(node, raws, expressions, guesses, isExplicit) { @Override public void setError(String message, int valueIdx) { if (valueIdx < 0) node.addError(message); else node.addError(message, positions.get(valueIdx)); diff --git a/src/core/lombok/javac/JavacASTAdapter.java b/src/core/lombok/javac/JavacASTAdapter.java index 41bc46d3..bbdb6876 100644 --- a/src/core/lombok/javac/JavacASTAdapter.java +++ b/src/core/lombok/javac/JavacASTAdapter.java @@ -35,6 +35,11 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl; */ public class JavacASTAdapter implements JavacASTVisitor { /** {@inheritDoc} */ + @Override public boolean isResolutionBased() { + return false; + } + + /** {@inheritDoc} */ @Override public void visitCompilationUnit(JavacNode top, JCCompilationUnit unit) {} /** {@inheritDoc} */ diff --git a/src/core/lombok/javac/JavacASTVisitor.java b/src/core/lombok/javac/JavacASTVisitor.java index 3c5887a7..18376037 100644 --- a/src/core/lombok/javac/JavacASTVisitor.java +++ b/src/core/lombok/javac/JavacASTVisitor.java @@ -38,6 +38,12 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl; */ public interface JavacASTVisitor { /** + * If true, you'll be called after all the non-resolution based visitors. + * NB: Temporary solution - will be rewritten to a different style altogether in a future release. + */ + boolean isResolutionBased(); + + /** * Called at the very beginning and end. */ void visitCompilationUnit(JavacNode top, JCCompilationUnit unit); @@ -101,6 +107,10 @@ public interface JavacASTVisitor { private int disablePrinting = 0; private int indent = 0; + @Override public boolean isResolutionBased() { + return false; + } + /** * @param printContent if true, bodies are printed directly, as java code, * instead of a tree listing of every AST node inside it. diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java new file mode 100644 index 00000000..e0eb436d --- /dev/null +++ b/src/core/lombok/javac/JavacResolution.java @@ -0,0 +1,414 @@ +package lombok.javac; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.util.ArrayDeque; +import java.util.Map; + +import javax.lang.model.type.TypeKind; +import javax.tools.DiagnosticListener; + +import com.sun.tools.javac.code.BoundKind; +import com.sun.tools.javac.code.Symbol.TypeSymbol; +import com.sun.tools.javac.code.Symtab; +import com.sun.tools.javac.code.Type.ArrayType; +import com.sun.tools.javac.code.Type.CapturedType; +import com.sun.tools.javac.code.Type.ClassType; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.TypeTags; +import com.sun.tools.javac.code.Types; +import com.sun.tools.javac.comp.Attr; +import com.sun.tools.javac.comp.AttrContext; +import com.sun.tools.javac.comp.Enter; +import com.sun.tools.javac.comp.Env; +import com.sun.tools.javac.comp.MemberEnter; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Log; + +public class JavacResolution { + private final Attr attr; + private final LogDisabler logDisabler; + + public JavacResolution(Context context) { + attr = Attr.instance(context); + logDisabler = new LogDisabler(context); + } + /** + * During resolution, the resolver will emit resolution errors, but without appropriate file names and line numbers. If these resolution errors stick around + * then they will be generated AGAIN, this time with proper names and line numbers, at the end. Therefore, we want to suppress the logger. + */ + private static final class LogDisabler { + private final Log log; + private static final Field errWriterField, warnWriterField, noticeWriterField, dumpOnErrorField, promptOnErrorField, diagnosticListenerField; + private PrintWriter errWriter, warnWriter, noticeWriter; + private Boolean dumpOnError, promptOnError; + private DiagnosticListener<?> contextDiagnosticListener, logDiagnosticListener; + private final Context context; + + // If this is true, the fields changed. Better to print weird error messages than to fail outright. + private static final boolean dontBother; + + static { + boolean z; + Field a = null, b = null, c = null, d = null, e = null, f = null; + try { + a = Log.class.getDeclaredField("errWriter"); + b = Log.class.getDeclaredField("warnWriter"); + c = Log.class.getDeclaredField("noticeWriter"); + d = Log.class.getDeclaredField("dumpOnError"); + e = Log.class.getDeclaredField("promptOnError"); + f = Log.class.getDeclaredField("diagListener"); + z = false; + a.setAccessible(true); + b.setAccessible(true); + c.setAccessible(true); + d.setAccessible(true); + e.setAccessible(true); + f.setAccessible(true); + } catch (Exception x) { + z = true; + } + + errWriterField = a; + warnWriterField = b; + noticeWriterField = c; + dumpOnErrorField = d; + promptOnErrorField = e; + diagnosticListenerField = f; + dontBother = z; + } + + LogDisabler(Context context) { + this.log = Log.instance(context); + this.context = context; + } + + boolean disableLoggers() { + contextDiagnosticListener = context.get(DiagnosticListener.class); + context.put(DiagnosticListener.class, (DiagnosticListener<?>) null); + if (dontBother) return false; + boolean dontBotherInstance = false; + + PrintWriter dummyWriter = new PrintWriter(new OutputStream() { + @Override public void write(int b) throws IOException { + // Do nothing on purpose + } + }); + + if (!dontBotherInstance) try { + errWriter = (PrintWriter) errWriterField.get(log); + errWriterField.set(log, dummyWriter); + } catch (Exception e) { + dontBotherInstance = true; + } + + if (!dontBotherInstance) try { + warnWriter = (PrintWriter) warnWriterField.get(log); + warnWriterField.set(log, dummyWriter); + } catch (Exception e) { + dontBotherInstance = true; + } + + if (!dontBotherInstance) try { + noticeWriter = (PrintWriter) noticeWriterField.get(log); + noticeWriterField.set(log, dummyWriter); + } catch (Exception e) { + dontBotherInstance = true; + } + + if (!dontBotherInstance) try { + dumpOnError = (Boolean) dumpOnErrorField.get(log); + dumpOnErrorField.set(log, false); + } catch (Exception e) { + dontBotherInstance = true; + } + + if (!dontBotherInstance) try { + promptOnError = (Boolean) promptOnErrorField.get(log); + promptOnErrorField.set(log, false); + } catch (Exception e) { + dontBotherInstance = true; + } + + if (!dontBotherInstance) try { + logDiagnosticListener = (DiagnosticListener<?>) diagnosticListenerField.get(log); + diagnosticListenerField.set(log, null); + } catch (Exception e) { + dontBotherInstance = true; + } + + if (dontBotherInstance) enableLoggers(); + return !dontBotherInstance; + } + + void enableLoggers() { + if (contextDiagnosticListener != null) { + context.put(DiagnosticListener.class, contextDiagnosticListener); + contextDiagnosticListener = null; + } + if (errWriter != null) try { + errWriterField.set(log, errWriter); + errWriter = null; + } catch (Exception e) {} + + if (warnWriter != null) try { + warnWriterField.set(log, warnWriter); + warnWriter = null; + } catch (Exception e) {} + + if (noticeWriter != null) try { + noticeWriterField.set(log, noticeWriter); + noticeWriter = null; + } catch (Exception e) {} + + if (dumpOnError != null) try { + dumpOnErrorField.set(log, dumpOnError); + dumpOnError = null; + } catch (Exception e) {} + + if (promptOnError != null) try { + promptOnErrorField.set(log, promptOnError); + promptOnError = null; + } catch (Exception e) {} + + if (logDiagnosticListener != null) try { + diagnosticListenerField.set(log, logDiagnosticListener); + logDiagnosticListener = null; + } catch (Exception e) {} + } + } + + private static final class EnvFinder extends JCTree.Visitor { + private Env<AttrContext> env = null; + private Enter enter; + private MemberEnter memberEnter; + private JCTree copyAt = null; + + EnvFinder(Context context) { + this.enter = Enter.instance(context); + this.memberEnter = MemberEnter.instance(context); + } + + Env<AttrContext> get() { + return env; + } + + JCTree copyAt() { + return copyAt; + } + + @Override public void visitTopLevel(JCCompilationUnit tree) { + if (copyAt != null) return; + env = enter.getTopLevelEnv(tree); + } + + @Override public void visitClassDef(JCClassDecl tree) { + if (copyAt != null) return; + // The commented out one leaves the 'lint' field unset, which causes NPEs during attrib. So, we use the other one. + //env = enter.classEnv((JCClassDecl) tree, env); + env = enter.getClassEnv(tree.sym); + } + + @Override public void visitMethodDef(JCMethodDecl tree) { + if (copyAt != null) return; + env = memberEnter.getMethodEnv(tree, env); + copyAt = tree; + } + + public void visitVarDef(JCVariableDecl tree) { + if (copyAt != null) return; + env = memberEnter.getInitEnv(tree, env); + copyAt = tree; + } + + @Override public void visitBlock(JCBlock tree) { + if (copyAt != null) return; + copyAt = tree; + } + + @Override public void visitTree(JCTree that) { + } + } + + public Map<JCTree, JCTree> resolve(JavacNode node) { + ArrayDeque<JCTree> stack = new ArrayDeque<JCTree>(); + + { + JavacNode n = node; + while (n != null) { + stack.push(n.get()); + n = n.up(); + } + } + + logDisabler.disableLoggers(); + try { + EnvFinder finder = new EnvFinder(node.getContext()); + while (!stack.isEmpty()) stack.pop().accept(finder); + + TreeMirrorMaker mirrorMaker = new TreeMirrorMaker(node); + JCTree copy = mirrorMaker.copy(finder.copyAt()); + + attrib(copy, finder.get()); + return mirrorMaker.getOriginalToCopyMap(); + } finally { + logDisabler.enableLoggers(); + } + } + + private void attrib(JCTree tree, Env<AttrContext> env) { + if (tree instanceof JCBlock) attr.attribStat(tree, env); + else if (tree instanceof JCMethodDecl) attr.attribStat(((JCMethodDecl)tree).body, env); + else if (tree instanceof JCVariableDecl) attr.attribStat(tree, env); + else throw new IllegalStateException("Called with something that isn't a block, method decl, or variable decl"); + } + + public static class TypeNotConvertibleException extends Exception { + public TypeNotConvertibleException(String msg) { + super(msg); + } + } + + public static Type ifTypeIsIterableToComponent(Type type, JavacAST ast) { + Types types = Types.instance(ast.getContext()); + Symtab syms = Symtab.instance(ast.getContext()); + Type boundType = types.upperBound(type); + Type elemTypeIfArray = types.elemtype(boundType); + if (elemTypeIfArray != null) return elemTypeIfArray; + + Type base = types.asSuper(boundType, syms.iterableType.tsym); + if (base == null) return syms.objectType; + + List<Type> iterableParams = base.allparams(); + return iterableParams.isEmpty() ? syms.objectType : types.upperBound(iterableParams.head); + } + + public static JCExpression typeToJCTree(Type type, TreeMaker maker, JavacAST ast) throws TypeNotConvertibleException { + return typeToJCTree(type, maker, ast, false); + } + + public static JCExpression createJavaLangObject(TreeMaker maker, JavacAST ast) { + JCExpression out = maker.Ident(ast.toName("java")); + out = maker.Select(out, ast.toName("lang")); + out = maker.Select(out, ast.toName("Object")); + return out; + } + + private static JCExpression typeToJCTree(Type type, TreeMaker maker, JavacAST ast, boolean allowCompound) throws TypeNotConvertibleException { + int dims = 0; + Type type0 = type; + while (type0 instanceof ArrayType) { + dims++; + type0 = ((ArrayType)type0).elemtype; + } + + JCExpression result = typeToJCTree0(type0, maker, ast, allowCompound); + while (dims > 0) { + result = maker.TypeArray(result); + dims--; + } + return result; + } + + private static JCExpression typeToJCTree0(Type type, TreeMaker maker, JavacAST ast, boolean allowCompound) throws TypeNotConvertibleException { + // NB: There's such a thing as maker.Type(type), but this doesn't work very well; it screws up anonymous classes, captures, and adds an extra prefix dot for some reason too. + // -- so we write our own take on that here. + + if (type.isPrimitive()) return primitiveToJCTree(type.getKind(), maker); + if (type.isErroneous()) throw new TypeNotConvertibleException("Type cannot be resolved"); + + TypeSymbol symbol = type.asElement(); + List<Type> generics = type.getTypeArguments(); + + JCExpression replacement = null; + + if (symbol == null) throw new TypeNotConvertibleException("Null or compound type"); + + if (symbol.name.len == 0) { + // Anonymous inner class + if (type instanceof ClassType) { + List<Type> ifaces = ((ClassType)type).interfaces_field; + Type supertype = ((ClassType)type).supertype_field; + if (ifaces != null && ifaces.length() == 1) { + return typeToJCTree(ifaces.get(0), maker, ast, allowCompound); + } + if (supertype != null) return typeToJCTree(supertype, maker, ast, allowCompound); + } + throw new TypeNotConvertibleException("Anonymous inner class"); + } + + if (type instanceof CapturedType) { + if (allowCompound) { + if (type.getLowerBound() == null || type.getLowerBound().tag == TypeTags.BOT) { + if (type.getUpperBound().toString().equals("java.lang.Object")) { + return maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); + } + return maker.Wildcard(maker.TypeBoundKind(BoundKind.EXTENDS), typeToJCTree(type.getUpperBound(), maker, ast, false)); + } else { + return maker.Wildcard(maker.TypeBoundKind(BoundKind.SUPER), typeToJCTree(type.getLowerBound(), maker, ast, false)); + } + } + if (type.getUpperBound() != null) { + return typeToJCTree(type.getUpperBound(), maker, ast, allowCompound); + } + + return createJavaLangObject(maker, ast); + } + + String qName = symbol.getQualifiedName().toString(); + if (qName.isEmpty()) throw new TypeNotConvertibleException("unknown type"); + if (qName.startsWith("<")) throw new TypeNotConvertibleException(qName); + String[] baseNames = symbol.getQualifiedName().toString().split("\\."); + replacement = maker.Ident(ast.toName(baseNames[0])); + for (int i = 1; i < baseNames.length; i++) { + replacement = maker.Select(replacement, ast.toName(baseNames[i])); + } + + if (generics != null && !generics.isEmpty()) { + List<JCExpression> args = List.nil(); + for (Type t : generics) args = args.append(typeToJCTree(t, maker, ast, true)); + replacement = maker.TypeApply(replacement, args); + } + + return replacement; + } + + private static JCExpression primitiveToJCTree(TypeKind kind, TreeMaker maker) throws TypeNotConvertibleException { + switch (kind) { + case BYTE: + return maker.TypeIdent(TypeTags.BYTE); + case CHAR: + return maker.TypeIdent(TypeTags.CHAR); + case SHORT: + return maker.TypeIdent(TypeTags.SHORT); + case INT: + return maker.TypeIdent(TypeTags.INT); + case LONG: + return maker.TypeIdent(TypeTags.LONG); + case FLOAT: + return maker.TypeIdent(TypeTags.FLOAT); + case DOUBLE: + return maker.TypeIdent(TypeTags.DOUBLE); + case BOOLEAN: + return maker.TypeIdent(TypeTags.BOOLEAN); + case VOID: + return maker.TypeIdent(TypeTags.VOID); + case NULL: + case NONE: + case OTHER: + default: + throw new TypeNotConvertibleException("Nulltype"); + } + } +} diff --git a/src/core/lombok/javac/TreeMirrorMaker.java b/src/core/lombok/javac/TreeMirrorMaker.java new file mode 100644 index 00000000..1c0b9311 --- /dev/null +++ b/src/core/lombok/javac/TreeMirrorMaker.java @@ -0,0 +1,50 @@ +package lombok.javac; + +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.Map; + +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.TreeCopier; +import com.sun.tools.javac.util.List; + +public class TreeMirrorMaker extends TreeCopier<Void> { + private final IdentityHashMap<JCTree, JCTree> originalToCopy = new IdentityHashMap<JCTree, JCTree>(); + + public TreeMirrorMaker(JavacNode node) { + super(node.getTreeMaker()); + } + + @Override public <T extends JCTree> T copy(T original) { + T copy = super.copy(original); + originalToCopy.put(original, copy); + return copy; + } + + @Override public <T extends JCTree> T copy(T original, Void p) { + T copy = super.copy(original, p); + originalToCopy.put(original, copy); + return copy; + } + + @Override public <T extends JCTree> List<T> copy(List<T> originals) { + List<T> copies = super.copy(originals); + Iterator<T> it1 = originals.iterator(); + Iterator<T> it2 = copies.iterator(); + while (it1.hasNext()) originalToCopy.put(it1.next(), it2.next()); + return copies; + } + + @Override public <T extends JCTree> List<T> copy(List<T> originals, Void p) { + List<T> copies = super.copy(originals, p); + Iterator<T> it1 = originals.iterator(); + Iterator<T> it2 = copies.iterator(); + while (it1.hasNext()) originalToCopy.put(it1.next(), it2.next()); + return copies; + } + + public Map<JCTree, JCTree> getOriginalToCopyMap() { + return Collections.unmodifiableMap(originalToCopy); + } +} diff --git a/src/core/lombok/javac/apt/Processor.java b/src/core/lombok/javac/apt/Processor.java index 1d3d0c34..037f5ba5 100644 --- a/src/core/lombok/javac/apt/Processor.java +++ b/src/core/lombok/javac/apt/Processor.java @@ -157,6 +157,8 @@ public class Processor extends AbstractProcessor { ClassLoader unwrapped = (ClassLoader) f.get(processingEnv); ClassLoader wrapped = new WrappingClassLoader(unwrapped); f.set(processingEnv, wrapped); + } catch (NoSuchFieldException e) { + // Some versions of javac have this (and call close on it), some don't. I guess this one doesn't have it. } catch (Throwable t) { throw Lombok.sneakyThrow(t); } diff --git a/src/core/lombok/javac/handlers/HandleCleanup.java b/src/core/lombok/javac/handlers/HandleCleanup.java index 2c89d9ad..779dd3ea 100644 --- a/src/core/lombok/javac/handlers/HandleCleanup.java +++ b/src/core/lombok/javac/handlers/HandleCleanup.java @@ -30,10 +30,12 @@ import lombok.javac.JavacNode; import org.mangosdk.spi.ProviderFor; +import com.sun.tools.javac.code.TypeTags; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCAssign; +import com.sun.tools.javac.tree.JCTree.JCBinary; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCCase; import com.sun.tools.javac.tree.JCTree.JCCatch; @@ -41,6 +43,7 @@ import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCIf; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTypeCast; @@ -108,11 +111,16 @@ public class HandleCleanup implements JavacAnnotationHandler<Cleanup> { doAssignmentCheck(annotationNode, tryBlock, decl.name); TreeMaker maker = annotationNode.getTreeMaker(); - JCFieldAccess cleanupCall = maker.Select(maker.Ident(decl.name), annotationNode.toName(cleanupName)); - List<JCStatement> finalizerBlock = List.<JCStatement>of(maker.Exec( - maker.Apply(List.<JCExpression>nil(), cleanupCall, List.<JCExpression>nil()))); + JCFieldAccess cleanupMethod = maker.Select(maker.Ident(decl.name), annotationNode.toName(cleanupName)); + List<JCStatement> cleanupCall = List.<JCStatement>of(maker.Exec( + maker.Apply(List.<JCExpression>nil(), cleanupMethod, List.<JCExpression>nil()))); + + JCBinary isNull = maker.Binary(JCTree.NE, maker.Ident(decl.name), maker.Literal(TypeTags.BOT, null)); + + JCIf ifNotNullCleanup = maker.If(isNull, maker.Block(0, cleanupCall), null); + + JCBlock finalizer = maker.Block(0, List.<JCStatement>of(ifNotNullCleanup)); - JCBlock finalizer = maker.Block(0, finalizerBlock); newStatements = newStatements.append(maker.Try(maker.Block(0, tryBlock), List.<JCCatch>nil(), finalizer)); if (blockNode instanceof JCBlock) { diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java index ee049f1f..c5475fb1 100644 --- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java @@ -167,9 +167,13 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd } } + boolean needsCanEqual = false; switch (methodExists("equals", typeNode)) { case NOT_EXISTS: - JCMethodDecl method = createEquals(typeNode, nodesForEquality, callSuper, useFieldsDirectly); + boolean isFinal = (((JCClassDecl)typeNode.get()).mods.flags & Flags.FINAL) != 0; + needsCanEqual = !isFinal || !isDirectDescendantOfObject; + + JCMethodDecl method = createEquals(typeNode, nodesForEquality, callSuper, useFieldsDirectly, needsCanEqual); injectMethod(typeNode, method); break; case EXISTS_BY_LOMBOK: @@ -182,6 +186,18 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd break; } + if (needsCanEqual) { + switch (methodExists("canEqual", typeNode)) { + case NOT_EXISTS: + JCMethodDecl method = createCanEqual(typeNode); + injectMethod(typeNode, method); + break; + case EXISTS_BY_LOMBOK: + case EXISTS_BY_USER: + default: + break; + } + } switch (methodExists("hashCode", typeNode)) { case NOT_EXISTS: JCMethodDecl method = createHashCode(typeNode, nodesForEquality, callSuper, useFieldsDirectly); @@ -196,7 +212,6 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd } break; } - return true; } @@ -314,7 +329,7 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd return maker.TypeCast(maker.TypeIdent(TypeTags.INT), xorBits); } - private JCMethodDecl createEquals(JavacNode typeNode, List<JavacNode> fields, boolean callSuper, boolean useFieldsDirectly) { + private JCMethodDecl createEquals(JavacNode typeNode, List<JavacNode> fields, boolean callSuper, boolean useFieldsDirectly, boolean needsCanEqual) { TreeMaker maker = typeNode.getTreeMaker(); JCClassDecl type = (JCClassDecl) typeNode.get(); @@ -335,27 +350,9 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd maker.Ident(thisName)), returnBool(maker, true), null)); } - /* if (o == null) return false; */ { - statements = statements.append(maker.If(maker.Binary(JCTree.EQ, maker.Ident(oName), - maker.Literal(TypeTags.BOT, null)), returnBool(maker, false), null)); - } - - /* if (o.getClass() != this.getClass()) return false; */ { - Name getClass = typeNode.toName("getClass"); - List<JCExpression> exprNil = List.nil(); - JCExpression oGetClass = maker.Apply(exprNil, maker.Select(maker.Ident(oName), getClass), exprNil); - JCExpression thisGetClass = maker.Apply(exprNil, maker.Select(maker.Ident(thisName), getClass), exprNil); - statements = statements.append( - maker.If(maker.Binary(JCTree.NE, oGetClass, thisGetClass), returnBool(maker, false), null)); - } - - /* if (!super.equals(o)) return false; */ - if (callSuper) { - JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(), - maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")), - List.<JCExpression>of(maker.Ident(oName))); - JCUnary superNotEqual = maker.Unary(JCTree.NOT, callToSuper); - statements = statements.append(maker.If(superNotEqual, returnBool(maker, false), null)); + /* if (!(o instanceof MyType) return false; */ { + JCUnary notInstanceOf = maker.Unary(JCTree.NOT, maker.TypeTest(maker.Ident(oName), maker.Ident(type.name))); + statements = statements.append(maker.If(notInstanceOf, returnBool(maker, false), null)); } /* MyType<?> other = (MyType<?>) o; */ { @@ -379,6 +376,25 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd maker.VarDef(maker.Modifiers(Flags.FINAL), otherName, selfType1, maker.TypeCast(selfType2, maker.Ident(oName)))); } + /* if (!other.canEqual(this)) return false; */ { + if (needsCanEqual) { + List<JCExpression> exprNil = List.nil(); + JCExpression equalityCheck = maker.Apply(exprNil, + maker.Select(maker.Ident(otherName), typeNode.toName("canEqual")), + List.<JCExpression>of(maker.Ident(thisName))); + statements = statements.append(maker.If(maker.Unary(JCTree.NOT, equalityCheck), returnBool(maker, false), null)); + } + } + + /* if (!super.equals(o)) return false; */ + if (callSuper) { + JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(), + maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")), + List.<JCExpression>of(maker.Ident(oName))); + JCUnary superNotEqual = maker.Unary(JCTree.NOT, callToSuper); + statements = statements.append(maker.If(superNotEqual, returnBool(maker, false), null)); + } + for (JavacNode fieldNode : fields) { JCExpression fType = getFieldType(fieldNode, useFieldsDirectly); JCExpression thisFieldAccessor = createFieldAccessor(maker, fieldNode, useFieldsDirectly); @@ -428,6 +444,27 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd JCBlock body = maker.Block(0, statements); return maker.MethodDef(mods, typeNode.toName("equals"), returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), body, null); } + + private JCMethodDecl createCanEqual(JavacNode typeNode) { + /* public boolean canEquals(final java.lang.Object other) { + * return other instanceof MyType; + * } + */ + TreeMaker maker = typeNode.getTreeMaker(); + JCClassDecl type = (JCClassDecl) typeNode.get(); + + JCModifiers mods = maker.Modifiers(Flags.PUBLIC, List.<JCAnnotation>nil()); + JCExpression returnType = maker.TypeIdent(TypeTags.BOOLEAN); + Name canEqualName = typeNode.toName("canEqual"); + JCExpression objectType = chainDots(maker, typeNode, "java", "lang", "Object"); + Name otherName = typeNode.toName("other"); + List<JCVariableDecl> params = List.of(maker.VarDef(maker.Modifiers(Flags.FINAL), otherName, objectType, null)); + + JCBlock body = maker.Block(0, List.<JCStatement>of( + maker.Return(maker.TypeTest(maker.Ident(otherName), maker.Ident(type.name))))); + + return maker.MethodDef(mods, canEqualName, returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), body, null); + } private JCStatement generateCompareFloatOrDouble(JCExpression thisDotField, JCExpression otherDotField, TreeMaker maker, JavacNode node, boolean isDouble) { @@ -442,5 +479,4 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd private JCStatement returnBool(TreeMaker maker, boolean bool) { return maker.Return(maker.Literal(TypeTags.BOOLEAN, bool ? 1 : 0)); } - } diff --git a/src/core/lombok/javac/handlers/HandleLog.java b/src/core/lombok/javac/handlers/HandleLog.java new file mode 100644 index 00000000..03e40d7f --- /dev/null +++ b/src/core/lombok/javac/handlers/HandleLog.java @@ -0,0 +1,203 @@ +/* + * Copyright © 2010 Reinier Zwitserloot, Roel Spilker and Robbert Jan Grootjans. + * + * 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.javac.handlers; + +import static lombok.javac.handlers.JavacHandlerUtil.*; + +import java.lang.annotation.Annotation; + +import lombok.core.AnnotationValues; +import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacNode; +import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult; + +import org.mangosdk.spi.ProviderFor; + +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCFieldAccess; +import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Name; + +public class HandleLog { + + private HandleLog() { + throw new UnsupportedOperationException(); + } + + public static boolean processAnnotation(LoggingFramework framework, AnnotationValues<?> annotation, JavacNode annotationNode) { + markAnnotationAsProcessed(annotationNode, framework.getAnnotationClass()); + +// String loggingClassName = annotation.getRawExpression("value"); +// if (loggingClassName == null) loggingClassName = "void"; +// if (loggingClassName.endsWith(".class")) loggingClassName = loggingClassName.substring(0, loggingClassName.length() - 6); + + JCExpression annotationValue = (JCExpression) annotation.getActualExpression("value"); + JCFieldAccess loggingType = null; + if (annotationValue != null) { + if (!(annotationValue instanceof JCFieldAccess)) return true; + loggingType = (JCFieldAccess) annotationValue; + if (!loggingType.name.contentEquals("class")) return true; + } + + + JavacNode typeNode = annotationNode.up(); + switch (typeNode.getKind()) { + case TYPE: + if ((((JCClassDecl)typeNode.get()).mods.flags & Flags.INTERFACE)!= 0) { + annotationNode.addError("@Log is legal only on classes and enums."); + return true; + } + + if (fieldExists("log", typeNode)!= MemberExistsResult.NOT_EXISTS) { + annotationNode.addWarning("Field 'log' already exists."); + return true; + } + + if (loggingType == null) { + loggingType = selfType(typeNode); + } + createField(framework, typeNode, loggingType); + return true; + default: + annotationNode.addError("@Log is legal only on types."); + return true; + } + } + + private static JCFieldAccess selfType(JavacNode typeNode) { + TreeMaker maker = typeNode.getTreeMaker(); + Name name = ((JCClassDecl) typeNode.get()).name; + return maker.Select(maker.Ident(name), typeNode.toName("class")); + } + + private static boolean createField(LoggingFramework framework, JavacNode typeNode, JCFieldAccess loggingType) { + TreeMaker maker = typeNode.getTreeMaker(); + + // private static final <loggerType> log = <factoryMethod>(<parameter>); + JCExpression loggerType = chainDotsString(maker, typeNode, framework.getLoggerTypeName()); + JCExpression factoryMethod = chainDotsString(maker, typeNode, framework.getLoggerFactoryMethodName()); + + JCExpression loggerName = framework.createFactoryParameter(typeNode, loggingType); + JCMethodInvocation factoryMethodCall = maker.Apply(List.<JCExpression>nil(), factoryMethod, List.<JCExpression>of(loggerName)); + + JCVariableDecl fieldDecl = maker.VarDef( + maker.Modifiers(Flags.PRIVATE | Flags.FINAL | Flags.STATIC), + typeNode.toName("log"), loggerType, factoryMethodCall); + + injectField(typeNode, fieldDecl); + return true; + } + + /** + * Handles the {@link lombok.extern.apachecommons.Log} annotation for javac. + */ + @ProviderFor(JavacAnnotationHandler.class) + public static class HandleCommonsLog implements JavacAnnotationHandler<lombok.extern.apachecommons.Log> { + @Override public boolean handle(AnnotationValues<lombok.extern.apachecommons.Log> annotation, JCAnnotation ast, JavacNode annotationNode) { + return processAnnotation(LoggingFramework.COMMONS, annotation, annotationNode); + } + } + + /** + * Handles the {@link lombok.extern.jul.Log} annotation for javac. + */ + @ProviderFor(JavacAnnotationHandler.class) + public static class HandleJulLog implements JavacAnnotationHandler<lombok.extern.jul.Log> { + @Override public boolean handle(AnnotationValues<lombok.extern.jul.Log> annotation, JCAnnotation ast, JavacNode annotationNode) { + return processAnnotation(LoggingFramework.JUL, annotation, annotationNode); + } + } + + /** + * Handles the {@link lombok.extern.log4j.Log} annotation for javac. + */ + @ProviderFor(JavacAnnotationHandler.class) + public static class HandleLog4jLog implements JavacAnnotationHandler<lombok.extern.log4j.Log> { + @Override public boolean handle(AnnotationValues<lombok.extern.log4j.Log> annotation, JCAnnotation ast, JavacNode annotationNode) { + return processAnnotation(LoggingFramework.LOG4J, annotation, annotationNode); + } + } + + /** + * Handles the {@link lombok.extern.slf4j.Log} annotation for javac. + */ + @ProviderFor(JavacAnnotationHandler.class) + public static class HandleSlf4jLog implements JavacAnnotationHandler<lombok.extern.slf4j.Log> { + @Override public boolean handle(AnnotationValues<lombok.extern.slf4j.Log> annotation, JCAnnotation ast, JavacNode annotationNode) { + return processAnnotation(LoggingFramework.SLF4J, annotation, annotationNode); + } + } + + enum LoggingFramework { + // private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(TargetType.class); + COMMONS(lombok.extern.jul.Log.class, "org.apache.commons.logging.Log", "org.apache.commons.logging.LogFactory.getLog"), + + // private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(TargetType.class.getName()); + JUL(lombok.extern.jul.Log.class, "java.util.logging.Logger", "java.util.logging.Logger.getLogger") { + @Override public JCExpression createFactoryParameter(JavacNode typeNode, JCFieldAccess loggingType) { + TreeMaker maker = typeNode.getTreeMaker(); + JCExpression method = maker.Select(loggingType, typeNode.toName("getName")); + return maker.Apply(List.<JCExpression>nil(), method, List.<JCExpression>nil()); + } + }, + + // private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(TargetType.class); + LOG4J(lombok.extern.jul.Log.class, "org.apache.log4j.Logger", "org.apache.log4j.Logger.getLogger"), + + // private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TargetType.class); + SLF4J(lombok.extern.slf4j.Log.class, "org.slf4j.Logger", "org.slf4j.LoggerFactory.getLogger"), + + ; + + private final Class<? extends Annotation> annotationClass; + private final String loggerTypeName; + private final String loggerFactoryName; + + LoggingFramework(Class<? extends Annotation> annotationClass, String loggerTypeName, String loggerFactoryName) { + this.annotationClass = annotationClass; + this.loggerTypeName = loggerTypeName; + this.loggerFactoryName = loggerFactoryName; + } + + final Class<? extends Annotation> getAnnotationClass() { + return annotationClass; + } + + final String getLoggerTypeName() { + return loggerTypeName; + } + + final String getLoggerFactoryMethodName() { + return loggerFactoryName; + } + + JCExpression createFactoryParameter(JavacNode typeNode, JCFieldAccess loggingType) { + return loggingType; + } + } +} diff --git a/src/core/lombok/javac/handlers/HandleSynchronized.java b/src/core/lombok/javac/handlers/HandleSynchronized.java index 2f900eb8..a095aaf1 100644 --- a/src/core/lombok/javac/handlers/HandleSynchronized.java +++ b/src/core/lombok/javac/handlers/HandleSynchronized.java @@ -89,7 +89,7 @@ public class HandleSynchronized implements JavacAnnotationHandler<Synchronized> JCVariableDecl fieldDecl = maker.VarDef( maker.Modifiers(Flags.PRIVATE | Flags.FINAL | (isStatic ? Flags.STATIC : 0)), methodNode.toName(lockName), objectType, newObjectArray); - injectField(methodNode.up(), fieldDecl); + injectFieldSuppressWarnings(methodNode.up(), fieldDecl); } if (method.body == null) return false; diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java new file mode 100644 index 00000000..5da6fec0 --- /dev/null +++ b/src/core/lombok/javac/handlers/HandleVal.java @@ -0,0 +1,113 @@ +/* + * Copyright © 2010 Reinier Zwitserloot and Roel Spilker. + * + * 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.javac.handlers; + +import lombok.javac.JavacASTAdapter; +import lombok.javac.JavacASTVisitor; +import lombok.javac.JavacNode; +import lombok.javac.JavacResolution; + +import org.mangosdk.spi.ProviderFor; + +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCNewArray; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; + +@ProviderFor(JavacASTVisitor.class) +public class HandleVal extends JavacASTAdapter { + @Override public boolean isResolutionBased() { + return true; + } + + @Override public void visitLocal(JavacNode localNode, JCVariableDecl local) { + if (local.vartype != null && local.vartype.toString().equals("val")) { + JCExpression rhsOfEnhancedForLoop = null; + if (local.init == null) { + JCTree parentRaw = localNode.directUp().get(); + if (parentRaw instanceof JCEnhancedForLoop) { + JCEnhancedForLoop efl = (JCEnhancedForLoop) parentRaw; + if (efl.var == local) rhsOfEnhancedForLoop = efl.expr; + } + } + + if (rhsOfEnhancedForLoop == null && local.init == null) { + localNode.addError("'val' on a local variable requires an initializer expression"); + return; + } + + if (local.init instanceof JCNewArray && ((JCNewArray)local.init).elemtype == null) { + localNode.addError("'val' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })"); + return; + } + + local.mods.flags |= Flags.FINAL; + local.vartype = JavacResolution.createJavaLangObject(localNode.getTreeMaker(), localNode.getAst()); + + Type type; + try { + if (rhsOfEnhancedForLoop == null) { + if (local.init.type == null) { + JavacResolution resolver = new JavacResolution(localNode.getContext()); + type = ((JCExpression) resolver.resolve(localNode).get(local.init)).type; + } else { + type = local.init.type; + } + } else { + if (rhsOfEnhancedForLoop.type == null) { + JavacResolution resolver = new JavacResolution(localNode.getContext()); + type = ((JCExpression) resolver.resolve(localNode.directUp()).get(rhsOfEnhancedForLoop)).type; + } else { + type = rhsOfEnhancedForLoop.type; + } + } + + try { + JCExpression replacement; + + if (rhsOfEnhancedForLoop != null) { + Type componentType = JavacResolution.ifTypeIsIterableToComponent(type, localNode.getAst()); + if (componentType == null) replacement = JavacResolution.createJavaLangObject(localNode.getTreeMaker(), localNode.getAst()); + else replacement = JavacResolution.typeToJCTree(componentType, localNode.getTreeMaker(), localNode.getAst()); + } else { + replacement = JavacResolution.typeToJCTree(type, localNode.getTreeMaker(), localNode.getAst()); + } + + if (replacement != null) { + local.vartype = replacement; + localNode.getAst().setChanged(); + } + else local.vartype = JavacResolution.createJavaLangObject(localNode.getTreeMaker(), localNode.getAst());; + } catch (JavacResolution.TypeNotConvertibleException e) { + localNode.addError("Cannot use 'val' here because initializer expression does not have a representable type: " + e.getMessage()); + local.vartype = JavacResolution.createJavaLangObject(localNode.getTreeMaker(), localNode.getAst());; + } + } catch (RuntimeException e) { + local.vartype = JavacResolution.createJavaLangObject(localNode.getTreeMaker(), localNode.getAst());; + throw e; + } + } + } +} diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 79436327..5dacf2ca 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -392,13 +392,26 @@ public class JavacHandlerUtil { /** * Adds the given new field declaration to the provided type AST Node. + * The field carries the @{@link SuppressWarnings}("all") annotation. + * Also takes care of updating the JavacAST. + */ + public static void injectFieldSuppressWarnings(JavacNode typeNode, JCVariableDecl field) { + injectField(typeNode, field, true); + } + + /** + * Adds the given new field declaration to the provided type AST Node. * * Also takes care of updating the JavacAST. */ public static void injectField(JavacNode typeNode, JCVariableDecl field) { + injectField(typeNode, field, false); + } + + private static void injectField(JavacNode typeNode, JCVariableDecl field, boolean addSuppressWarnings) { JCClassDecl type = (JCClassDecl) typeNode.get(); - addSuppressWarningsAll(field.mods, typeNode, field.pos); + if (addSuppressWarnings) addSuppressWarningsAll(field.mods, typeNode, field.pos); type.defs = type.defs.append(field); typeNode.add(field, Kind.FIELD).recursiveSetHandled(); @@ -476,6 +489,21 @@ public class JavacHandlerUtil { return e; } + + + /** + * In javac, dotted access of any kind, from {@code java.lang.String} to {@code var.methodName} + * is represented by a fold-left of {@code Select} nodes with the leftmost string represented by + * a {@code Ident} node. This method generates such an expression. + * + * For example, maker.Select(maker.Select(maker.Ident(NAME[java]), NAME[lang]), NAME[String]). + * + * @see com.sun.tools.javac.tree.JCTree.JCIdent + * @see com.sun.tools.javac.tree.JCTree.JCFieldAccess + */ + public static JCExpression chainDotsString(TreeMaker maker, JavacNode node, String elems) { + return chainDots(maker, node, elems.split("\\.")); + } /** * Searches the given field node for annotations and returns each one that matches the provided regular expression pattern. |