aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/lombok/javac/JavacResolution.java480
-rw-r--r--src/core/lombok/javac/TreeMirrorMaker.java50
-rw-r--r--src/core/lombok/javac/handlers/HandleVal.java104
-rw-r--r--test/transform/resource/after-delombok/ValComplex.java20
-rw-r--r--test/transform/resource/after-delombok/ValErrors.java8
-rw-r--r--test/transform/resource/after-delombok/ValInFor.java20
-rw-r--r--test/transform/resource/after-delombok/ValLessSimple.java26
-rw-r--r--test/transform/resource/after-delombok/ValSimple.java24
-rw-r--r--test/transform/resource/after-delombok/ValWeirdTypes.java47
-rw-r--r--test/transform/resource/before/ValComplex.java20
-rw-r--r--test/transform/resource/before/ValErrors.java9
-rw-r--r--test/transform/resource/before/ValInFor.java20
-rw-r--r--test/transform/resource/before/ValLessSimple.java31
-rw-r--r--test/transform/resource/before/ValSimple.java26
-rw-r--r--test/transform/resource/before/ValWeirdTypes.java54
-rw-r--r--test/transform/resource/messages-delombok/ValErrors.java.messages2
16 files changed, 941 insertions, 0 deletions
diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java
new file mode 100644
index 00000000..6c60b11e
--- /dev/null
+++ b/src/core/lombok/javac/JavacResolution.java
@@ -0,0 +1,480 @@
+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.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.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;
+ }
+ }
+
+// /**
+// * The {@code Env} object primarily tracks legal symbol names. i.e. its the lexical scope. To build it, we need to go from the top and drill down to the current node,
+// * updating the {@code Env} object at each step. This TreeVisitor does that. Requires {@code enterTrees} to be called first (this is done before processors run, normally).
+// */
+// private static final class EnvChainer extends JCTree.Visitor {
+// private Env<AttrContext> env = null;
+// private Enter enter;
+// private MemberEnter memberEnter;
+// private Attr attr;
+// private JCTree target;
+// private boolean blocksAreInitializers;
+//
+// EnvChainer(Context context) {
+// this.enter = Enter.instance(context);
+// this.memberEnter = MemberEnter.instance(context);
+// this.attr = Attr.instance(context);
+// }
+//
+// Env<AttrContext> get() {
+// return env;
+// }
+//
+// @Override public void visitTopLevel(JCCompilationUnit tree) {
+// env = enter.getTopLevelEnv(tree);
+// }
+//
+// @Override public void visitClassDef(JCClassDecl tree) {
+// // 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);
+// blocksAreInitializers = true;
+// }
+//
+// @Override public void visitMethodDef(JCMethodDecl tree) {
+// env = memberEnter.getMethodEnv(tree, env);
+// blocksAreInitializers = false;
+// if (tree.body != null) visitBlock(tree.body);
+// }
+//
+// @Override public void visitBlock(JCBlock tree) {
+// if (blocksAreInitializers) attr.attribStat(tree, env);
+// for (JCStatement stat : tree.stats) {
+// if (stat == target) return;
+// attr.attribStat(stat, env);
+// }
+// }
+//
+// @Override public void visitTree(JCTree tree) {
+// // Do nothing
+// }
+//
+// public void setTarget(JCTree target) {
+// this.target = target;
+// }
+// };
+
+ 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 void resolveUpTo(JavacNode statementNode) {
+// ArrayDeque<JCTree> stack = new ArrayDeque<JCTree>();
+//
+// {
+// JavacNode n = statementNode;
+// while (n != null) {
+// stack.push(n.get());
+// n = n.up();
+// }
+// }
+//
+// logDisabler.disableLoggers();
+// try {
+// JCTree tree = stack.isEmpty() ? null : stack.pop();
+// while (!stack.isEmpty()) {
+// JCTree target = stack.pop();
+// envChainer.setTarget(target);
+// tree.accept(envChainer);
+// tree = target;
+// }
+// if (tree != null) {
+// envChainer.setTarget(null);
+// tree.accept(envChainer);
+// }
+//
+//// System.out.println("ATTRIBSTAT: " + attr.attribStat(statementNode.get(), envChainer.get()));
+//// if (statementNode.get() instanceof JCVariableDecl) {
+//// System.out.println("Force-tribbing expr");
+//// JCExpression init = ((JCVariableDecl)statementNode.get()).init;
+//// System.out.println("ATTRIBEXPR: " + attr.attribExpr(init, envChainer.get(), Type.noType));
+//// System.out.println("TYPE: " + ((JCVariableDecl)statementNode.get()).init.type);
+//// }
+// } finally {
+// logDisabler.enableLoggers();
+// }
+// }
+//
+// public void resolveExpr(JCExpression expr) {
+// logDisabler.disableLoggers();
+// try {
+// attr.attribExpr(expr, envChainer.get(), Type.noType);
+// } finally {
+// logDisabler.enableLoggers();
+// }
+// }
+
+ public static class TypeNotConvertibleException extends Exception {
+ public TypeNotConvertibleException(String msg) {
+ super(msg);
+ }
+ }
+
+ 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 {
+ // 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/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java
new file mode 100644
index 00000000..a6f22093
--- /dev/null
+++ b/src/core/lombok/javac/handlers/HandleVal.java
@@ -0,0 +1,104 @@
+/*
+ * 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.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;
+ }
+
+ local.mods.flags |= Flags.FINAL;
+ JCExpression oldVarType = local.vartype;
+ 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) {
+ localNode.addError("For now 'val' cannot be used in enhanced for loops. Fixing this is a high priority lombok issue though!");
+ return;
+ // TODO: Fix enhanced for loops - then uncomment a bunch of lines in test/transform/resource/*/ValInFor.java
+ }
+ replacement = JavacResolution.typeToJCTree(type, localNode.getTreeMaker(), localNode.getAst());
+ if (replacement != null) local.vartype = replacement;
+ else local.vartype = oldVarType;
+ } catch (JavacResolution.TypeNotConvertibleException e) {
+ localNode.addError("Cannot use 'val' here because initializer expression does not have a representable type: " + e.getMessage());
+ local.vartype = oldVarType;
+ }
+ } catch (RuntimeException e) {
+ local.vartype = oldVarType;
+ throw e;
+ }
+ }
+ // TODO search for val decls in either kind of for.
+ }
+}
diff --git a/test/transform/resource/after-delombok/ValComplex.java b/test/transform/resource/after-delombok/ValComplex.java
new file mode 100644
index 00000000..54ef5c78
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValComplex.java
@@ -0,0 +1,20 @@
+public class ValComplex {
+ private ValSimple field = new ValSimple();
+ private static final int CONSTANT = 20;
+ public void testReferencingOtherFiles() {
+ final java.lang.String shouldBeString = field.method();
+ final int shouldBeInt = CONSTANT;
+ final java.lang.Object lock = new Object();
+ synchronized (lock) {
+ final int field = 20; //Shadowing
+ final int inner = 10;
+ switch (field) {
+ case 5:
+ final java.lang.String shouldBeString2 = shouldBeString;
+ final int innerInner = inner;
+
+ }
+ }
+ final ValSimple shouldBeValSimple = field; //Unshadowing
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/ValErrors.java b/test/transform/resource/after-delombok/ValErrors.java
new file mode 100644
index 00000000..5ac785ab
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValErrors.java
@@ -0,0 +1,8 @@
+public class ValErrors {
+ public void nullType() {
+ final val a = null;
+ }
+ public void unresolvableExpression() {
+ final val c = d;
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/ValInFor.java b/test/transform/resource/after-delombok/ValInFor.java
new file mode 100644
index 00000000..d97ce732
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValInFor.java
@@ -0,0 +1,20 @@
+public class ValInFor {
+ {
+ final int x = 10;
+ final int x2 = -1;
+ final java.lang.String a = "Hello";
+ for (final int y = x, z = x2; y < 20; y++) {
+ final int q = y;
+ final int w = z;
+ final java.lang.String v = a;
+ }
+ }
+/* public void enhancedFor() {
+ java.util.List<String> list = java.util.Arrays.asList("Hello, World!");
+ for (val shouldBeString : list) {
+ System.out.println(shouldBeString.toLowerCase());
+ val shouldBeString2 = shouldBeString;
+ }
+ }
+*/
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/ValLessSimple.java b/test/transform/resource/after-delombok/ValLessSimple.java
new file mode 100644
index 00000000..678b419e
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValLessSimple.java
@@ -0,0 +1,26 @@
+public class ValLessSimple {
+ private short field2 = 5;
+ private String method() {
+ return "method";
+ }
+ private double method2() {
+ return 2.0;
+ }
+ {
+ System.out.println("Hello");
+ final int z = 20;
+ final int x = 10;
+ final int a = z;
+ final short y = field2;
+ }
+ private void testVal(String param) {
+ final java.lang.String fieldV = field;
+ final int a = 10;
+ final int b = 20;
+ {
+ final java.lang.String methodV = method();
+ final java.lang.String foo = fieldV + methodV;
+ }
+ }
+ private String field = "field";
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/ValSimple.java b/test/transform/resource/after-delombok/ValSimple.java
new file mode 100644
index 00000000..b2783eac
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValSimple.java
@@ -0,0 +1,24 @@
+public class ValSimple {
+ private String field = "field";
+ private short field2 = 5;
+
+ private String method() {
+ return "method";
+ }
+
+ private double method2() {
+ return 2.0;
+ }
+
+ private void testVal(String param) {
+ final java.lang.String fieldV = field;
+ final java.lang.String methodV = method();
+ final java.lang.String paramV = param;
+ final java.lang.String valOfVal = fieldV;
+ final java.lang.String operatorV = fieldV + valOfVal;
+ final short fieldW = field2;
+ final double methodW = method2();
+ byte localVar = 3;
+ final int operatorW = fieldW + localVar;
+ }
+}
diff --git a/test/transform/resource/after-delombok/ValWeirdTypes.java b/test/transform/resource/after-delombok/ValWeirdTypes.java
new file mode 100644
index 00000000..66212906
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValWeirdTypes.java
@@ -0,0 +1,47 @@
+import java.util.*;
+public class ValWeirdTypes<Z> {
+ private final List<Z> fieldList;
+ public void testGenerics() {
+ List<String> list = new ArrayList<String>();
+ list.add("Hello, World!");
+ final java.lang.String shouldBeString = list.get(0);
+ final java.util.List<java.lang.String> shouldBeListOfString = list;
+ final java.util.List<java.lang.String> shouldBeListOfStringToo = Arrays.asList("hello", "world");
+ final java.lang.String shouldBeString2 = shouldBeListOfString.get(0);
+ }
+ public void testGenericsInference() {
+ final java.util.List<java.lang.Object> huh = Collections.emptyList();
+ final java.util.List<java.lang.Number> huh2 = Collections.<Number>emptyList();
+ }
+ public void testPrimitives() {
+ final int x = 10;
+ final long y = 5 + 3L;
+ }
+ public void testAnonymousInnerClass() {
+ final java.lang.Runnable y = new Runnable(){
+ public void run() {
+ }
+ };
+ }
+ public <T extends Number>void testTypeParams(List<T> param) {
+ final T t = param.get(0);
+ final Z z = fieldList.get(0);
+ final java.util.List<T> k = param;
+ final java.util.List<Z> y = fieldList;
+ }
+ public void testBounds(List<? extends Number> lower, List<? super Number> upper) {
+ final java.lang.Number a = lower.get(0);
+ final java.lang.Object b = upper.get(0);
+ final java.util.List<? extends java.lang.Number> c = lower;
+ final java.util.List<? super java.lang.Number> d = upper;
+ List<?> unbound = lower;
+ final java.util.List<?> e = unbound;
+ }
+ public void testCompound() {
+ final java.util.ArrayList<java.lang.String> a = new ArrayList<String>();
+ final java.util.Vector<java.lang.String> b = new Vector<String>();
+ final boolean c = 1 < System.currentTimeMillis();
+ final java.util.AbstractList<java.lang.String> d = c ? a : b;
+ java.util.RandomAccess confirm = c ? a : b;
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/before/ValComplex.java b/test/transform/resource/before/ValComplex.java
new file mode 100644
index 00000000..5f718003
--- /dev/null
+++ b/test/transform/resource/before/ValComplex.java
@@ -0,0 +1,20 @@
+public class ValComplex {
+ private ValSimple field = new ValSimple();
+ private static final int CONSTANT = 20;
+
+ public void testReferencingOtherFiles() {
+ val shouldBeString = field.method();
+ val shouldBeInt = CONSTANT;
+ val lock = new Object();
+ synchronized (lock) {
+ val field = 20; //Shadowing
+ val inner = 10;
+ switch (field) {
+ case 5:
+ val shouldBeString2 = shouldBeString;
+ val innerInner = inner;
+ }
+ }
+ val shouldBeValSimple = field; //Unshadowing
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/before/ValErrors.java b/test/transform/resource/before/ValErrors.java
new file mode 100644
index 00000000..742bca6d
--- /dev/null
+++ b/test/transform/resource/before/ValErrors.java
@@ -0,0 +1,9 @@
+public class ValErrors {
+ public void nullType() {
+ val a = null;
+ }
+
+ public void unresolvableExpression() {
+ val c = d;
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/before/ValInFor.java b/test/transform/resource/before/ValInFor.java
new file mode 100644
index 00000000..af13540e
--- /dev/null
+++ b/test/transform/resource/before/ValInFor.java
@@ -0,0 +1,20 @@
+public class ValInFor {
+ {
+ val x = 10;
+ val x2 = -1;
+ val a = "Hello";
+ for (val y = x, z = x2; y < 20; y++) {
+ val q = y;
+ val w = z;
+ val v = a;
+ }
+ }
+
+/* public void enhancedFor() {
+ java.util.List<String> list = java.util.Arrays.asList("Hello, World!");
+ for (val shouldBeString : list) {
+ System.out.println(shouldBeString.toLowerCase());
+ val shouldBeString2 = shouldBeString;
+ }
+ }*/
+} \ No newline at end of file
diff --git a/test/transform/resource/before/ValLessSimple.java b/test/transform/resource/before/ValLessSimple.java
new file mode 100644
index 00000000..bae7b73b
--- /dev/null
+++ b/test/transform/resource/before/ValLessSimple.java
@@ -0,0 +1,31 @@
+public class ValLessSimple {
+ private short field2 = 5;
+
+ private String method() {
+ return "method";
+ }
+
+ private double method2() {
+ return 2.0;
+ }
+
+ {
+ System.out.println("Hello");
+ val z = 20;
+ val x = 10;
+ val a = z;
+ val y = field2;
+ }
+
+ private void testVal(String param) {
+ val fieldV = field;
+ val a = 10;
+ val b = 20;
+ {
+ val methodV = method();
+ val foo = fieldV + methodV;
+ }
+ }
+
+ private String field = "field";
+}
diff --git a/test/transform/resource/before/ValSimple.java b/test/transform/resource/before/ValSimple.java
new file mode 100644
index 00000000..15508bbc
--- /dev/null
+++ b/test/transform/resource/before/ValSimple.java
@@ -0,0 +1,26 @@
+public class ValSimple {
+ private String field = "field";
+ private short field2 = 5;
+
+ private String method() {
+ return "method";
+ }
+
+ private double method2() {
+ return 2.0;
+ }
+
+ private void testVal(String param) {
+ val fieldV = field;
+ val methodV = method();
+ val paramV = param;
+
+ val valOfVal = fieldV;
+ val operatorV = fieldV + valOfVal;
+
+ val fieldW = field2;
+ val methodW = method2();
+ byte localVar = 3;
+ val operatorW = fieldW + localVar;
+ }
+}
diff --git a/test/transform/resource/before/ValWeirdTypes.java b/test/transform/resource/before/ValWeirdTypes.java
new file mode 100644
index 00000000..6f6eb9db
--- /dev/null
+++ b/test/transform/resource/before/ValWeirdTypes.java
@@ -0,0 +1,54 @@
+import java.util.*;
+
+public class ValWeirdTypes<Z> {
+ private final List<Z> fieldList;
+
+ public void testGenerics() {
+ List<String> list = new ArrayList<String>();
+ list.add("Hello, World!");
+ val shouldBeString = list.get(0);
+ val shouldBeListOfString = list;
+ val shouldBeListOfStringToo = Arrays.asList("hello", "world");
+ val shouldBeString2 = shouldBeListOfString.get(0);
+ }
+
+ public void testGenericsInference() {
+ val huh = Collections.emptyList();
+ val huh2 = Collections.<Number>emptyList();
+ }
+
+ public void testPrimitives() {
+ val x = 10;
+ val y = 5 + 3L;
+ }
+
+ public void testAnonymousInnerClass() {
+ val y = new Runnable() {
+ public void run() {}
+ };
+ }
+
+ public <T extends Number> void testTypeParams(List<T> param) {
+ val t = param.get(0);
+ val z = fieldList.get(0);
+ val k = param;
+ val y = fieldList;
+ }
+
+ public void testBounds(List<? extends Number> lower, List<? super Number> upper) {
+ val a = lower.get(0);
+ val b = upper.get(0);
+ val c = lower;
+ val d = upper;
+ List<?> unbound = lower;
+ val e = unbound;
+ }
+
+ public void testCompound() {
+ val a = new ArrayList<String>();
+ val b = new Vector<String>();
+ val c = 1 < System.currentTimeMillis();
+ val d = c ? a : b;
+ java.util.RandomAccess confirm = c ? a : b;
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/messages-delombok/ValErrors.java.messages b/test/transform/resource/messages-delombok/ValErrors.java.messages
new file mode 100644
index 00000000..feba6912
--- /dev/null
+++ b/test/transform/resource/messages-delombok/ValErrors.java.messages
@@ -0,0 +1,2 @@
+3:21 ERROR Cannot use 'val' here because initializer expression does not have a representable type: <nulltype>
+7:21 ERROR Cannot use 'val' here because initializer expression does not have a representable type: Type cannot be resolved