Merge pull request #3669 from Rawi01/javadoc-constructor

Add javadoc for generated constructors
This commit is contained in:
Reinier Zwitserloot
2024-06-28 00:15:17 +02:00
committed by GitHub
15 changed files with 235 additions and 19 deletions
+28 -5
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2022 The Project Lombok Authors.
* Copyright (C) 2013-2024 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
@@ -910,9 +910,13 @@ public class HandlerUtil {
}
}
public static String stripLinesWithTagFromJavadoc(String javadoc, JavadocTag tag) {
public static String stripLinesWithTagFromJavadoc(String javadoc, JavadocTag... tags) {
if (javadoc == null || javadoc.isEmpty()) return javadoc;
return tag.pattern.matcher(javadoc).replaceAll("").trim();
String result = javadoc;
for (JavadocTag tag : tags) {
result = tag.pattern.matcher(result).replaceAll("").trim();
}
return result;
}
public static String stripSectionsFromJavadoc(String javadoc) {
@@ -968,17 +972,36 @@ public class HandlerUtil {
public static String addJavadocLine(String in, String line) {
if (in == null) return line;
if (in.endsWith("\n")) return in + line + "\n";
if (in.endsWith("\n")) return in + line;
return in + "\n" + line;
}
public static String getParamJavadoc(String methodComment, String param) {
if (methodComment == null || methodComment.isEmpty()) return methodComment;
Pattern pattern = Pattern.compile("@param " + param + " (\\S|\\s)+?(?=^ ?@)", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
Pattern pattern = Pattern.compile("@param " + param + " (\\S|\\s)+?(?=^ ?@|\\z)", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(methodComment);
if (matcher.find()) {
return matcher.group();
}
return null;
}
public static String getConstructorJavadocHeader(String typeName) {
return "Creates a new {@code " + typeName + "} instance.\n\n";
}
public static String getConstructorParameterJavadoc(String paramName, String fieldJavadoc) {
String fieldBaseJavadoc = stripSectionsFromJavadoc(fieldJavadoc);
String paramJavadoc = getParamJavadoc(fieldBaseJavadoc, paramName);
if (paramJavadoc != null) {
return paramJavadoc;
}
String javadocWithoutTags = stripLinesWithTagFromJavadoc(fieldBaseJavadoc, JavadocTag.PARAM, JavadocTag.RETURN);
if (javadocWithoutTags != null) {
return "@param " + paramName + " " + javadocWithoutTags;
}
return null;
}
}
@@ -2800,10 +2800,21 @@ public class EclipseHandlerUtil {
public static String getDocComment(EclipseNode eclipseNode) {
if (eclipseNode.getAst().getSource() == null) return null;
int start = -1;
int end = -1;
final ASTNode node = eclipseNode.get();
if (node instanceof FieldDeclaration) {
FieldDeclaration fieldDeclaration = (FieldDeclaration) node;
char[] rawContent = CharOperation.subarray(eclipseNode.getAst().getSource(), fieldDeclaration.declarationSourceStart, fieldDeclaration.declarationSourceEnd);
start = fieldDeclaration.declarationSourceStart;
end = fieldDeclaration.declarationSourceEnd;
} else if (node instanceof AbstractMethodDeclaration) {
AbstractMethodDeclaration abstractMethodDeclaration = (AbstractMethodDeclaration) node;
start = abstractMethodDeclaration.declarationSourceStart;
end = abstractMethodDeclaration.declarationSourceEnd;
}
if (start != -1 && end != -1) {
char[] rawContent = CharOperation.subarray(eclipseNode.getAst().getSource(), start, end);
String rawContentString = new String(rawContent);
int startIndex = rawContentString.indexOf("/**");
int endIndex = rawContentString.indexOf("*/");
@@ -2815,10 +2826,14 @@ public class EclipseHandlerUtil {
return null;
}
public static void setDocComment(EclipseNode typeNode, EclipseNode eclipseNode, String doc) {
setDocComment((CompilationUnitDeclaration) eclipseNode.top().get(), (TypeDeclaration) typeNode.get(), eclipseNode.get(), doc);
}
public static void setDocComment(CompilationUnitDeclaration cud, EclipseNode eclipseNode, String doc) {
setDocComment(cud, (TypeDeclaration) upToTypeNode(eclipseNode).get(), eclipseNode.get(), doc);
}
public static void setDocComment(CompilationUnitDeclaration cud, TypeDeclaration type, ASTNode node, String doc) {
if (doc == null) return;
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2021 The Project Lombok Authors.
* Copyright (C) 2013-2024 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
@@ -1053,7 +1053,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, bfd.name, bfd.nameOfSetFlag, job.oldChain, toEclipseModifier(job.accessInners),
job.sourceNode, methodAnnsList, bfd.annotations != null ? Arrays.asList(copyAnnotations(source, bfd.annotations)) : Collections.<Annotation>emptyList());
if (job.sourceNode.up().getKind() == Kind.METHOD) {
copyJavadocFromParam(bfd.originalFieldNode.up(), setter, td, bfd.name.toString());
copyJavadocFromParam(bfd.originalFieldNode.up(), setter, td, new String(bfd.name));
} else {
copyJavadoc(bfd.originalFieldNode, setter, td, CopyJavadoc.SETTER, true);
}
@@ -22,7 +22,7 @@
package lombok.eclipse.handlers;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.Eclipse.*;
import static lombok.eclipse.Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
import java.lang.reflect.Modifier;
@@ -286,7 +286,8 @@ public class HandleConstructor {
ConstructorDeclaration constr = createConstructor(
staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fieldsToParam, forceDefaults,
sourceNode, onConstructor);
injectMethod(typeNode, constr);
EclipseNode constructorNode = injectMethod(typeNode, constr);
generateConstructorJavadoc(typeNode, constructorNode, fieldsToParam);
}
generateStaticConstructor(staticConstrRequired, typeNode, staticName, level, fieldsToParam, source);
}
@@ -294,7 +295,8 @@ public class HandleConstructor {
private void generateStaticConstructor(boolean staticConstrRequired, EclipseNode typeNode, String staticName, AccessLevel level, Collection<EclipseNode> fields, ASTNode source) {
if (staticConstrRequired) {
MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, fields, source);
injectMethod(typeNode, staticConstr);
EclipseNode constructorNode = injectMethod(typeNode, staticConstr);
generateConstructorJavadoc(typeNode, constructorNode, fields);
}
}
@@ -584,4 +586,27 @@ public class HandleConstructor {
constructor.traverse(new SetGeneratedByVisitor(source), typeDecl.scope);
return constructor;
}
private void generateConstructorJavadoc(EclipseNode typeNode, EclipseNode constructorNode, Collection<EclipseNode> fields) {
if (fields.isEmpty()) return;
String constructorJavadoc = getConstructorJavadocHeader(typeNode.getName());
boolean fieldDescriptionAdded = false;
for (EclipseNode fieldNode : fields) {
String paramName = String.valueOf(removePrefixFromField(fieldNode));
String fieldJavadoc = getDocComment(fieldNode);
String paramJavadoc = getConstructorParameterJavadoc(paramName, fieldJavadoc);
if (paramJavadoc == null) {
paramJavadoc = "@param " + paramName;
} else {
fieldDescriptionAdded = true;
}
constructorJavadoc = addJavadocLine(constructorJavadoc, paramJavadoc);
}
if (fieldDescriptionAdded) {
setDocComment(typeNode, constructorNode, constructorJavadoc);
}
}
}
@@ -30,6 +30,7 @@ import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
@@ -251,6 +252,7 @@ public class HandleConstructor {
if (!(skipIfConstructorExists != SkipIfConstructorExists.NO && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS)) {
JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, allToDefault, source);
generateConstructorJavadoc(constr, typeNode, fields);
injectMethod(typeNode, constr);
}
generateStaticConstructor(staticConstrRequired, typeNode, staticName, level, allToDefault, fields, source);
@@ -259,6 +261,7 @@ public class HandleConstructor {
private void generateStaticConstructor(boolean staticConstrRequired, JavacNode typeNode, String staticName, AccessLevel level, boolean allToDefault, List<JavacNode> fields, JavacNode source) {
if (staticConstrRequired) {
JCMethodDecl staticConstr = createStaticConstructor(staticName, level, typeNode, allToDefault ? List.<JavacNode>nil() : fields, source);
generateConstructorJavadoc(staticConstr, typeNode, fields);
injectMethod(typeNode, staticConstr);
}
}
@@ -474,4 +477,28 @@ public class HandleConstructor {
createRelevantNonNullAnnotation(typeNode, methodDef);
return recursiveSetGeneratedBy(methodDef, source);
}
private void generateConstructorJavadoc(JCMethodDecl constructor, JavacNode typeNode, List<JavacNode> fields) {
if (fields.isEmpty()) return;
JCCompilationUnit cu = ((JCCompilationUnit) typeNode.top().get());
String constructorJavadoc = getConstructorJavadocHeader(typeNode.getName());
boolean fieldDescriptionAdded = false;
for (JavacNode fieldNode : fields) {
String paramName = removePrefixFromField(fieldNode).toString();
String fieldJavadoc = getDocComment(cu, fieldNode.get());
String paramJavadoc = getConstructorParameterJavadoc(paramName, fieldJavadoc);
if (paramJavadoc == null) {
paramJavadoc = "@param " + paramName;
} else {
fieldDescriptionAdded = true;
}
constructorJavadoc = addJavadocLine(constructorJavadoc, paramJavadoc);
}
if (fieldDescriptionAdded) {
setDocComment(cu, constructor, constructorJavadoc);
}
}
}
@@ -9,8 +9,9 @@ class BuilderConstructorJavadoc<T> {
* {@code @code} or <code>tags</code>
* @param predef don't copy this one
* @param predefWithJavadoc don't copy this one
* @param last also copy last param
*/
BuilderConstructorJavadoc(int basic, int multiline, int predef, int predefWithJavadoc) {
BuilderConstructorJavadoc(int basic, int multiline, int predef, int predefWithJavadoc, int last) {
}
public static class BuilderConstructorJavadocBuilder<T> {
@java.lang.SuppressWarnings("all")
@@ -25,6 +26,9 @@ class BuilderConstructorJavadoc<T> {
@java.lang.SuppressWarnings("all")
@lombok.Generated
private int predefWithJavadoc;
@java.lang.SuppressWarnings("all")
@lombok.Generated
private int last;
public BuilderConstructorJavadocBuilder<T> predef(final int x) {
this.predef = x;
return this;
@@ -64,16 +68,26 @@ class BuilderConstructorJavadoc<T> {
this.multiline = multiline;
return this;
}
/**
* @param last also copy last param
* @return {@code this}.
*/
@java.lang.SuppressWarnings("all")
@lombok.Generated
public BuilderConstructorJavadoc.BuilderConstructorJavadocBuilder<T> last(final int last) {
this.last = last;
return this;
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
public BuilderConstructorJavadoc<T> build() {
return new BuilderConstructorJavadoc<T>(this.basic, this.multiline, this.predef, this.predefWithJavadoc);
return new BuilderConstructorJavadoc<T>(this.basic, this.multiline, this.predef, this.predefWithJavadoc, this.last);
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@lombok.Generated
public java.lang.String toString() {
return "BuilderConstructorJavadoc.BuilderConstructorJavadocBuilder(basic=" + this.basic + ", multiline=" + this.multiline + ", predef=" + this.predef + ", predefWithJavadoc=" + this.predefWithJavadoc + ")";
return "BuilderConstructorJavadoc.BuilderConstructorJavadocBuilder(basic=" + this.basic + ", multiline=" + this.multiline + ", predef=" + this.predef + ", predefWithJavadoc=" + this.predefWithJavadoc + ", last=" + this.last + ")";
}
}
@java.lang.SuppressWarnings("all")
@@ -87,6 +87,15 @@ class BuilderJavadoc<T> {
return "BuilderJavadoc.BuilderJavadocBuilder(basic=" + this.basic + ", getsetwith=" + this.getsetwith + ", predef=" + this.predef + ", predefWithJavadoc=" + this.predefWithJavadoc + ")";
}
}
/**
* Creates a new {@code BuilderJavadoc} instance.
*
* @param basic basic gets only a builder setter.
* @see #getsetwith
* @param getsetwith getsetwith gets a builder setter, an instance getter and setter, and a wither.
* @param predef Predef has a predefined builder setter with no javadoc, and the builder setter does not get this one.
* @param predefWithJavadoc predefWithJavadoc has a predefined builder setter with javadoc, so it keeps that one untouched.
*/
@java.lang.SuppressWarnings("all")
@lombok.Generated
BuilderJavadoc(final int basic, final int getsetwith, final int predef, final int predefWithJavadoc) {
@@ -10,6 +10,14 @@ public class BuilderWithDeprecated {
java.util.List<String> strings;
@Deprecated
ImmutableList<Integer> numbers;
/**
* Creates a new {@code BuilderWithDeprecated} instance.
*
* @param dep1 @deprecated since always
* @param dep2
* @param strings
* @param numbers
*/
@java.lang.SuppressWarnings("all")
@lombok.Generated
BuilderWithDeprecated(final String dep1, final int dep2, final java.util.List<String> strings, final ImmutableList<Integer> numbers) {
@@ -0,0 +1,28 @@
public class ConstructorJavadoc {
/**
* Some text
*
* @param fieldName Hello, World1
* --- GETTER ---
* Getter section
*
* @return Sky is blue1
*/
private int fieldName;
/**
* Sky is blue
*/
private int fieldName2;
/**
* Creates a new {@code ConstructorJavadoc} instance.
*
* @param fieldName Hello, World1
* @param fieldName2 Sky is blue
*/
@java.lang.SuppressWarnings("all")
@lombok.Generated
public ConstructorJavadoc(final int fieldName, final int fieldName2) {
this.fieldName = fieldName;
this.fieldName2 = fieldName2;
}
}
@@ -5,6 +5,7 @@ class BuilderConstructorJavadoc<T> {
private @java.lang.SuppressWarnings("all") @lombok.Generated int multiline;
private @java.lang.SuppressWarnings("all") @lombok.Generated int predef;
private @java.lang.SuppressWarnings("all") @lombok.Generated int predefWithJavadoc;
private @java.lang.SuppressWarnings("all") @lombok.Generated int last;
public BuilderConstructorJavadocBuilder<T> predef(final int x) {
this.predef = x;
return this;
@@ -17,6 +18,7 @@ class BuilderConstructorJavadoc<T> {
super();
}
/**
* @param basic tag is moved to the setter
* @return {@code this}.
*/
public @java.lang.SuppressWarnings("all") @lombok.Generated BuilderConstructorJavadoc.BuilderConstructorJavadocBuilder<T> basic(final int basic) {
@@ -24,20 +26,31 @@ class BuilderConstructorJavadoc<T> {
return this;
}
/**
* @param multiline a param comment
* can be on multiple lines and can use
* {@code @code} or <code>tags</code>
* @return {@code this}.
*/
public @java.lang.SuppressWarnings("all") @lombok.Generated BuilderConstructorJavadoc.BuilderConstructorJavadocBuilder<T> multiline(final int multiline) {
this.multiline = multiline;
return this;
}
/**
* @param last also copy last param
* @return {@code this}.
*/
public @java.lang.SuppressWarnings("all") @lombok.Generated BuilderConstructorJavadoc.BuilderConstructorJavadocBuilder<T> last(final int last) {
this.last = last;
return this;
}
public @java.lang.SuppressWarnings("all") @lombok.Generated BuilderConstructorJavadoc<T> build() {
return new BuilderConstructorJavadoc<T>(this.basic, this.multiline, this.predef, this.predefWithJavadoc);
return new BuilderConstructorJavadoc<T>(this.basic, this.multiline, this.predef, this.predefWithJavadoc, this.last);
}
public @java.lang.Override @java.lang.SuppressWarnings("all") @lombok.Generated java.lang.String toString() {
return (((((((("BuilderConstructorJavadoc.BuilderConstructorJavadocBuilder(basic=" + this.basic) + ", multiline=") + this.multiline) + ", predef=") + this.predef) + ", predefWithJavadoc=") + this.predefWithJavadoc) + ")");
return (((((((((("BuilderConstructorJavadoc.BuilderConstructorJavadocBuilder(basic=" + this.basic) + ", multiline=") + this.multiline) + ", predef=") + this.predef) + ", predefWithJavadoc=") + this.predefWithJavadoc) + ", last=") + this.last) + ")");
}
}
@lombok.Builder BuilderConstructorJavadoc(int basic, int multiline, int predef, int predefWithJavadoc) {
@lombok.Builder BuilderConstructorJavadoc(int basic, int multiline, int predef, int predefWithJavadoc, int last) {
super();
}
public static @java.lang.SuppressWarnings("all") @lombok.Generated <T>BuilderConstructorJavadoc.BuilderConstructorJavadocBuilder<T> builder() {
@@ -46,6 +46,15 @@ import java.util.List;
private @lombok.Getter @lombok.Setter @lombok.experimental.Wither int getsetwith;
private final int predef;
private final int predefWithJavadoc;
/**
* Creates a new {@code BuilderJavadoc} instance.
*
* @param basic basic gets only a builder setter.
* @see #getsetwith
* @param getsetwith getsetwith gets a builder setter, an instance getter and setter, and a wither.
* @param predef Predef has a predefined builder setter with no javadoc, and the builder setter does not get this one.
* @param predefWithJavadoc predefWithJavadoc has a predefined builder setter with javadoc, so it keeps that one untouched.
*/
@java.lang.SuppressWarnings("all") @lombok.Generated BuilderJavadoc(final int basic, final int getsetwith, final int predef, final int predefWithJavadoc) {
super();
this.basic = basic;
@@ -89,6 +89,14 @@ public @Builder class BuilderWithDeprecated {
@Deprecated int dep2;
@Singular @Deprecated java.util.List<String> strings;
@Singular @Deprecated ImmutableList<Integer> numbers;
/**
* Creates a new {@code BuilderWithDeprecated} instance.
*
* @param dep1 @deprecated since always
* @param dep2
* @param strings
* @param numbers
*/
@java.lang.SuppressWarnings("all") @lombok.Generated BuilderWithDeprecated(final String dep1, final int dep2, final java.util.List<String> strings, final ImmutableList<Integer> numbers) {
super();
this.dep1 = dep1;
@@ -0,0 +1,16 @@
import lombok.AllArgsConstructor;
public @AllArgsConstructor class ConstructorJavadoc {
private int fieldName;
private int fieldName2;
/**
* Creates a new {@code ConstructorJavadoc} instance.
*
* @param fieldName Hello, World1
* @param fieldName2 Sky is blue
*/
public @java.lang.SuppressWarnings("all") @lombok.Generated ConstructorJavadoc(final int fieldName, final int fieldName2) {
super();
this.fieldName = fieldName;
this.fieldName2 = fieldName2;
}
}
@@ -10,9 +10,10 @@ class BuilderConstructorJavadoc<T> {
* {@code @code} or <code>tags</code>
* @param predef don't copy this one
* @param predefWithJavadoc don't copy this one
* @param last also copy last param
*/
@lombok.Builder
BuilderConstructorJavadoc(int basic, int multiline, int predef, int predefWithJavadoc) {
BuilderConstructorJavadoc(int basic, int multiline, int predef, int predefWithJavadoc, int last) {
}
@@ -0,0 +1,20 @@
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class ConstructorJavadoc {
/**
* Some text
*
* @param fieldName Hello, World1
* --- GETTER ---
* Getter section
*
* @return Sky is blue1
*/
private int fieldName;
/**
* Sky is blue
*/
private int fieldName2;
}