Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add javadoc for generated constructors #3669

Merged
merged 3 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions src/core/lombok/core/handlers/HandlerUtil.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -910,9 +910,13 @@ public enum JavadocTag {
}
}

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) {
Expand Down Expand Up @@ -968,17 +972,36 @@ public static String addReturnsUpdatedSelfIfNeeded(String in) {

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;
}
}
19 changes: 17 additions & 2 deletions src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2800,10 +2800,21 @@ public static Annotation[][] getRecordFieldAnnotations(TypeDeclaration typeDecla
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("*/");
Expand All @@ -2815,10 +2826,14 @@ public static String getDocComment(EclipseNode eclipseNode) {
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;

Expand Down
4 changes: 2 additions & 2 deletions src/core/lombok/eclipse/handlers/HandleBuilder.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -1053,7 +1053,7 @@ private void makePrefixedSetterMethodForBuilder(BuilderJob job, BuilderFieldData
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);
}
Expand Down
31 changes: 28 additions & 3 deletions src/core/lombok/eclipse/handlers/HandleConstructor.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -286,15 +286,17 @@ public void generate(
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);
}

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);
}
}

Expand Down Expand Up @@ -584,4 +586,27 @@ public MethodDeclaration createStaticConstructor(AccessLevel level, String name,
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);
}
}
}
27 changes: 27 additions & 0 deletions src/core/lombok/javac/handlers/HandleConstructor.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
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;
Expand Down Expand Up @@ -251,6 +252,7 @@ private void generate(JavacNode typeNode, AccessLevel level, List<JCAnnotation>

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);
Expand All @@ -259,6 +261,7 @@ private void generate(JavacNode typeNode, AccessLevel level, List<JCAnnotation>
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);
}
}
Expand Down Expand Up @@ -474,4 +477,28 @@ public JCMethodDecl createStaticConstructor(String name, AccessLevel level, Java
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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -25,6 +26,9 @@ public static class BuilderConstructorJavadocBuilder<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;
Expand Down Expand Up @@ -64,16 +68,26 @@ public BuilderConstructorJavadoc.BuilderConstructorJavadocBuilder<T> multiline(f
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")
Expand Down
9 changes: 9 additions & 0 deletions test/transform/resource/after-delombok/BuilderJavadoc.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ public java.lang.String toString() {
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
28 changes: 28 additions & 0 deletions test/transform/resource/after-delombok/ConstructorJavadoc.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
19 changes: 16 additions & 3 deletions test/transform/resource/after-ecj/BuilderConstructorJavadoc.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public static class BuilderConstructorJavadocBuilder<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;
Expand All @@ -17,27 +18,39 @@ public BuilderConstructorJavadocBuilder<T> predefWithJavadoc(final int x) {
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) {
this.basic = basic;
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() {
Expand Down
Loading
Loading