diff options
Diffstat (limited to 'src')
23 files changed, 256 insertions, 85 deletions
diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index 04decf69..132bde8d 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -283,7 +283,8 @@ public class ConfigurationKeys { * If set, <em>any</em> usage of {@code val} results in a warning / error. */ public static final ConfigurationKey<FlagUsageType> VAL_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.val.flagUsage", "Emit a warning or error if 'val' is used.") {}; - + public static final ConfigurationKey<FlagUsageType> VAR_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.var.flagUsage", "Emit a warning or error if 'var' is used.") {}; + // ##### Extern ##### // ----- Logging ----- diff --git a/src/core/lombok/core/Version.java b/src/core/lombok/core/Version.java index 20b10601..4fdb7216 100644 --- a/src/core/lombok/core/Version.java +++ b/src/core/lombok/core/Version.java @@ -30,7 +30,7 @@ public class Version { // ** CAREFUL ** - this class must always compile with 0 dependencies (it must not refer to any other sources or libraries). // Note: In 'X.Y.Z', if Z is odd, its a snapshot build built from the repository, so many different 0.10.3 versions can exist, for example. // Official builds always end in an even number. (Since 0.10.2). - private static final String VERSION = "1.16.11"; + private static final String VERSION = "1.16.13"; private static final String RELEASE_NAME = "Edgy Guinea Pig"; // private static final String RELEASE_NAME = "Candid Duck"; diff --git a/src/core/lombok/core/configuration/AllowHelper.java b/src/core/lombok/core/configuration/AllowHelper.java new file mode 100644 index 00000000..3873b055 --- /dev/null +++ b/src/core/lombok/core/configuration/AllowHelper.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.core.configuration; + +import java.util.Collection; +import java.util.Collections; + +import lombok.ConfigurationKeys; + +public final class AllowHelper { + private final static Collection<? extends ConfigurationKey<?>> ALLOWABLE = Collections.singleton(ConfigurationKeys.VAR_FLAG_USAGE); + + private AllowHelper() { + // Prevent instantiation + } + + public static boolean isAllowable(ConfigurationKey<?> key) { + return ALLOWABLE.contains(key); + } +} diff --git a/src/core/lombok/core/configuration/FlagUsageType.java b/src/core/lombok/core/configuration/FlagUsageType.java index b7053b7c..8717c22b 100644 --- a/src/core/lombok/core/configuration/FlagUsageType.java +++ b/src/core/lombok/core/configuration/FlagUsageType.java @@ -23,5 +23,5 @@ package lombok.core.configuration; /** Used for lombok configuration to flag usages of certain lombok feature. */ public enum FlagUsageType { - WARNING, ERROR; + WARNING, ERROR, ALLOW; } diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index a9a4be78..ef4ac7d6 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -43,6 +43,7 @@ import lombok.core.AST; import lombok.core.AnnotationValues; import lombok.core.JavaIdentifiers; import lombok.core.LombokNode; +import lombok.core.configuration.AllowHelper; import lombok.core.configuration.ConfigurationKey; import lombok.core.configuration.FlagUsageType; import lombok.experimental.Accessors; @@ -97,10 +98,14 @@ public class HandlerUtil { public static void handleFlagUsage(LombokNode<?, ?, ?> node, ConfigurationKey<FlagUsageType> key, String featureName) { FlagUsageType fut = node.getAst().readConfiguration(key); + if (fut == null && AllowHelper.isAllowable(key)) { + node.addError("Use of " + featureName + " is disabled by default. Please add '" + key.getKeyName() + " = " + FlagUsageType.ALLOW + "' to 'lombok.config' if you want to enable is."); + } + if (fut != null) { String msg = "Use of " + featureName + " is flagged according to lombok configuration."; if (fut == FlagUsageType.WARNING) node.addWarning(msg); - else node.addError(msg); + else if (fut == FlagUsageType.ERROR) node.addError(msg); } } diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 0ff5a7f6..59d8f587 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -1168,9 +1168,8 @@ public class EclipseHandlerUtil { if (params < minArgs || params > maxArgs) continue; } - if (def.annotations != null) for (Annotation anno : def.annotations) { - if (typeMatches(Tolerate.class, node, anno.type)) continue top; - } + + if (isTolerate(node, def)) continue top; return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK; } @@ -1181,6 +1180,13 @@ public class EclipseHandlerUtil { return MemberExistsResult.NOT_EXISTS; } + public static boolean isTolerate(EclipseNode node, AbstractMethodDeclaration def) { + if (def.annotations != null) for (Annotation anno : def.annotations) { + if (typeMatches(Tolerate.class, node, anno.type)) return true; + } + return false; + } + /** * Checks if there is a (non-default) constructor. In case of multiple constructors (overloading), only * the first constructor decides if EXISTS_BY_USER or EXISTS_BY_LOMBOK is returned. @@ -1198,9 +1204,7 @@ public class EclipseHandlerUtil { if (def instanceof ConstructorDeclaration) { if ((def.bits & ASTNode.IsDefaultConstructor) != 0) continue; - if (def.annotations != null) for (Annotation anno : def.annotations) { - if (typeMatches(Tolerate.class, node, anno.type)) continue top; - } + if (isTolerate(node, def)) continue; return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK; } diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 4e0c4218..afa03538 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -652,7 +652,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { for (int i = 0; i < len; i++) { if (!(existing[i] instanceof MethodDeclaration)) continue; char[] existingName = existing[i].selector; - if (Arrays.equals(name, existingName)) return; + if (Arrays.equals(name, existingName) && !isTolerate(fieldNode, existing[i])) return; } String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName()); diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index 85d1d4ed..a3b0585d 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -48,6 +48,7 @@ import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; +import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CharLiteral; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; @@ -381,6 +382,8 @@ public class HandleConstructor { } private static Expression getDefaultExpr(TypeReference type, int s, int e) { + boolean array = type instanceof ArrayTypeReference; + if (array) return new NullLiteral(s, e); char[] lastToken = type.getLastToken(); if (Arrays.equals(TypeConstants.BOOLEAN, lastToken)) return new FalseLiteral(s, e); if (Arrays.equals(TypeConstants.CHAR, lastToken)) return new CharLiteral(new char[] {'\'', '\\', '0', '\''}, s, e); diff --git a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java index 5ea5a210..702713fe 100644 --- a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java +++ b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 The Project Lombok Authors. + * Copyright (C) 2012-2016 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -97,14 +97,16 @@ public class HandleFieldDefaults extends EclipseASTAdapter { if (level != null && level != AccessLevel.NONE) { if ((field.modifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected)) == 0) { if (!hasAnnotation(PackagePrivate.class, fieldNode)) { - field.modifiers |= EclipseHandlerUtil.toEclipseModifier(level); + if ((field.modifiers & ClassFileConstants.AccStatic) == 0) { + field.modifiers |= EclipseHandlerUtil.toEclipseModifier(level); + } } } } if (makeFinal && (field.modifiers & ClassFileConstants.AccFinal) == 0) { if (!hasAnnotation(NonFinal.class, fieldNode)) { - if ((field.modifiers & ClassFileConstants.AccStatic) == 0 || field.initialization != null) { + if ((field.modifiers & ClassFileConstants.AccStatic) == 0) { field.modifiers |= ClassFileConstants.AccFinal; } } diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java index 14f2fb72..77d678f2 100644 --- a/src/core/lombok/eclipse/handlers/HandleGetter.java +++ b/src/core/lombok/eclipse/handlers/HandleGetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2016 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -183,6 +183,10 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> { errorNode.addError("'lazy' requires the field to be private and final."); return; } + if ((field.modifiers & ClassFileConstants.AccTransient) != 0) { + errorNode.addError("'lazy' is not supported on transient fields."); + return; + } if (field.initialization == null) { errorNode.addError("'lazy' requires field initialization."); return; diff --git a/src/core/lombok/eclipse/handlers/HandleVal.java b/src/core/lombok/eclipse/handlers/HandleVal.java index d4ae417c..d8901067 100644 --- a/src/core/lombok/eclipse/handlers/HandleVal.java +++ b/src/core/lombok/eclipse/handlers/HandleVal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2014 The Project Lombok Authors. + * Copyright (C) 2010-2016 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,7 +21,8 @@ */ package lombok.eclipse.handlers; -import static lombok.core.handlers.HandlerUtil.*; +import static lombok.core.handlers.HandlerUtil.handleFlagUsage; +import static lombok.eclipse.handlers.EclipseHandlerUtil.typeMatches; import lombok.ConfigurationKeys; import lombok.val; import lombok.core.HandlerPriority; @@ -29,11 +30,14 @@ import lombok.eclipse.DeferUntilPostDiet; import lombok.eclipse.EclipseASTAdapter; import lombok.eclipse.EclipseASTVisitor; import lombok.eclipse.EclipseNode; +import lombok.experimental.var; import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.ForStatement; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.NullLiteral; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.mangosdk.spi.ProviderFor; /* @@ -44,8 +48,13 @@ import org.mangosdk.spi.ProviderFor; @HandlerPriority(65536) // 2^16; resolution needs to work, so if the RHS expression is i.e. a call to a generated getter, we have to run after that getter has been generated. public class HandleVal extends EclipseASTAdapter { @Override public void visitLocal(EclipseNode localNode, LocalDeclaration local) { - if (!EclipseHandlerUtil.typeMatches(val.class, localNode, local.type)) return; - handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val"); + TypeReference type = local.type; + boolean isVal = typeMatches(val.class, localNode, type); + boolean isVar = typeMatches(var.class, localNode, type); + if (!(isVal || isVar)) return; + + if (isVal) handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val"); + if (isVar) handleFlagUsage(localNode, ConfigurationKeys.VAR_FLAG_USAGE, "var"); boolean variableOfForEach = false; @@ -54,23 +63,30 @@ public class HandleVal extends EclipseASTAdapter { variableOfForEach = fs.elementVariable == local; } + String annotation = isVal ? "val" : "var"; if (local.initialization == null && !variableOfForEach) { - localNode.addError("'val' on a local variable requires an initializer expression"); + localNode.addError("'" + annotation + "' on a local variable requires an initializer expression"); return; } if (local.initialization instanceof ArrayInitializer) { - localNode.addError("'val' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })"); + localNode.addError("'" + annotation + "' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })"); return; } - if (localNode.directUp().get() instanceof ForStatement) { + if (isVal && localNode.directUp().get() instanceof ForStatement) { localNode.addError("'val' is not allowed in old-style for loops"); return; } if (local.initialization != null && local.initialization.getClass().getName().equals("org.eclipse.jdt.internal.compiler.ast.LambdaExpression")) { - localNode.addError("'val' is not allowed with lambda expressions."); + localNode.addError("'" + annotation + "' is not allowed with lambda expressions."); + return; + } + + if(isVar && local.initialization instanceof NullLiteral) { + localNode.addError("variable initializer is 'null'"); + return; } } } diff --git a/src/core/lombok/experimental/FieldDefaults.java b/src/core/lombok/experimental/FieldDefaults.java index dbc4993b..384abda5 100644 --- a/src/core/lombok/experimental/FieldDefaults.java +++ b/src/core/lombok/experimental/FieldDefaults.java @@ -33,9 +33,9 @@ import lombok.AccessLevel; * <p> * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/FieldDefaults.html">the project lombok features page for @FieldDefaults</a>. * <p> - * If {@code makeFinal} is {@code true}, then each field that is not annotated with {@code @NonFinal} will have the {@code final} modifier added. + * If {@code makeFinal} is {@code true}, then each (instance) field that is not annotated with {@code @NonFinal} will have the {@code final} modifier added. * <p> - * If {@code level} is set, then each field that is package private (i.e. no access modifier) and does not have the {@code @PackagePrivate} annotation will + * If {@code level} is set, then each (instance) field that is package private (i.e. no access modifier) and does not have the {@code @PackagePrivate} annotation will * have the appropriate access level modifier added. */ @Target(ElementType.TYPE) diff --git a/src/core/lombok/experimental/NonFinal.java b/src/core/lombok/experimental/NonFinal.java index 0c31dd2a..12a45d22 100644 --- a/src/core/lombok/experimental/NonFinal.java +++ b/src/core/lombok/experimental/NonFinal.java @@ -28,7 +28,7 @@ import java.lang.annotation.Target; /** * Used to indicate the explicit intention for the annotated entity to <em>not</em> be {@code final}. - * Currently used by {@code FieldDefaults} to avoid having it make a field final. + * Currently used by {@code FieldDefaults} and {@code Value} to avoid having it make a field final. */ @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) diff --git a/src/core/lombok/experimental/PackagePrivate.java b/src/core/lombok/experimental/PackagePrivate.java index bfe5638b..42002818 100644 --- a/src/core/lombok/experimental/PackagePrivate.java +++ b/src/core/lombok/experimental/PackagePrivate.java @@ -28,7 +28,7 @@ import java.lang.annotation.Target; /** * Used to indicate the explicit intention for the annotated entity to have the <em>package private</em> access level. - * Currently used by {@code FieldDefaults} to avoid having it make a field one of {@code public}, {@code protected}, or {@code private}. + * Currently used by {@code FieldDefaults} and {@code Value} to avoid having it make a field one of {@code public}, {@code protected}, or {@code private}. */ @Target({ElementType.TYPE, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.SOURCE) diff --git a/src/core/lombok/experimental/var.java b/src/core/lombok/experimental/var.java new file mode 100644 index 00000000..d8de8b19 --- /dev/null +++ b/src/core/lombok/experimental/var.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010-2013 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.experimental; + +/** + * like val but not final + */ +public @interface var { +} diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index b83e3d5f..9c3c9d03 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -608,8 +608,9 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { for (JavacNode child : builderType.down()) { if (child.getKind() != Kind.METHOD) continue; - Name existingName = ((JCMethodDecl) child.get()).name; - if (existingName.equals(fieldName)) return; + JCMethodDecl methodDecl = (JCMethodDecl) child.get(); + Name existingName = methodDecl.name; + if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) return; } String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName()); diff --git a/src/core/lombok/javac/handlers/HandleDelegate.java b/src/core/lombok/javac/handlers/HandleDelegate.java index 680e7745..49bef769 100644 --- a/src/core/lombok/javac/handlers/HandleDelegate.java +++ b/src/core/lombok/javac/handlers/HandleDelegate.java @@ -312,7 +312,7 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> { for (TypeMirror param : sig.type.getTypeVariables()) { Name name = ((TypeVar) param).tsym.name; - ListBuffer<JCExpression> bounds = types.getBounds((TypeVar) param).isEmpty() ? null : new ListBuffer<JCExpression>(); + ListBuffer<JCExpression> bounds = new ListBuffer<JCExpression>(); for (Type type : types.getBounds((TypeVar) param)) { bounds.append(JavacResolution.typeToJCTree(type, annotation.getAst(), true)); } diff --git a/src/core/lombok/javac/handlers/HandleFieldDefaults.java b/src/core/lombok/javac/handlers/HandleFieldDefaults.java index 12c22059..52f6c39c 100644 --- a/src/core/lombok/javac/handlers/HandleFieldDefaults.java +++ b/src/core/lombok/javac/handlers/HandleFieldDefaults.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 The Project Lombok Authors. + * Copyright (C) 2012-2016 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -84,14 +84,16 @@ public class HandleFieldDefaults extends JavacASTAdapter { if (level != null && level != AccessLevel.NONE) { if ((field.mods.flags & (Flags.PUBLIC | Flags.PRIVATE | Flags.PROTECTED)) == 0) { if (!hasAnnotationAndDeleteIfNeccessary(PackagePrivate.class, fieldNode)) { - field.mods.flags |= toJavacModifier(level); + if ((field.mods.flags & Flags.STATIC) == 0) { + field.mods.flags |= toJavacModifier(level); + } } } } if (makeFinal && (field.mods.flags & Flags.FINAL) == 0) { if (!hasAnnotationAndDeleteIfNeccessary(NonFinal.class, fieldNode)) { - if ((field.mods.flags & Flags.STATIC) == 0 || field.init != null) { + if ((field.mods.flags & Flags.STATIC) == 0) { field.mods.flags |= Flags.FINAL; } } diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index a330dbc1..0a2fe362 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2016 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -182,6 +182,10 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { source.addError("'lazy' requires the field to be private and final."); return; } + if ((fieldDecl.mods.flags & Flags.TRANSIENT) != 0) { + source.addError("'lazy' is not supported on transient fields."); + return; + } if (fieldDecl.init == null) { source.addError("'lazy' requires field initialization."); return; diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java index 337ab2d7..2976eabe 100644 --- a/src/core/lombok/javac/handlers/HandleVal.java +++ b/src/core/lombok/javac/handlers/HandleVal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2015 The Project Lombok Authors. + * Copyright (C) 2010-2016 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,11 +21,12 @@ */ package lombok.javac.handlers; -import static lombok.core.handlers.HandlerUtil.*; +import static lombok.core.handlers.HandlerUtil.handleFlagUsage; import static lombok.javac.handlers.JavacHandlerUtil.*; import lombok.ConfigurationKeys; import lombok.val; import lombok.core.HandlerPriority; +import lombok.experimental.var; import lombok.javac.JavacASTAdapter; import lombok.javac.JavacASTVisitor; import lombok.javac.JavacNode; @@ -42,6 +43,7 @@ import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCForLoop; +import com.sun.tools.javac.tree.JCTree.JCLiteral; import com.sun.tools.javac.tree.JCTree.JCNewArray; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.List; @@ -50,21 +52,31 @@ import com.sun.tools.javac.util.List; @HandlerPriority(65536) // 2^16; resolution needs to work, so if the RHS expression is i.e. a call to a generated getter, we have to run after that getter has been generated. @ResolutionResetNeeded public class HandleVal extends JavacASTAdapter { - @Override public void visitLocal(JavacNode localNode, JCVariableDecl local) { + + private static boolean eq(String typeTreeToString, String key) { + return (typeTreeToString.equals(key) || typeTreeToString.equals("lombok." + key)); + } + + @Override + public void visitLocal(JavacNode localNode, JCVariableDecl local) { JCTree typeTree = local.vartype; if (typeTree == null) return; String typeTreeToString = typeTree.toString(); - if (!typeTreeToString.equals("val") && !typeTreeToString.equals("lombok.val")) return; - if (!typeMatches(val.class, localNode, typeTree)) return; - - handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val"); - + + if (!(eq(typeTreeToString, "val") || eq(typeTreeToString, "var"))) return; + boolean isVal = typeMatches(val.class, localNode, typeTree); + boolean isVar = typeMatches(var.class, localNode, typeTree); + if (!(isVal || isVar)) return; + + if (isVal) handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val"); + if (isVar) handleFlagUsage(localNode, ConfigurationKeys.VAR_FLAG_USAGE, "var"); + JCTree parentRaw = localNode.directUp().get(); - if (parentRaw instanceof JCForLoop) { + if (isVal && parentRaw instanceof JCForLoop) { localNode.addError("'val' is not allowed in old-style for loops"); return; } - + JCExpression rhsOfEnhancedForLoop = null; if (local.init == null) { if (parentRaw instanceof JCEnhancedForLoop) { @@ -72,21 +84,26 @@ public class HandleVal extends JavacASTAdapter { if (efl.var == local) rhsOfEnhancedForLoop = efl.expr; } } - + + final String annotation = typeTreeToString; if (rhsOfEnhancedForLoop == null && local.init == null) { - localNode.addError("'val' on a local variable requires an initializer expression"); + localNode.addError("'" + annotation + "' 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 { ... })"); + localNode.addError("'" + annotation + "' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })"); return; } - - if (localNode.shouldDeleteLombokAnnotations()) JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, "lombok.val"); - - local.mods.flags |= Flags.FINAL; - + + if (localNode.shouldDeleteLombokAnnotations()) { + JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, val.class.getName()); + JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, var.class.getName()); + } + + if (isVal) local.mods.flags |= Flags.FINAL; + if (!localNode.shouldDeleteLombokAnnotations()) { JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.<JCExpression>nil()), typeTree, localNode.getContext()); local.mods.annotations = local.mods.annotations == null ? List.of(valAnnotation) : local.mods.annotations.append(valAnnotation); @@ -102,6 +119,9 @@ public class HandleVal extends JavacASTAdapter { try { if (rhsOfEnhancedForLoop == null) { if (local.init.type == null) { + if (isVar && local.init instanceof JCLiteral && ((JCLiteral) local.init).value == null) { + localNode.addError("variable initializer is 'null'"); + } JavacResolution resolver = new JavacResolution(localNode.getContext()); try { type = ((JCExpression) resolver.resolveMethodMember(localNode).get(local.init)).type; @@ -149,7 +169,7 @@ public class HandleVal extends JavacASTAdapter { } localNode.getAst().setChanged(); } catch (JavacResolution.TypeNotConvertibleException e) { - localNode.addError("Cannot use 'val' here because initializer expression does not have a representable type: " + e.getMessage()); + localNode.addError("Cannot use '" + annotation + "' here because initializer expression does not have a representable type: " + e.getMessage()); local.vartype = JavacResolution.createJavaLangObject(localNode.getAst()); } } catch (RuntimeException e) { diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index fdc8a262..efa67604 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -605,10 +605,7 @@ public class JavacHandlerUtil { if (params < minArgs || params > maxArgs) continue; } - List<JCAnnotation> annotations = md.getModifiers().getAnnotations(); - if (annotations != null) for (JCAnnotation anno : annotations) { - if (typeMatches(Tolerate.class, node, anno.getAnnotationType())) continue top; - } + if (isTolerate(node, md)) continue top; return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK; } @@ -619,6 +616,14 @@ public class JavacHandlerUtil { return MemberExistsResult.NOT_EXISTS; } + public static boolean isTolerate(JavacNode node, JCTree.JCMethodDecl md) { + List<JCAnnotation> annotations = md.getModifiers().getAnnotations(); + if (annotations != null) for (JCTree.JCAnnotation anno : annotations) { + if (typeMatches(Tolerate.class, node, anno.getAnnotationType())) return true; + } + return false; + } + /** * Checks if there is a (non-default) constructor. In case of multiple constructors (overloading), only * the first constructor decides if EXISTS_BY_USER or EXISTS_BY_LOMBOK is returned. @@ -634,10 +639,7 @@ public class JavacHandlerUtil { JCMethodDecl md = (JCMethodDecl) def; if (md.name.contentEquals("<init>")) { if ((md.mods.flags & Flags.GENERATEDCONSTR) != 0) continue; - List<JCAnnotation> annotations = md.getModifiers().getAnnotations(); - if (annotations != null) for (JCAnnotation anno : annotations) { - if (typeMatches(Tolerate.class, node, anno.getAnnotationType())) continue top; - } + if (isTolerate(node, md)) continue; return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK; } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java index 2b8dfbaa..e4dd7b26 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java @@ -21,11 +21,6 @@ */ package lombok.eclipse.agent; -import static lombok.eclipse.handlers.EclipseHandlerUtil.*; -import static lombok.eclipse.Eclipse.*; - -import java.lang.reflect.Field; - import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; @@ -43,6 +38,11 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; +import java.lang.reflect.Field; + +import static lombok.eclipse.Eclipse.poss; +import static lombok.eclipse.handlers.EclipseHandlerUtil.makeType; + public class PatchVal { // This is half of the work for 'val' support - the other half is in PatchValEclipse. This half is enough for ecj. @@ -84,31 +84,44 @@ public class PatchVal { return true; } - public static boolean couldBeVal(TypeReference ref) { + public static boolean couldBe(String key, TypeReference ref) { + String[] keyParts = key.split("\\."); if (ref instanceof SingleTypeReference) { char[] token = ((SingleTypeReference)ref).token; - return matches("val", token); + return matches(keyParts[keyParts.length - 1], token); } if (ref instanceof QualifiedTypeReference) { char[][] tokens = ((QualifiedTypeReference)ref).tokens; - if (tokens == null || tokens.length != 2) return false; - return matches("lombok", tokens[0]) && matches("val", tokens[1]); + if (keyParts.length != tokens.length) return false; + for(int i = 0; i < tokens.length; ++i) { + String part = keyParts[i]; + char[] token = tokens[i]; + if (!matches(part, token)) return false; + } + return true; } return false; } - - private static boolean isVal(TypeReference ref, BlockScope scope) { - if (!couldBeVal(ref)) return false; - + + private static boolean is(TypeReference ref, BlockScope scope, String key) { + if (!couldBe(key, ref)) return false; + TypeBinding resolvedType = ref.resolvedType; if (resolvedType == null) resolvedType = ref.resolveType(scope, false); if (resolvedType == null) return false; char[] pkg = resolvedType.qualifiedPackageName(); char[] nm = resolvedType.qualifiedSourceName(); - return matches("lombok", pkg) && matches("val", nm); + int pkgFullLength = pkg.length > 0 ? pkg.length + 1: 0; + char[] fullName = new char[pkgFullLength + nm.length]; + if(pkg.length > 0) { + System.arraycopy(pkg, 0, fullName, 0, pkg.length); + fullName[pkg.length] = '.'; + } + System.arraycopy(nm, 0, fullName, pkgFullLength, nm.length); + return matches(key, fullName); } public static final class Reflection { @@ -132,13 +145,17 @@ public class PatchVal { if (local == null || !LocalDeclaration.class.equals(local.getClass())) return false; boolean decomponent = false; - if (!isVal(local.type, scope)) return false; + boolean val = isVal(local, scope); + boolean var = isVar(local, scope); + if (!(val || var)) return false; StackTraceElement[] st = new Throwable().getStackTrace(); for (int i = 0; i < st.length - 2 && i < 10; i++) { if (st[i].getClassName().equals("lombok.launch.PatchFixesHider$Val")) { - if (st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") && - st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement")) return false; + boolean valInForStatement = val && + st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") && + st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement"); + if (valInForStatement) return false; break; } } @@ -187,23 +204,33 @@ public class PatchVal { } } - local.modifiers |= ClassFileConstants.AccFinal; + if(val) local.modifiers |= ClassFileConstants.AccFinal; local.annotations = addValAnnotation(local.annotations, local.type, scope); local.type = replacement != null ? replacement : new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(local.type, 3)); return false; } + private static boolean isVar(LocalDeclaration local, BlockScope scope) { + return is(local.type, scope, "lombok.experimental.var"); + } + + private static boolean isVal(LocalDeclaration local, BlockScope scope) { + return is(local.type, scope, "lombok.val"); + } + public static boolean handleValForForEach(ForeachStatement forEach, BlockScope scope) { if (forEach.elementVariable == null) return false; - if (!isVal(forEach.elementVariable.type, scope)) return false; + boolean val = isVal(forEach.elementVariable, scope); + boolean var = isVar(forEach.elementVariable, scope); + if (!(val || var)) return false; TypeBinding component = getForEachComponentType(forEach.collection, scope); if (component == null) return false; TypeReference replacement = makeType(component, forEach.elementVariable.type, false); - forEach.elementVariable.modifiers |= ClassFileConstants.AccFinal; + if (val) forEach.elementVariable.modifiers |= ClassFileConstants.AccFinal; forEach.elementVariable.annotations = addValAnnotation(forEach.elementVariable.annotations, forEach.elementVariable.type, scope); forEach.elementVariable.type = replacement != null ? replacement : new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(forEach.elementVariable.type, 3)); diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java index 7d5f36f4..505eb767 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java @@ -47,6 +47,7 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.parser.Parser; @@ -65,7 +66,9 @@ public class PatchValEclipse { ForeachStatement foreachDecl = (ForeachStatement) astStack[astPtr]; ASTNode init = foreachDecl.collection; if (init == null) return; - if (foreachDecl.elementVariable == null || !PatchVal.couldBeVal(foreachDecl.elementVariable.type)) return; + boolean val = couldBeVal(foreachDecl.elementVariable.type); + boolean var = couldBeVar(foreachDecl.elementVariable.type); + if (foreachDecl.elementVariable == null || !(val || var)) return; try { if (Reflection.iterableCopyField != null) Reflection.iterableCopyField.set(foreachDecl.elementVariable, init); @@ -88,7 +91,9 @@ public class PatchValEclipse { if (!(variableDecl instanceof LocalDeclaration)) return; ASTNode init = variableDecl.initialization; if (init == null) return; - if (!PatchVal.couldBeVal(variableDecl.type)) return; + boolean val = couldBeVal(variableDecl.type); + boolean var = couldBeVar(variableDecl.type); + if (!(val || var)) return; try { if (Reflection.initCopyField != null) Reflection.initCopyField.set(variableDecl, init); @@ -97,6 +102,10 @@ public class PatchValEclipse { } } + private static boolean couldBeVar(TypeReference type) { + return PatchVal.couldBe("lombok.experimental.var", type); + } + public static void addFinalAndValAnnotationToSingleVariableDeclaration(Object converter, SingleVariableDeclaration out, LocalDeclaration in) { @SuppressWarnings("unchecked") List<IExtendedModifier> modifiers = out.modifiers(); addFinalAndValAnnotationToModifierList(converter, modifiers, out.getAST(), in); @@ -115,7 +124,7 @@ public class PatchValEclipse { Annotation valAnnotation = null; for (Annotation ann : in.annotations) { - if (PatchVal.couldBeVal(ann.type)) { + if (couldBeVal(ann.type)) { found = true; valAnnotation = ann; break; @@ -167,6 +176,10 @@ public class PatchValEclipse { } } + private static boolean couldBeVal(TypeReference type) { + return PatchVal.couldBe("lombok.val", type); + } + public static Modifier createModifier(AST ast, ModifierKeyword keyword, int start, int end) { Modifier modifier = null; try { |