diff options
Diffstat (limited to 'src')
22 files changed, 719 insertions, 163 deletions
diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index ba66649b..b8cd442a 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -695,4 +695,10 @@ public class ConfigurationKeys { */ public static final ConfigurationKey<CheckerFrameworkVersion> CHECKER_FRAMEWORK = new ConfigurationKey<CheckerFrameworkVersion>("checkerframework", "If set with the version of checkerframework.org (in major.minor, or just 'true' for the latest supported version), create relevant checkerframework.org annotations for code lombok generates (default: false).") {}; + /** + * lombok configuration: {@code lombok.standardException.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @StandardException} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> STANDARD_EXCEPTION_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.standardException.flagUsage", "Emit a warning or error if @StandardException is used.") {}; } diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java index 0036ec23..4a0854a4 100644 --- a/src/core/lombok/eclipse/EclipseAST.java +++ b/src/core/lombok/eclipse/EclipseAST.java @@ -232,6 +232,14 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { } } + public void setSource(char[] source) { + this.source = source; + } + + public char[] getSource() { + return source; + } + /** * Eclipse starts off with a 'diet' parse which leaves method bodies blank, amongst other shortcuts. * @@ -309,6 +317,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { } private final CompilationUnitDeclaration compilationUnitDeclaration; + private char[] source; private boolean completeParse; private static String toFileName(CompilationUnitDeclaration ast) { diff --git a/src/core/lombok/eclipse/TransformEclipseAST.java b/src/core/lombok/eclipse/TransformEclipseAST.java index 59a0709e..201d4695 100644 --- a/src/core/lombok/eclipse/TransformEclipseAST.java +++ b/src/core/lombok/eclipse/TransformEclipseAST.java @@ -182,6 +182,7 @@ public class TransformEclipseAST { DebugSnapshotStore.INSTANCE.snapshot(ast, "transform entry"); long histoToken = lombokTracker == null ? 0L : lombokTracker.start(); EclipseAST existing = getAST(ast, false); + existing.setSource(parser.scanner.getSource()); new TransformEclipseAST(existing).go(); if (lombokTracker != null) lombokTracker.end(histoToken); DebugSnapshotStore.INSTANCE.snapshot(ast, "transform exit"); diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 70d98cc6..5eea980c 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -95,7 +95,6 @@ import org.eclipse.jdt.internal.compiler.ast.TypeParameter; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.CaptureBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; @@ -2689,11 +2688,13 @@ public class EclipseHandlerUtil { return annotations; } - public static String getDocComment(CompilationUnitDeclaration cud, ASTNode node) { - ICompilationUnit compilationUnit = cud.compilationResult.compilationUnit; + public static String getDocComment(EclipseNode eclipseNode) { + if (eclipseNode.getAst().getSource() == null) return null; + + final ASTNode node = eclipseNode.get(); if (node instanceof FieldDeclaration) { FieldDeclaration fieldDeclaration = (FieldDeclaration) node; - char[] rawContent = CharOperation.subarray(compilationUnit.getContents(), fieldDeclaration.declarationSourceStart, fieldDeclaration.declarationSourceEnd); + char[] rawContent = CharOperation.subarray(eclipseNode.getAst().getSource(), fieldDeclaration.declarationSourceStart, fieldDeclaration.declarationSourceEnd); String rawContentString = new String(rawContent); int startIndex = rawContentString.indexOf("/**"); int endIndex = rawContentString.indexOf("*/"); @@ -2739,14 +2740,13 @@ public class EclipseHandlerUtil { public static enum CopyJavadoc { VERBATIM { - @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { - return getDocComment(cu, node.get()); + @Override public String apply(final EclipseNode node) { + return getDocComment(node); } }, GETTER { - @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { - final ASTNode n = node.get(); - String javadoc = getDocComment(cu, n); + @Override public String apply(final EclipseNode node) { + String javadoc = getDocComment(node); // step 1: Check if there is a 'GETTER' section. If yes, that becomes the new method's javadoc. String out = getJavadocSection(javadoc, "GETTER"); final boolean sectionBased = out != null; @@ -2757,26 +2757,25 @@ public class EclipseHandlerUtil { } }, SETTER { - @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { - return applySetter(cu, node, "SETTER"); + @Override public String apply(final EclipseNode node) { + return applySetter(node, "SETTER"); } }, WITH { - @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { - return addReturnsUpdatedSelfIfNeeded(applySetter(cu, node, "WITH|WITHER")); + @Override public String apply(final EclipseNode node) { + return addReturnsUpdatedSelfIfNeeded(applySetter(node, "WITH|WITHER")); } }, WITH_BY { - @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { - return applySetter(cu, node, "WITHBY|WITH_BY"); + @Override public String apply( final EclipseNode node) { + return applySetter(node, "WITHBY|WITH_BY"); } }; - public abstract String apply(final CompilationUnitDeclaration cu, final EclipseNode node); + public abstract String apply(final EclipseNode node); - private static String applySetter(final CompilationUnitDeclaration cu, EclipseNode node, String sectionName) { - final ASTNode n = node.get(); - String javadoc = getDocComment(cu, n); + private static String applySetter(EclipseNode node, String sectionName) { + String javadoc = getDocComment(node); // step 1: Check if there is a 'SETTER' section. If yes, that becomes the new method's javadoc. String out = getJavadocSection(javadoc, sectionName); final boolean sectionBased = out != null; @@ -2824,7 +2823,7 @@ public class EclipseHandlerUtil { if (copyMode == null) copyMode = CopyJavadoc.VERBATIM; try { CompilationUnitDeclaration cud = ((CompilationUnitDeclaration) from.top().get()); - String newJavadoc = copyMode.apply(cud, from); + String newJavadoc = copyMode.apply(from); if (forceAddReturn) { newJavadoc = addReturnsThisIfNeeded(newJavadoc); } diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index c002c4ca..dab774f3 100755 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -1057,7 +1057,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { private void copyJavadocFromParam(EclipseNode from, MethodDeclaration to, TypeDeclaration type, String param) { try { CompilationUnitDeclaration cud = (CompilationUnitDeclaration) from.top().get(); - String methodComment = getDocComment(cud, from.get()); + String methodComment = getDocComment(from); String newJavadoc = addReturnsThisIfNeeded(getParamJavadoc(methodComment, param)); setDocComment(cud, type, to, newJavadoc); } catch (Exception ignore) {} diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index 753c489c..12a3c315 100755 --- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -250,6 +250,8 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH setGeneratedBy(hashCodeCacheDecl.type, source); } + private static final char[] HASH_CODE = "hashCode".toCharArray(), FLOAT_TO_INT_BITS = "floatToIntBits".toCharArray(), DOUBLE_TO_LONG_BITS = "doubleToLongBits".toCharArray(); + public MethodDeclaration createHashCode(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> members, boolean callSuper, boolean cacheHashCode, ASTNode source, FieldAccess fieldAccess) { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; @@ -373,7 +375,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH floatToIntBits.sourceStart = pS; floatToIntBits.sourceEnd = pE; setGeneratedBy(floatToIntBits, source); floatToIntBits.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA_LANG_FLOAT); - floatToIntBits.selector = "floatToIntBits".toCharArray(); + floatToIntBits.selector = FLOAT_TO_INT_BITS; floatToIntBits.arguments = new Expression[] { fieldAccessor }; statements.add(createResultCalculation(source, floatToIntBits)); } else if (Arrays.equals(TypeConstants.DOUBLE, token)) { @@ -382,7 +384,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH doubleToLongBits.sourceStart = pS; doubleToLongBits.sourceEnd = pE; setGeneratedBy(doubleToLongBits, source); doubleToLongBits.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA_LANG_DOUBLE); - doubleToLongBits.selector = "doubleToLongBits".toCharArray(); + doubleToLongBits.selector = DOUBLE_TO_LONG_BITS; doubleToLongBits.arguments = new Expression[] { fieldAccessor }; statements.add(createLocalDeclaration(source, dollarFieldName, TypeReference.baseTypeReference(TypeIds.T_long, 0), doubleToLongBits)); SingleNameReference copy1 = new SingleNameReference(dollarFieldName, p); @@ -406,7 +408,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH hashCodeCall.sourceStart = pS; hashCodeCall.sourceEnd = pE; setGeneratedBy(hashCodeCall, source); hashCodeCall.receiver = copy1; - hashCodeCall.selector = "hashCode".toCharArray(); + hashCodeCall.selector = HASH_CODE; NullLiteral nullLiteral = new NullLiteral(pS, pE); setGeneratedBy(nullLiteral, source); EqualExpression objIsNull = new EqualExpression(copy2, nullLiteral, OperatorIds.EQUAL_EQUAL); diff --git a/src/core/lombok/eclipse/handlers/HandleStandardException.java b/src/core/lombok/eclipse/handlers/HandleStandardException.java new file mode 100755 index 00000000..def6e495 --- /dev/null +++ b/src/core/lombok/eclipse/handlers/HandleStandardException.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2021 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.eclipse.handlers; + +import lombok.AccessLevel; +import lombok.ConfigurationKeys; +import lombok.experimental.StandardException; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.EclipseNode; +import lombok.eclipse.handlers.EclipseHandlerUtil.*; +import lombok.spi.Provides; +import org.eclipse.jdt.internal.compiler.ast.*; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; + +import java.lang.reflect.Modifier; +import java.util.*; + +import static lombok.core.handlers.HandlerUtil.handleFlagUsage; +import static lombok.eclipse.Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; +import static lombok.eclipse.handlers.EclipseHandlerUtil.*; + +@Provides +public class HandleStandardException extends EclipseAnnotationHandler<StandardException> { + @Override + public void handle(AnnotationValues<StandardException> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.STANDARD_EXCEPTION_FLAG_USAGE, "@StandardException"); + + EclipseNode typeNode = annotationNode.up(); + if (!isClass(typeNode)) { + annotationNode.addError("@StandardException is only supported on a class"); + return; + } + + TypeDeclaration classDef = (TypeDeclaration) typeNode.get(); + if (classDef.superclass == null) { + annotationNode.addError("@StandardException requires that you extend a Throwable type"); + return; + } + + AccessLevel access = annotation.getInstance().access(); + + generateNoArgsConstructor(typeNode, access, annotationNode); + generateMsgOnlyConstructor(typeNode, access, annotationNode); + generateCauseOnlyConstructor(typeNode, access, annotationNode); + generateFullConstructor(typeNode, access, annotationNode); + } + + private void generateNoArgsConstructor(EclipseNode typeNode, AccessLevel level, EclipseNode source) { + if (hasConstructor(typeNode) != MemberExistsResult.NOT_EXISTS) return; + int pS = source.get().sourceStart, pE = source.get().sourceEnd; + + ExplicitConstructorCall explicitCall = new ExplicitConstructorCall(ExplicitConstructorCall.This); + explicitCall.arguments = new Expression[] {new NullLiteral(pS, pE), new NullLiteral(pS, pE)}; + ConstructorDeclaration constructor = createConstructor(level, typeNode, false, false, source, explicitCall, null); + injectMethod(typeNode, constructor); + } + + private void generateMsgOnlyConstructor(EclipseNode typeNode, AccessLevel level, EclipseNode source) { + if (hasConstructor(typeNode, String.class) != MemberExistsResult.NOT_EXISTS) return; + int pS = source.get().sourceStart, pE = source.get().sourceEnd; + long p = (long) pS << 32 | pE; + + ExplicitConstructorCall explicitCall = new ExplicitConstructorCall(ExplicitConstructorCall.This); + explicitCall.arguments = new Expression[] {new SingleNameReference(MESSAGE, p), new NullLiteral(pS, pE)}; + ConstructorDeclaration constructor = createConstructor(level, typeNode, true, false, source, explicitCall, null); + injectMethod(typeNode, constructor); + } + + private void generateCauseOnlyConstructor(EclipseNode typeNode, AccessLevel level, EclipseNode source) { + if (hasConstructor(typeNode, Throwable.class) != MemberExistsResult.NOT_EXISTS) return; + int pS = source.get().sourceStart, pE = source.get().sourceEnd; + long p = (long) pS << 32 | pE; + + ExplicitConstructorCall explicitCall = new ExplicitConstructorCall(ExplicitConstructorCall.This); + Expression causeNotNull = new EqualExpression(new SingleNameReference(CAUSE, p), new NullLiteral(pS, pE), OperatorIds.NOT_EQUAL); + MessageSend causeDotGetMessage = new MessageSend(); + causeDotGetMessage.sourceStart = pS; causeDotGetMessage.sourceEnd = pE; + causeDotGetMessage.receiver = new SingleNameReference(CAUSE, p); + causeDotGetMessage.selector = GET_MESSAGE; + Expression messageExpr = new ConditionalExpression(causeNotNull, causeDotGetMessage, new NullLiteral(pS, pE)); + explicitCall.arguments = new Expression[] {messageExpr, new SingleNameReference(CAUSE, p)}; + ConstructorDeclaration constructor = createConstructor(level, typeNode, false, true, source, explicitCall, null); + injectMethod(typeNode, constructor); + } + + private void generateFullConstructor(EclipseNode typeNode, AccessLevel level, EclipseNode source) { + if (hasConstructor(typeNode, String.class, Throwable.class) != MemberExistsResult.NOT_EXISTS) return; + int pS = source.get().sourceStart, pE = source.get().sourceEnd; + long p = (long) pS << 32 | pE; + + ExplicitConstructorCall explicitCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); + explicitCall.arguments = new Expression[] {new SingleNameReference(MESSAGE, p)}; + Expression causeNotNull = new EqualExpression(new SingleNameReference(CAUSE, p), new NullLiteral(pS, pE), OperatorIds.NOT_EQUAL); + MessageSend causeDotInitCause = new MessageSend(); + causeDotInitCause.sourceStart = pS; causeDotInitCause.sourceEnd = pE; + causeDotInitCause.receiver = new SuperReference(pS, pE); + causeDotInitCause.selector = INIT_CAUSE; + causeDotInitCause.arguments = new Expression[] {new SingleNameReference(CAUSE, p)}; + IfStatement ifs = new IfStatement(causeNotNull, causeDotInitCause, pS, pE); + ConstructorDeclaration constructor = createConstructor(level, typeNode, true, true, source, explicitCall, ifs); + injectMethod(typeNode, constructor); + } + + /** + * Checks if a constructor with the provided parameters exists under the type node. + */ + public static MemberExistsResult hasConstructor(EclipseNode node, Class<?>... paramTypes) { + node = upToTypeNode(node); + + if (node != null && node.get() instanceof TypeDeclaration) { + TypeDeclaration typeDecl = (TypeDeclaration) node.get(); + if (typeDecl.methods != null) for (AbstractMethodDeclaration def : typeDecl.methods) { + if (def instanceof ConstructorDeclaration) { + if ((def.bits & ASTNode.IsDefaultConstructor) != 0) continue; + if (!paramsMatch(node, def.arguments, paramTypes)) continue; + return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK; + } + } + } + + return MemberExistsResult.NOT_EXISTS; + } + + private static boolean paramsMatch(EclipseNode node, Argument[] a, Class<?>[] b) { + if (a == null) return b == null || b.length == 0; + if (b == null) return a.length == 0; + if (a.length != b.length) return false; + + for (int i = 0; i < a.length; i++) { + if (!typeMatches(b[i], node, a[i].type)) return false; + } + return true; + } + + private static final char[][] JAVA_BEANS_CONSTRUCTORPROPERTIES = new char[][] { "java".toCharArray(), "beans".toCharArray(), "ConstructorProperties".toCharArray() }; + private static final char[] MESSAGE = "message".toCharArray(), CAUSE = "cause".toCharArray(), GET_MESSAGE = "getMessage".toCharArray(), INIT_CAUSE = "initCause".toCharArray(); + + public static Annotation[] createConstructorProperties(ASTNode source, boolean msgParam, boolean causeParam) { + if (!msgParam && !causeParam) return null; + + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long) pS << 32 | pE; + long[] poss = new long[3]; + Arrays.fill(poss, p); + + QualifiedTypeReference constructorPropertiesType = new QualifiedTypeReference(JAVA_BEANS_CONSTRUCTORPROPERTIES, poss); + setGeneratedBy(constructorPropertiesType, source); + SingleMemberAnnotation ann = new SingleMemberAnnotation(constructorPropertiesType, pS); + ann.declarationSourceEnd = pE; + + ArrayInitializer fieldNames = new ArrayInitializer(); + fieldNames.sourceStart = pS; + fieldNames.sourceEnd = pE; + fieldNames.expressions = new Expression[(msgParam && causeParam) ? 2 : 1]; + + int ctr = 0; + if (msgParam) { + fieldNames.expressions[ctr] = new StringLiteral(MESSAGE, pS, pE, 0); + setGeneratedBy(fieldNames.expressions[ctr], source); + ctr++; + } + if (causeParam) { + fieldNames.expressions[ctr] = new StringLiteral(CAUSE, pS, pE, 0); + setGeneratedBy(fieldNames.expressions[ctr], source); + ctr++; + } + + ann.memberValue = fieldNames; + setGeneratedBy(ann, source); + setGeneratedBy(ann.memberValue, source); + return new Annotation[] { ann }; + } + + @SuppressWarnings("deprecation") public static ConstructorDeclaration createConstructor(AccessLevel level, EclipseNode typeNode, boolean msgParam, boolean causeParam, EclipseNode sourceNode, ExplicitConstructorCall explicitCall, Statement extra) { + ASTNode source = sourceNode.get(); + TypeDeclaration typeDeclaration = ((TypeDeclaration) typeNode.get()); + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long) pS << 32 | pE; + + boolean addConstructorProperties; + if ((!msgParam && !causeParam) || isLocalType(typeNode)) { + addConstructorProperties = false; + } else { + Boolean v = typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES); + addConstructorProperties = v != null ? v.booleanValue() : + Boolean.FALSE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); + } + + ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult); + + constructor.modifiers = toEclipseModifier(level); + constructor.selector = typeDeclaration.name; + constructor.thrownExceptions = null; + constructor.typeParameters = null; + constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = pS; + constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = pE; + constructor.arguments = null; + + List<Argument> params = new ArrayList<Argument>(); + + if (msgParam) { + TypeReference typeRef = new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[] {p, p, p}); + Argument parameter = new Argument(MESSAGE, p, typeRef, Modifier.FINAL); + params.add(parameter); + } + if (causeParam) { + TypeReference typeRef = new QualifiedTypeReference(TypeConstants.JAVA_LANG_THROWABLE, new long[] {p, p, p}); + Argument parameter = new Argument(CAUSE, p, typeRef, Modifier.FINAL); + params.add(parameter); + } + + explicitCall.sourceStart = pS; + explicitCall.sourceEnd = pE; + constructor.constructorCall = explicitCall; + constructor.statements = extra != null ? new Statement[] {extra} : null; + constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[0]); + + Annotation[] constructorProperties = null; + if (addConstructorProperties) constructorProperties = createConstructorProperties(source, msgParam, causeParam); + constructor.annotations = copyAnnotations(source, constructorProperties); + constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); + return constructor; + } + + public static boolean isLocalType(EclipseNode type) { + Kind kind = type.up().getKind(); + if (kind == Kind.COMPILATION_UNIT) return false; + if (kind == Kind.TYPE) return isLocalType(type.up()); + return true; + } +} diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java index 72491277..171402b3 100644 --- a/src/core/lombok/eclipse/handlers/HandleToString.java +++ b/src/core/lombok/eclipse/handlers/HandleToString.java @@ -161,12 +161,12 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> { String typeName = getTypeName(type); boolean isEnum = type.isEnumType(); - + char[] suffix = ")".toCharArray(); String infixS = ", "; char[] infix = infixS.toCharArray(); int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long)pS << 32 | pE; + long p = (long) pS << 32 | pE; final int PLUS = OperatorIds.PLUS; String prefix; diff --git a/src/core/lombok/experimental/StandardException.java b/src/core/lombok/experimental/StandardException.java new file mode 100644 index 00000000..b04ac2ee --- /dev/null +++ b/src/core/lombok/experimental/StandardException.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import lombok.AccessLevel; + +/** + * Put on any class that extends some {@code java.lang.Throwable} type to add the 4 common exception constructors. + * + * Specifically, all 4 constructors derived from the combinatorial explosion of {@code String message} and {@code Throwable cause}. + * You may write any or all of these 4 constructors by hand; lombok will only generate the missing ones. + * <p> + * All but the full {@code (String message, Throwable cause)} constructor are implemented as a {@code this(msg, cause)} call; it is therefore + * possibly to write code to run on construction by writing just the {@code (String message, Throwable cause)} constructor. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.SOURCE) +public @interface StandardException { + /** + * Sets the access level of the generated constuctors. By default, generated constructors are {@code public}. + * Note: This does nothing if you write your own constructors (we won't change their access levels). + * + * @return The constructors will be generated with this access modifier. + */ + AccessLevel access() default lombok.AccessLevel.PUBLIC; +} diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index 9c74ca0e..dc70e2ce 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -26,8 +26,6 @@ import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Symbol.ClassSymbol; -import com.sun.tools.javac.code.Type; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCBlock; @@ -55,7 +53,6 @@ import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.configuration.CheckerFrameworkVersion; import lombok.delombok.LombokOptionsFactory; -import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; @@ -243,30 +240,17 @@ public class HandleConstructor { if (noArgs && noArgsConstructorExists(typeNode)) return; - ListBuffer<Type> argTypes = new ListBuffer<Type>(); - for (JavacNode fieldNode : fields) { - Type mirror = getMirrorForFieldType(fieldNode); - if (mirror == null) { - argTypes = null; - break; - } - argTypes.append(mirror); - } - List<Type> argTypes_ = argTypes == null ? null : argTypes.toList(); - if (!(skipIfConstructorExists != SkipIfConstructorExists.NO && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS)) { JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, allToDefault, source); - injectMethod(typeNode, constr, argTypes_, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); + injectMethod(typeNode, constr); } - generateStaticConstructor(staticConstrRequired, typeNode, staticName, level, allToDefault, fields, source, argTypes_); + generateStaticConstructor(staticConstrRequired, typeNode, staticName, level, allToDefault, fields, source); } - private void generateStaticConstructor(boolean staticConstrRequired, JavacNode typeNode, String staticName, AccessLevel level, boolean allToDefault, List<JavacNode> fields, JavacNode source, List<Type> argTypes_) { + private void generateStaticConstructor(boolean staticConstrRequired, JavacNode typeNode, String staticName, AccessLevel level, boolean allToDefault, List<JavacNode> fields, JavacNode source) { if (staticConstrRequired) { - ClassSymbol sym = ((JCClassDecl) typeNode.get()).sym; - Type returnType = sym == null ? null : sym.type; JCMethodDecl staticConstr = createStaticConstructor(staticName, level, typeNode, allToDefault ? List.<JavacNode>nil() : fields, source); - injectMethod(typeNode, staticConstr, argTypes_, returnType); + injectMethod(typeNode, staticConstr); } } diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index 8f6de9bb..7a7e41f9 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -46,7 +46,6 @@ import lombok.javac.JavacTreeMaker.TypeTag; import lombok.spi.Provides; 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.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCBinary; @@ -214,7 +213,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { long access = toJavacModifier(level) | (fieldDecl.mods.flags & Flags.STATIC); - injectMethod(fieldNode.up(), createGetter(access, fieldNode, fieldNode.getTreeMaker(), source, lazy, onMethod), List.<Type>nil(), getMirrorForFieldType(fieldNode)); + injectMethod(fieldNode.up(), createGetter(access, fieldNode, fieldNode.getTreeMaker(), source, lazy, onMethod)); } public JCMethodDecl createGetter(long access, JavacNode field, JavacTreeMaker treeMaker, JavacNode source, boolean lazy, List<JCAnnotation> onMethod) { diff --git a/src/core/lombok/javac/handlers/HandleNonNull.java b/src/core/lombok/javac/handlers/HandleNonNull.java index 786a7659..fe66432a 100644 --- a/src/core/lombok/javac/handlers/HandleNonNull.java +++ b/src/core/lombok/javac/handlers/HandleNonNull.java @@ -71,7 +71,7 @@ import lombok.spi.Provides; @Provides @HandlerPriority(value = 512) // 2^9; onParameter=@__(@NonNull) has to run first. public class HandleNonNull extends JavacAnnotationHandler<NonNull> { - private JCMethodDecl createRecordArgslessConstructor(JavacNode typeNode, JavacNode source) { + private JCMethodDecl createRecordArgslessConstructor(JavacNode typeNode, JavacNode source, JCMethodDecl existingCtr) { JavacTreeMaker maker = typeNode.getTreeMaker(); java.util.List<JCVariableDecl> fields = new ArrayList<JCVariableDecl>(); @@ -94,8 +94,18 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> { JCModifiers mods = maker.Modifiers(toJavacModifier(AccessLevel.PUBLIC) | COMPACT_RECORD_CONSTRUCTOR, List.<JCAnnotation>nil()); JCBlock body = maker.Block(0L, List.<JCStatement>nil()); - JCMethodDecl constr = maker.MethodDef(mods, typeNode.toName("<init>"), null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), body, null); - return recursiveSetGeneratedBy(constr, source); + if (existingCtr == null) { + JCMethodDecl constr = maker.MethodDef(mods, typeNode.toName("<init>"), null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), body, null); + return recursiveSetGeneratedBy(constr, source); + } else { + existingCtr.mods = mods; + existingCtr.params = params.toList(); + existingCtr.body = body; + existingCtr = recursiveSetGeneratedBy(existingCtr, source); + addSuppressWarningsAll(existingCtr.mods, typeNode, typeNode.getNodeFor(getGeneratedBy(existingCtr)), typeNode.getContext()); + addGenerated(existingCtr.mods, typeNode, typeNode.getNodeFor(getGeneratedBy(existingCtr)), typeNode.getContext()); + return existingCtr; + } } /** @@ -113,16 +123,17 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> { JCClassDecl cDecl = (JCClassDecl) typeNode.get(); if ((cDecl.mods.flags & RECORD) == 0) return answer; - ListBuffer<JCTree> newDefs = new ListBuffer<JCTree>(); boolean generateConstructor = false; + JCMethodDecl existingCtr = null; + for (JCTree def : cDecl.defs) { - boolean remove = false; if (def instanceof JCMethodDecl) { JCMethodDecl md = (JCMethodDecl) def; if (md.name.contentEquals("<init>")) { if ((md.mods.flags & Flags.GENERATEDCONSTR) != 0) { - remove = true; + existingCtr = md; + existingCtr.mods.flags = existingCtr.mods.flags & ~Flags.GENERATEDCONSTR; generateConstructor = true; } else { if (!isTolerate(typeNode, md)) { @@ -134,13 +145,16 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> { } } } - if (!remove) newDefs.append(def); } if (generateConstructor) { - cDecl.defs = newDefs.toList(); - JCMethodDecl ctr = createRecordArgslessConstructor(typeNode, source); - injectMethod(typeNode, ctr); + JCMethodDecl ctr; + if (existingCtr != null) { + ctr = createRecordArgslessConstructor(typeNode, source, existingCtr); + } else { + ctr = createRecordArgslessConstructor(typeNode, source, null); + injectMethod(typeNode, ctr); + } answer = answer.prepend(ctr); } diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index 5c34f9f5..a0634494 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -40,8 +40,6 @@ import lombok.javac.JavacTreeMaker; import lombok.spi.Provides; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Symbol.ClassSymbol; -import com.sun.tools.javac.code.Type; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCAssign; import com.sun.tools.javac.tree.JCTree.JCBlock; @@ -184,17 +182,7 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { long access = toJavacModifier(level) | (fieldDecl.mods.flags & Flags.STATIC); JCMethodDecl createdSetter = createSetter(access, fieldNode, fieldNode.getTreeMaker(), sourceNode, onMethod, onParam); - Type fieldType = getMirrorForFieldType(fieldNode); - Type returnType; - - if (shouldReturnThis(fieldNode)) { - ClassSymbol sym = ((JCClassDecl) fieldNode.up().get()).sym; - returnType = sym == null ? null : sym.type; - } else { - returnType = Javac.createVoidType(fieldNode.getSymbolTable(), CTC_VOID); - } - - injectMethod(fieldNode.up(), createdSetter, fieldType == null ? null : List.of(fieldType), returnType); + injectMethod(fieldNode.up(), createdSetter); } public static JCMethodDecl createSetter(long access, JavacNode field, JavacTreeMaker treeMaker, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) { diff --git a/src/core/lombok/javac/handlers/HandleStandardException.java b/src/core/lombok/javac/handlers/HandleStandardException.java new file mode 100644 index 00000000..dd764233 --- /dev/null +++ b/src/core/lombok/javac/handlers/HandleStandardException.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2021 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.javac.handlers; + +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Name; +import lombok.AccessLevel; +import lombok.ConfigurationKeys; +import lombok.experimental.StandardException; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.delombok.LombokOptionsFactory; +import lombok.javac.Javac; +import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacNode; +import lombok.javac.JavacTreeMaker; +import lombok.javac.handlers.JavacHandlerUtil.*; +import lombok.spi.Provides; + +import static lombok.core.handlers.HandlerUtil.handleFlagUsage; +import static lombok.javac.Javac.*; +import static lombok.javac.handlers.JavacHandlerUtil.*; + +@Provides +public class HandleStandardException extends JavacAnnotationHandler<StandardException> { + @Override + public void handle(AnnotationValues<StandardException> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.STANDARD_EXCEPTION_FLAG_USAGE, "@StandardException"); + deleteAnnotationIfNeccessary(annotationNode, StandardException.class); + deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); + JavacNode typeNode = annotationNode.up(); + + if (!isClass(typeNode)) { + annotationNode.addError("@StandardException is only supported on a class"); + return; + } + + JCTree extending = Javac.getExtendsClause((JCClassDecl) typeNode.get()); + if (extending == null) { + annotationNode.addError("@StandardException requires that you extend a Throwable type"); + return; + } + + AccessLevel access = annotation.getInstance().access(); + if (access == null) access = AccessLevel.PUBLIC; + if (access == AccessLevel.NONE) { + annotationNode.addError("AccessLevel.NONE is not valid here"); + access = AccessLevel.PUBLIC; + } + + generateNoArgsConstructor(typeNode, access, annotationNode); + generateMsgOnlyConstructor(typeNode, access, annotationNode); + generateCauseOnlyConstructor(typeNode, access, annotationNode); + generateFullConstructor(typeNode, access, annotationNode); + } + + private void generateNoArgsConstructor(JavacNode typeNode, AccessLevel level, JavacNode source) { + if (hasConstructor(typeNode) != MemberExistsResult.NOT_EXISTS) return; + JavacTreeMaker maker = typeNode.getTreeMaker(); + + List<JCExpression> args = List.<JCExpression>of(maker.Literal(CTC_BOT, null), maker.Literal(CTC_BOT, null)); + JCStatement thisCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(typeNode.toName("this")), args)); + JCMethodDecl constr = createConstructor(level, typeNode, false, false, source, List.of(thisCall)); + injectMethod(typeNode, constr); + } + + private void generateMsgOnlyConstructor(JavacNode typeNode, AccessLevel level, JavacNode source) { + if (hasConstructor(typeNode, String.class) != MemberExistsResult.NOT_EXISTS) return; + JavacTreeMaker maker = typeNode.getTreeMaker(); + + List<JCExpression> args = List.<JCExpression>of(maker.Ident(typeNode.toName("message")), maker.Literal(CTC_BOT, null)); + JCStatement thisCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(typeNode.toName("this")), args)); + JCMethodDecl constr = createConstructor(level, typeNode, true, false, source, List.of(thisCall)); + injectMethod(typeNode, constr); + } + + private void generateCauseOnlyConstructor(JavacNode typeNode, AccessLevel level, JavacNode source) { + if (hasConstructor(typeNode, Throwable.class) != MemberExistsResult.NOT_EXISTS) return; + JavacTreeMaker maker = typeNode.getTreeMaker(); + Name causeName = typeNode.toName("cause"); + + JCExpression causeDotGetMessage = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(causeName), typeNode.toName("getMessage")), List.<JCExpression>nil()); + JCExpression msgExpression = maker.Conditional(maker.Binary(CTC_NOT_EQUAL, maker.Ident(causeName), maker.Literal(CTC_BOT, null)), causeDotGetMessage, maker.Literal(CTC_BOT, null)); + + List<JCExpression> args = List.<JCExpression>of(msgExpression, maker.Ident(causeName)); + JCStatement thisCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(typeNode.toName("this")), args)); + JCMethodDecl constr = createConstructor(level, typeNode, false, true, source, List.of(thisCall)); + injectMethod(typeNode, constr); + } + + private void generateFullConstructor(JavacNode typeNode, AccessLevel level, JavacNode source) { + if (hasConstructor(typeNode, String.class, Throwable.class) != MemberExistsResult.NOT_EXISTS) return; + JavacTreeMaker maker = typeNode.getTreeMaker(); + + Name causeName = typeNode.toName("cause"); + Name superName = typeNode.toName("super"); + + List<JCExpression> args = List.<JCExpression>of(maker.Ident(typeNode.toName("message"))); + JCStatement superCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(superName), args)); + JCExpression causeNotNull = maker.Binary(CTC_NOT_EQUAL, maker.Ident(causeName), maker.Literal(CTC_BOT, null)); + JCStatement initCauseCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(superName), typeNode.toName("initCause")), List.<JCExpression>of(maker.Ident(causeName)))); + JCStatement initCause = maker.If(causeNotNull, initCauseCall, null); + JCMethodDecl constr = createConstructor(level, typeNode, true, true, source, List.of(superCall, initCause)); + injectMethod(typeNode, constr); + } + + private static MemberExistsResult hasConstructor(JavacNode node, Class<?>... paramTypes) { + node = upToTypeNode(node); + + if (node != null && node.get() instanceof JCClassDecl) { + for (JCTree def : ((JCClassDecl) node.get()).defs) { + if (def instanceof JCMethodDecl) { + JCMethodDecl md = (JCMethodDecl) def; + if (md.name.contentEquals("<init>") && (md.mods.flags & Flags.GENERATEDCONSTR) == 0) { + if (!paramsMatch(node, md.params, paramTypes)) continue; + return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK; + } + } + } + } + + return MemberExistsResult.NOT_EXISTS; + } + + private static boolean paramsMatch(JavacNode node, List<JCVariableDecl> a, Class<?>[] b) { + if (a == null) return b == null || b.length == 0; + if (b == null) return a.size() == 0; + if (a.size() != b.length) return false; + + for (int i = 0; i < a.size(); i++) { + JCVariableDecl param = a.get(i); + Class<?> c = b[i]; + if (!typeMatches(c, node, param.vartype)) return false; + } + + return true; + } + + private static void addConstructorProperties(JCModifiers mods, JavacNode node, boolean msgParam, boolean causeParam) { + if (!msgParam && !causeParam) return; + JavacTreeMaker maker = node.getTreeMaker(); + JCExpression constructorPropertiesType = chainDots(node, "java", "beans", "ConstructorProperties"); + ListBuffer<JCExpression> fieldNames = new ListBuffer<JCExpression>(); + if (msgParam) fieldNames.append(maker.Literal("message")); + if (causeParam) fieldNames.append(maker.Literal("cause")); + JCExpression fieldNamesArray = maker.NewArray(null, List.<JCExpression>nil(), fieldNames.toList()); + JCAnnotation annotation = maker.Annotation(constructorPropertiesType, List.of(fieldNamesArray)); + mods.annotations = mods.annotations.append(annotation); + } + + @SuppressWarnings("deprecation") private static JCMethodDecl createConstructor(AccessLevel level, JavacNode typeNode, boolean msgParam, boolean causeParam, JavacNode source, List<JCStatement> statements) { + JavacTreeMaker maker = typeNode.getTreeMaker(); + + boolean addConstructorProperties; + if ((!msgParam && !causeParam) || isLocalType(typeNode) || !LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) { + addConstructorProperties = false; + } else { + Boolean v = typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES); + addConstructorProperties = v != null ? v.booleanValue() : + Boolean.FALSE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); + } + + ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>(); + + if (msgParam) { + Name fieldName = typeNode.toName("message"); + long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); + JCExpression pType = genJavaLangTypeRef(typeNode, "String"); + JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), fieldName, pType, null); + params.append(param); + } + + if (causeParam) { + Name fieldName = typeNode.toName("cause"); + long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); + JCExpression pType = genJavaLangTypeRef(typeNode, "Throwable"); + JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), fieldName, pType, null); + params.append(param); + } + + JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.<JCAnnotation>nil()); + if (addConstructorProperties) addConstructorProperties(mods, typeNode, msgParam, causeParam); + return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"), + null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), + maker.Block(0L, statements), null), source); + } + + public static boolean isLocalType(JavacNode type) { + Kind kind = type.up().getKind(); + if (kind == Kind.COMPILATION_UNIT) return false; + if (kind == Kind.TYPE) return isLocalType(type.up()); + return true; + } +} diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 8974cd68..87b18576 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -583,7 +583,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), maker.Block(0L, statements.toList()), null), job.sourceNode); - injectMethod(job.parentType, constr, null, Javac.createVoidType(job.builderType.getSymbolTable(), CTC_VOID)); + injectMethod(job.parentType, constr); } private JCMethodDecl generateBuilderMethod(SuperBuilderJob job) { diff --git a/src/core/lombok/javac/handlers/HandleUtilityClass.java b/src/core/lombok/javac/handlers/HandleUtilityClass.java index e006cc47..6fa630e2 100644 --- a/src/core/lombok/javac/handlers/HandleUtilityClass.java +++ b/src/core/lombok/javac/handlers/HandleUtilityClass.java @@ -22,11 +22,9 @@ package lombok.javac.handlers; import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage; -import static lombok.javac.Javac.CTC_VOID; import static lombok.javac.handlers.JavacHandlerUtil.*; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Type; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCClassDecl; @@ -44,7 +42,6 @@ import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.experimental.UtilityClass; -import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; @@ -141,7 +138,7 @@ public class HandleUtilityClass extends JavacAnnotationHandler<UtilityClass> { JCBlock block = maker.Block(0L, createThrowStatement(typeNode, maker)); JCMethodDecl methodDef = maker.MethodDef(mods, name, null, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), block, null); JCMethodDecl constructor = recursiveSetGeneratedBy(methodDef, typeNode); - JavacHandlerUtil.injectMethod(typeNode, constructor, List.<Type>nil(), Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); + JavacHandlerUtil.injectMethod(typeNode, constructor); } private List<JCStatement> createThrowStatement(JavacNode typeNode, JavacTreeMaker maker) { diff --git a/src/core/lombok/javac/handlers/HandleWith.java b/src/core/lombok/javac/handlers/HandleWith.java index f914b4c7..47f78b1e 100644 --- a/src/core/lombok/javac/handlers/HandleWith.java +++ b/src/core/lombok/javac/handlers/HandleWith.java @@ -40,8 +40,6 @@ import lombok.javac.handlers.JavacHandlerUtil.CopyJavadoc; import lombok.spi.Provides; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCClassDecl; @@ -211,11 +209,8 @@ public class HandleWith extends JavacAnnotationHandler<With> { JCMethodDecl createdWith = createWith(access, fieldNode, fieldNode.getTreeMaker(), source, onMethod, onParam, makeAbstract); createRelevantNonNullAnnotation(fieldNode, createdWith); - ClassSymbol sym = ((JCClassDecl) fieldNode.up().get()).sym; - Type returnType = sym == null ? null : sym.type; - recursiveSetGeneratedBy(createdWith, source); - injectMethod(typeNode, createdWith, List.<Type>of(getMirrorForFieldType(fieldNode)), returnType); + injectMethod(typeNode, createdWith); } public JCMethodDecl createWith(long access, JavacNode field, JavacTreeMaker maker, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam, boolean makeAbstract) { diff --git a/src/core/lombok/javac/handlers/HandleWithBy.java b/src/core/lombok/javac/handlers/HandleWithBy.java index f1f953b3..4ba4337e 100644 --- a/src/core/lombok/javac/handlers/HandleWithBy.java +++ b/src/core/lombok/javac/handlers/HandleWithBy.java @@ -45,8 +45,6 @@ import lombok.spi.Provides; import com.sun.tools.javac.code.BoundKind; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCClassDecl; @@ -204,11 +202,8 @@ public class HandleWithBy extends JavacAnnotationHandler<WithBy> { long access = toJavacModifier(level); JCMethodDecl createdWithBy = createWithBy(access, fieldNode, fieldNode.getTreeMaker(), source, onMethod, makeAbstract); - ClassSymbol sym = ((JCClassDecl) fieldNode.up().get()).sym; - Type returnType = sym == null ? null : sym.type; - recursiveSetGeneratedBy(createdWithBy, source); - injectMethod(typeNode, createdWithBy, List.<Type>of(getMirrorForFieldType(fieldNode)), returnType); + injectMethod(typeNode, createdWithBy); } private static final LombokImmutableList<String> NAME_JUF_FUNCTION = LombokImmutableList.of("java", "util", "function", "Function"); diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 32eae4e9..78c20d39 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -37,19 +37,19 @@ import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; -import javax.lang.model.element.Element; - import com.sun.tools.javac.code.Attribute; import com.sun.tools.javac.code.BoundKind; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Scope; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.ClassSymbol; -import com.sun.tools.javac.code.Symbol.MethodSymbol; -import com.sun.tools.javac.code.Symbol.VarSymbol; -import com.sun.tools.javac.code.Symtab; +import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.code.Type.MethodType; +import com.sun.tools.javac.comp.Annotate; +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.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree; @@ -1010,12 +1010,6 @@ public class JavacHandlerUtil { return call; } - public static Type getMirrorForFieldType(JavacNode fieldNode) { - Element fieldElement = fieldNode.getElement(); - if (fieldElement instanceof VarSymbol) return ((VarSymbol) fieldElement).type; - return null; - } - /** * Adds the given new field declaration to the provided type AST Node. * The field carries the @{@link SuppressWarnings}("all") annotation. @@ -1071,6 +1065,8 @@ public class JavacHandlerUtil { insertAfter.tail = fieldEntry; } + EnterReflect.memberEnter(field, typeNode); + return typeNode.add(field, Kind.FIELD); } @@ -1148,19 +1144,30 @@ public class JavacHandlerUtil { } static class JCAnnotationReflect { - private static Field ATTRIBUTE; - + private static final Field ATTRIBUTE; + static { - try { - ATTRIBUTE = Permit.getField(JCAnnotation.class, "attribute"); - } catch (Exception ignore) {} + ATTRIBUTE = Permit.permissiveGetField(JCAnnotation.class, "attribute"); } static Attribute.Compound getAttribute(JCAnnotation jcAnnotation) { - try { - return (Attribute.Compound) ATTRIBUTE.get(jcAnnotation); - } catch (Exception e) { - return null; + if (ATTRIBUTE != null) { + try { + return (Attribute.Compound) ATTRIBUTE.get(jcAnnotation); + } catch (Exception e) { + // Ignore + } + } + return null; + } + + static void setAttribute(JCAnnotation jcAnnotation, Attribute.Compound attribute) { + if (ATTRIBUTE != null) { + try { + Permit.set(ATTRIBUTE, jcAnnotation, attribute); + } catch (Exception e) { + // Ignore + } } } } @@ -1204,19 +1211,13 @@ public class JavacHandlerUtil { } } - public static void injectMethod(JavacNode typeNode, JCMethodDecl method) { - injectMethod(typeNode, method, null, null); - } - /** * Adds the given new method declaration to the provided type AST Node. * Can also inject constructors. * * Also takes care of updating the JavacAST. */ - public static void injectMethod(JavacNode typeNode, JCMethodDecl method, List<Type> paramTypes, Type returnType) { - Context context = typeNode.getContext(); - Symtab symtab = Symtab.instance(context); + public static void injectMethod(JavacNode typeNode, JCMethodDecl method) { JCClassDecl type = (JCClassDecl) typeNode.get(); if (method.getName().contentEquals("<init>")) { @@ -1240,55 +1241,11 @@ public class JavacHandlerUtil { addGenerated(method.mods, typeNode, typeNode.getNodeFor(getGeneratedBy(method)), typeNode.getContext()); type.defs = type.defs.append(method); - List<Symbol.VarSymbol> params = null; - if (method.getParameters() != null && !method.getParameters().isEmpty()) { - ListBuffer<Symbol.VarSymbol> newParams = new ListBuffer<Symbol.VarSymbol>(); - for (int i = 0; i < method.getParameters().size(); i++) { - JCTree.JCVariableDecl param = method.getParameters().get(i); - if (param.sym == null) { - Type paramType = paramTypes == null ? param.getType().type : paramTypes.get(i); - VarSymbol varSymbol = new VarSymbol(param.mods.flags, param.name, paramType, symtab.noSymbol); - varSymbol.adr = 1 << i; - List<JCAnnotation> annotations = param.getModifiers().getAnnotations(); - if (annotations != null && !annotations.isEmpty()) { - ListBuffer<Attribute.Compound> newAnnotations = new ListBuffer<Attribute.Compound>(); - for (JCAnnotation jcAnnotation : annotations) { - Attribute.Compound attribute = JCAnnotationReflect.getAttribute(jcAnnotation); - if (attribute != null) { - newAnnotations.append(attribute); - } - } - if (annotations.length() == newAnnotations.length()) { - varSymbol.appendAttributes(newAnnotations.toList()); - } - } - newParams.append(varSymbol); - } else { - newParams.append(param.sym); - } - } - params = newParams.toList(); - if (params.length() != method.getParameters().length()) params = null; - } - - fixMethodMirror(typeNode.getContext(), typeNode.getElement(), method.getModifiers().flags, method.getName(), paramTypes, params, returnType); + EnterReflect.memberEnter(method, typeNode); typeNode.add(method, Kind.METHOD); } - private static void fixMethodMirror(Context context, Element typeMirror, long access, Name methodName, List<Type> paramTypes, List<Symbol.VarSymbol> params, Type returnType) { - if (typeMirror == null || paramTypes == null || returnType == null) return; - ClassSymbol cs = (ClassSymbol) typeMirror; - MethodSymbol methodSymbol = new MethodSymbol(access, methodName, new MethodType(paramTypes, returnType, List.<Type>nil(), Symtab.instance(context).methodClass), cs); - if (params != null && !params.isEmpty()) { - methodSymbol.params = params; - for (VarSymbol varSymbol : params) { - varSymbol.owner = methodSymbol; - } - } - ClassSymbolMembersField.enter(cs, methodSymbol); - } - /** * Adds an inner type (class, interface, enum) to the given type. Cannot inject top-level types. * @@ -1301,9 +1258,55 @@ public class JavacHandlerUtil { addSuppressWarningsAll(type.mods, typeNode, typeNode.getNodeFor(getGeneratedBy(type)), typeNode.getContext()); addGenerated(type.mods, typeNode, typeNode.getNodeFor(getGeneratedBy(type)), typeNode.getContext()); typeDecl.defs = typeDecl.defs.append(type); + + EnterReflect.classEnter(type, typeNode); + return typeNode.add(type, Kind.TYPE); } + static class EnterReflect { + private static final Method classEnter; + private static final Method memberEnter; + private static final Method blockAnnotations; + private static final Method unblockAnnotations; + + static { + classEnter = Permit.permissiveGetMethod(Enter.class, "classEnter", JCTree.class, Env.class); + memberEnter = Permit.permissiveGetMethod(MemberEnter.class, "memberEnter", JCTree.class, Env.class); + + Method block = Permit.permissiveGetMethod(Annotate.class, "blockAnnotations"); + if (block == null) block = Permit.permissiveGetMethod(Annotate.class, "enterStart"); + blockAnnotations = block; + + Method unblock = Permit.permissiveGetMethod(Annotate.class, "unblockAnnotations"); + if (unblock == null) unblock = Permit.permissiveGetMethod(Annotate.class, "enterDone"); + unblockAnnotations = unblock; + } + + static Type classEnter(JCTree tree, JavacNode parent) { + Enter enter = Enter.instance(parent.getContext()); + Env<AttrContext> classEnv = enter.getEnv((TypeSymbol) parent.getElement()); + Type type = (Type) Permit.invokeSneaky(classEnter, enter, tree, classEnv); + if (type == null) return null; + type.complete(); + return type; + } + + static void memberEnter(JCTree tree, JavacNode parent) { + Context context = parent.getContext(); + MemberEnter me = MemberEnter.instance(context); + Annotate annotate = Annotate.instance(context); + Enter enter = Enter.instance(context); + + Env<AttrContext> classEnv = enter.getEnv((TypeSymbol) parent.getElement()); + if (classEnv == null) return; + + Permit.invokeSneaky(blockAnnotations, annotate); + Permit.invokeSneaky(memberEnter, me, tree, classEnv); + Permit.invokeSneaky(unblockAnnotations, annotate); + } + } + public static long addFinalIfNeeded(long flags, Context context) { boolean addFinal = LombokOptionsFactory.getDelombokOptions(context).getFormatPreferences().generateFinalParams(); @@ -1802,10 +1805,49 @@ public class JavacHandlerUtil { addError(errorName, annotationNode); } } + for (JCAnnotation annotation : result) { + clearTypes(annotation); + } ast.args = params.toList(); return result.toList(); } + /** + * Removes all type information from the provided tree. + */ + private static void clearTypes(JCTree tree) { + tree.accept(new TreeScanner() { + @Override public void scan(JCTree tree) { + tree.type = null; + super.scan(tree); + } + @Override public void visitClassDef(JCClassDecl tree) { + tree.sym = null; + super.visitClassDef(tree); + } + @Override public void visitMethodDef(JCMethodDecl tree) { + tree.sym = null; + super.visitMethodDef(tree); + } + @Override public void visitVarDef(JCVariableDecl tree) { + tree.sym = null; + super.visitVarDef(tree); + } + @Override public void visitSelect(JCFieldAccess tree) { + tree.sym = null; + super.visitSelect(tree); + } + @Override public void visitIdent(JCIdent tree) { + tree.sym = null; + super.visitIdent(tree); + } + @Override public void visitAnnotation(JCAnnotation tree) { + JCAnnotationReflect.setAttribute(tree, null); + super.visitAnnotation(tree); + } + }); + } + private static void addError(String errorName, JavacNode node) { if (node.getLatestJavaSpecSupported() < 8) { node.addError("The correct format up to JDK7 is " + errorName + "=@__({@SomeAnnotation, @SomeOtherAnnotation}))"); diff --git a/src/stubs/com/sun/tools/javac/code/Symtab.java b/src/stubs/com/sun/tools/javac/code/Symtab.java index 8d823531..89ed5478 100644 --- a/src/stubs/com/sun/tools/javac/code/Symtab.java +++ b/src/stubs/com/sun/tools/javac/code/Symtab.java @@ -16,7 +16,9 @@ public class Symtab { public static Symtab instance(Context context) {return null;} public Type unknownType; public TypeSymbol noSymbol; - + public Type stringType; + public Type throwableType; + // JDK 9 public ModuleSymbol unnamedModule; } diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java index c0bda93c..d9fcc4f2 100644 --- a/src/utils/lombok/javac/Javac.java +++ b/src/utils/lombok/javac/Javac.java @@ -260,6 +260,9 @@ public class Javac { } } + /** + * In some versions, the field's type is {@code JCTree}, in others it is {@code JCExpression}, which at the JVM level are not the same. + */ public static JCTree getExtendsClause(JCClassDecl decl) { try { return (JCTree) getExtendsClause.invoke(decl); diff --git a/src/utils/lombok/permit/Permit.java b/src/utils/lombok/permit/Permit.java index 2854706e..af97a807 100644 --- a/src/utils/lombok/permit/Permit.java +++ b/src/utils/lombok/permit/Permit.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2019 The Project Lombok Authors. + * Copyright (C) 2018-2021 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 |