From 9d8c4e4099bef9b7854cc5d77bc996c3b3bf0e41 Mon Sep 17 00:00:00 2001
From: Reinier Zwitserloot <reinier@zwitserloot.com>
Date: Tue, 6 Oct 2015 00:25:32 +0200
Subject: New feature: FieldDefaults can now be configured to apply to _every_
 file, regardless of annotations.

---
 src/core/lombok/ConfigurationKeys.java             | 14 ++++
 .../eclipse/handlers/HandleFieldDefaults.java      | 81 ++++++++++++++++------
 .../lombok/javac/handlers/HandleFieldDefaults.java | 75 +++++++++++++-------
 src/core/lombok/javac/handlers/HandleVal.java      | 14 ++--
 4 files changed, 130 insertions(+), 54 deletions(-)

(limited to 'src/core')

diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java
index 67d51895..dd6732ed 100644
--- a/src/core/lombok/ConfigurationKeys.java
+++ b/src/core/lombok/ConfigurationKeys.java
@@ -395,6 +395,20 @@ public class ConfigurationKeys {
 	
 	// ----- FieldDefaults -----
 	
+	/**
+	 * lombok configuration: {@code lombok.fieldDefaults.defaultPrivate} = {@code true} | {@code false}.
+	 * 
+	 * If set to <code>true</code> <em>any</em> field without an access modifier or {@code @PackagePrivate} is marked as {@code private} by lombok, in all source files compiled.
+	 */
+	public static final ConfigurationKey<Boolean> FIELD_DEFAULTS_PRIVATE_EVERYWHERE = new ConfigurationKey<Boolean>("lombok.fieldDefaults.defaultPrivate", "If true, fields without any access modifier, in any file (lombok annotated or not) are marked as private. Use @PackagePrivate or an explicit modifier to override this.") {};
+	
+	/**
+	 * lombok configuration: {@code lombok.fieldDefaults.defaultFinal} = {@code true} | {@code false}.
+	 * 
+	 * If set to <code>true</code> <em>any</em> field without {@code @NonFinal} is marked as {@code final} by lombok, in all source files compiled.
+	 */
+	public static final ConfigurationKey<Boolean> FIELD_DEFAULTS_FINAL_EVERYWHERE = new ConfigurationKey<Boolean>("lombok.fieldDefaults.defaultFinal", "If true, fields, in any file (lombok annotated or not) are marked as final. Use @NonFinal to override this.") {};
+	
 	/**
 	 * lombok configuration: {@code lombok.fieldDefaults.flagUsage} = {@code WARNING} | {@code ERROR}.
 	 * 
diff --git a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
index 33e796b7..f97a721b 100644
--- a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
+++ b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
@@ -23,12 +23,17 @@ package lombok.eclipse.handlers;
 
 import static lombok.core.handlers.HandlerUtil.*;
 import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.util.Arrays;
+
 import lombok.AccessLevel;
 import lombok.ConfigurationKeys;
 import lombok.core.AST.Kind;
 import lombok.core.AnnotationValues;
 import lombok.core.HandlerPriority;
-import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.Eclipse;
+import lombok.eclipse.EclipseASTAdapter;
+import lombok.eclipse.EclipseASTVisitor;
 import lombok.eclipse.EclipseNode;
 import lombok.experimental.FieldDefaults;
 import lombok.experimental.NonFinal;
@@ -37,16 +42,19 @@ import lombok.experimental.PackagePrivate;
 import org.eclipse.jdt.internal.compiler.ast.ASTNode;
 import org.eclipse.jdt.internal.compiler.ast.Annotation;
 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
 import org.mangosdk.spi.ProviderFor;
 
 /**
  * Handles the {@code lombok.FieldDefaults} annotation for eclipse.
  */
-@ProviderFor(EclipseAnnotationHandler.class)
+@ProviderFor(EclipseASTVisitor.class)
 @HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier.
-public class HandleFieldDefaults extends EclipseAnnotationHandler<FieldDefaults> {
+public class HandleFieldDefaults extends EclipseASTAdapter {
 	public boolean generateFieldDefaultsForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
 		if (checkForTypeLevelFieldDefaults) {
 			if (hasAnnotation(FieldDefaults.class, typeNode)) {
@@ -105,29 +113,60 @@ public class HandleFieldDefaults extends EclipseAnnotationHandler<FieldDefaults>
 		fieldNode.rebuild();
 	}
 	
-	public void handle(AnnotationValues<FieldDefaults> annotation, Annotation ast, EclipseNode annotationNode) {
-		handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
-		
-		EclipseNode node = annotationNode.up();
-		FieldDefaults instance = annotation.getInstance();
-		AccessLevel level = instance.level();
-		boolean makeFinal = instance.makeFinal();
+	private static final char[] FIELD_DEFAULTS = "FieldDefaults".toCharArray();
+	
+	@Override public void visitType(EclipseNode typeNode, TypeDeclaration type) {
+		AnnotationValues<FieldDefaults> fieldDefaults = null;
+		EclipseNode source = typeNode;
 		
-		if (level == AccessLevel.NONE && !makeFinal) {
-			annotationNode.addError("This does nothing; provide either level or makeFinal or both.");
-			return;
+		boolean levelIsExplicit = false;
+		boolean makeFinalIsExplicit = false;
+		FieldDefaults fd = null;
+		for (EclipseNode jn : typeNode.down()) {
+			if (jn.getKind() != Kind.ANNOTATION) continue;
+			Annotation ann = (Annotation) jn.get();
+			TypeReference typeTree = ann.type;
+			if (typeTree == null) continue;
+			if (typeTree instanceof SingleTypeReference) {
+				char[] t = ((SingleTypeReference) typeTree).token;
+				if (!Arrays.equals(t, FIELD_DEFAULTS)) continue;
+			} else if (typeTree instanceof QualifiedTypeReference) {
+				char[][] t = ((QualifiedTypeReference) typeTree).tokens;
+				if (!Eclipse.nameEquals(t, "lombok.experimental.FieldDefaults")) continue;
+			} else {
+				continue;
+			}
+			
+			if (!typeMatches(FieldDefaults.class, jn, typeTree)) continue;
+			
+			source = jn;
+			fieldDefaults = createAnnotation(FieldDefaults.class, jn);
+			levelIsExplicit = fieldDefaults.isExplicit("level");
+			makeFinalIsExplicit = fieldDefaults.isExplicit("makeFinal");
+			
+			handleExperimentalFlagUsage(jn, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
+			
+			fd = fieldDefaults.getInstance();
+			if (!levelIsExplicit && !makeFinalIsExplicit) {
+				jn.addError("This does nothing; provide either level or makeFinal or both.");
+			}
+			
+			if (levelIsExplicit && fd.level() == AccessLevel.NONE) {
+				jn.addError("AccessLevel.NONE doesn't mean anything here. Pick another value.");
+				levelIsExplicit = false;
+			}
+			break;
 		}
 		
-		if (level == AccessLevel.PACKAGE) {
-			annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field.");
-		}
+		if (fd == null && (type.modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0) return;
 		
-		if (!makeFinal && annotation.isExplicit("makeFinal")) {
-			annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field.");
-		}
+		boolean defaultToPrivate = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_PRIVATE_EVERYWHERE));
+		boolean defaultToFinal = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_FINAL_EVERYWHERE));
 		
-		if (node == null) return;
+		if (!defaultToPrivate && !defaultToFinal && fieldDefaults == null) return;
+		AccessLevel fdAccessLevel = (fieldDefaults != null && levelIsExplicit) ? fd.level() : defaultToPrivate ? AccessLevel.PRIVATE : null;
+		boolean fdToFinal = (fieldDefaults != null && makeFinalIsExplicit) ? fd.makeFinal() : defaultToFinal;
 		
-		generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false);
+		generateFieldDefaultsForType(typeNode, source, fdAccessLevel, fdToFinal, false);
 	}
 }
diff --git a/src/core/lombok/javac/handlers/HandleFieldDefaults.java b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
index 95effded..5fb46afe 100644
--- a/src/core/lombok/javac/handlers/HandleFieldDefaults.java
+++ b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
@@ -31,23 +31,24 @@ import lombok.core.HandlerPriority;
 import lombok.experimental.FieldDefaults;
 import lombok.experimental.NonFinal;
 import lombok.experimental.PackagePrivate;
-import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacASTAdapter;
+import lombok.javac.JavacASTVisitor;
 import lombok.javac.JavacNode;
 
 import org.mangosdk.spi.ProviderFor;
 
 import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
 import com.sun.tools.javac.tree.JCTree.JCAnnotation;
 import com.sun.tools.javac.tree.JCTree.JCClassDecl;
 import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
-import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 
 /**
  * Handles the {@code lombok.FieldDefaults} annotation for eclipse.
  */
-@ProviderFor(JavacAnnotationHandler.class)
+@ProviderFor(JavacASTVisitor.class)
 @HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier.
-public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
+public class HandleFieldDefaults extends JavacASTAdapter {
 	public boolean generateFieldDefaultsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
 		if (checkForTypeLevelFieldDefaults) {
 			if (hasAnnotation(FieldDefaults.class, typeNode)) {
@@ -72,13 +73,13 @@ public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
 			//Skip fields that start with $
 			if (fieldDecl.name.toString().startsWith("$")) continue;
 			
-			setFieldDefaultsForField(field, errorNode.get(), level, makeFinal);
+			setFieldDefaultsForField(field, level, makeFinal);
 		}
 		
 		return true;
 	}
 	
-	public void setFieldDefaultsForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level, boolean makeFinal) {
+	public void setFieldDefaultsForField(JavacNode fieldNode, AccessLevel level, boolean makeFinal) {
 		JCVariableDecl field = (JCVariableDecl) fieldNode.get();
 		if (level != null && level != AccessLevel.NONE) {
 			if ((field.mods.flags & (Flags.PUBLIC | Flags.PRIVATE | Flags.PROTECTED)) == 0) {
@@ -99,31 +100,53 @@ public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
 		fieldNode.rebuild();
 	}
 	
-	@Override public void handle(AnnotationValues<FieldDefaults> annotation, JCAnnotation ast, JavacNode annotationNode) {
-		handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
+	@Override public void visitType(JavacNode typeNode, JCClassDecl type) {
+		AnnotationValues<FieldDefaults> fieldDefaults = null;
+		JavacNode source = typeNode;
 		
-		deleteAnnotationIfNeccessary(annotationNode, FieldDefaults.class);
-		deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
-		JavacNode node = annotationNode.up();
-		FieldDefaults instance = annotation.getInstance();
-		AccessLevel level = instance.level();
-		boolean makeFinal = instance.makeFinal();
-		
-		if (level == AccessLevel.NONE && !makeFinal) {
-			annotationNode.addError("This does nothing; provide either level or makeFinal or both.");
-			return;
+		boolean levelIsExplicit = false;
+		boolean makeFinalIsExplicit = false;
+		FieldDefaults fd = null;
+		for (JavacNode jn : typeNode.down()) {
+			if (jn.getKind() != Kind.ANNOTATION) continue;
+			JCAnnotation ann = (JCAnnotation) jn.get();
+			JCTree typeTree = ann.annotationType;
+			if (typeTree == null) continue;
+			String typeTreeToString = typeTree.toString();
+			if (!typeTreeToString.equals("FieldDefaults") && !typeTreeToString.equals("lombok.experimental.FieldDefaults")) continue;
+			if (!typeMatches(FieldDefaults.class, jn, typeTree)) continue;
+			
+			source = jn;
+			fieldDefaults = createAnnotation(FieldDefaults.class, jn);
+			levelIsExplicit = fieldDefaults.isExplicit("level");
+			makeFinalIsExplicit = fieldDefaults.isExplicit("makeFinal");
+			
+			handleExperimentalFlagUsage(jn, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
+			
+			fd = fieldDefaults.getInstance();
+			if (!levelIsExplicit && !makeFinalIsExplicit) {
+				jn.addError("This does nothing; provide either level or makeFinal or both.");
+			}
+			
+			if (levelIsExplicit && fd.level() == AccessLevel.NONE) {
+				jn.addError("AccessLevel.NONE doesn't mean anything here. Pick another value.");
+				levelIsExplicit = false;
+			}
+			
+			deleteAnnotationIfNeccessary(jn, FieldDefaults.class);
+			deleteImportFromCompilationUnit(jn, "lombok.AccessLevel");
+			break;
 		}
 		
-		if (level == AccessLevel.PACKAGE) {
-			annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field.");
-		}
+		if (fd == null && (type.mods.flags & (Flags.INTERFACE | Flags.ANNOTATION)) != 0) return;
 		
-		if (!makeFinal && annotation.isExplicit("makeFinal")) {
-			annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field.");
-		}
+		boolean defaultToPrivate = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_PRIVATE_EVERYWHERE));
+		boolean defaultToFinal = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_FINAL_EVERYWHERE));
 		
-		if (node == null) return;
+		if (!defaultToPrivate && !defaultToFinal && fieldDefaults == null) return;
+		AccessLevel fdAccessLevel = (fieldDefaults != null && levelIsExplicit) ? fd.level() : defaultToPrivate ? AccessLevel.PRIVATE : null;
+		boolean fdToFinal = (fieldDefaults != null && makeFinalIsExplicit) ? fd.makeFinal() : defaultToFinal;
 		
-		generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false);
+		generateFieldDefaultsForType(typeNode, source, fdAccessLevel, fdToFinal, false);
 	}
 }
diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java
index 9eadd750..337ab2d7 100644
--- a/src/core/lombok/javac/handlers/HandleVal.java
+++ b/src/core/lombok/javac/handlers/HandleVal.java
@@ -51,11 +51,11 @@ import com.sun.tools.javac.util.List;
 @ResolutionResetNeeded
 public class HandleVal extends JavacASTAdapter {
 	@Override public void visitLocal(JavacNode localNode, JCVariableDecl local) {
-		if (local.vartype == null || (!local.vartype.toString().equals("val") && !local.vartype.toString().equals("lombok.val"))) return;
-		
-		JCTree source = local.vartype;
-		
-		if (!typeMatches(val.class, localNode, local.vartype)) return;
+		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");
 		
@@ -88,7 +88,7 @@ public class HandleVal extends JavacASTAdapter {
 		local.mods.flags |= Flags.FINAL;
 		
 		if (!localNode.shouldDeleteLombokAnnotations()) {
-			JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.<JCExpression>nil()), source, localNode.getContext());
+			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);
 		}
 		
@@ -156,7 +156,7 @@ public class HandleVal extends JavacASTAdapter {
 			local.vartype = JavacResolution.createJavaLangObject(localNode.getAst());
 			throw e;
 		} finally {
-			recursiveSetGeneratedBy(local.vartype, source, localNode.getContext());
+			recursiveSetGeneratedBy(local.vartype, typeTree, localNode.getContext());
 		}
 	}
 }
-- 
cgit