aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/lombok/ConfigurationKeys.java6
-rw-r--r--src/core/lombok/eclipse/EclipseAST.java9
-rw-r--r--src/core/lombok/eclipse/TransformEclipseAST.java1
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java39
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/HandleBuilder.java2
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java8
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/HandleStandardException.java253
-rw-r--r--src/core/lombok/eclipse/handlers/HandleToString.java4
-rw-r--r--src/core/lombok/experimental/StandardException.java50
-rw-r--r--src/core/lombok/javac/handlers/HandleConstructor.java24
-rw-r--r--src/core/lombok/javac/handlers/HandleGetter.java3
-rw-r--r--src/core/lombok/javac/handlers/HandleNonNull.java34
-rw-r--r--src/core/lombok/javac/handlers/HandleSetter.java14
-rw-r--r--src/core/lombok/javac/handlers/HandleStandardException.java217
-rw-r--r--src/core/lombok/javac/handlers/HandleSuperBuilder.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleUtilityClass.java5
-rw-r--r--src/core/lombok/javac/handlers/HandleWith.java7
-rw-r--r--src/core/lombok/javac/handlers/HandleWithBy.java7
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java188
-rw-r--r--src/stubs/com/sun/tools/javac/code/Symtab.java4
-rw-r--r--src/utils/lombok/javac/Javac.java3
-rw-r--r--src/utils/lombok/permit/Permit.java2
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 &#64;{@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