Merge remote-tracking branch 'upstream/pmd/7.0.x' into clem.type-annots-in-infer

This commit is contained in:
Clément Fournier
2022-12-05 23:32:39 +01:00
35 changed files with 589 additions and 569 deletions

View File

@ -188,6 +188,9 @@ conversions that may be made implicit.
* {% rule "java/design/UseUtilityClass" %}: The property `ignoredAnnotations` has been removed.
* {% rule "java/design/LawOfDemeter" %}: the rule has a new property `trustRadius`. This defines the maximum degree
of trusted data. The default of 1 is the most restrictive.
* {% rule "java/documentation/CommentContent" %}: The properties `caseSensitive` and `disallowedTerms` are removed. The
new property `fobiddenRegex` can be used now to define the disallowed terms with a single regular
expression.
#### Deprecated Rules

View File

@ -4,7 +4,7 @@
package net.sourceforge.pmd.lang.ast;
import java.util.Iterator;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
@ -112,13 +112,19 @@ public interface GenericToken<T extends GenericToken<T>> extends Comparable<T>,
*
* @throws IllegalArgumentException If the first token does not come before the other token
*/
static <T extends GenericToken<T>> Iterator<T> range(T from, T to) {
static <T extends GenericToken<T>> Iterable<T> range(T from, T to) {
if (from.compareTo(to) > 0) {
throw new IllegalArgumentException(from + " must come before " + to);
}
return IteratorUtil.generate(from, t -> t == to ? null : t.getNext());
return () -> IteratorUtil.generate(from, t -> t == to ? null : t.getNext());
}
/**
* Returns a stream corresponding to {@link #range(GenericToken, GenericToken)}.
*/
static <T extends GenericToken<T>> Stream<T> streamRange(T from, T to) {
return IteratorUtil.toStream(range(from, to).iterator());
}
/**
* Returns an iterable that enumerates all special tokens belonging

View File

@ -4,6 +4,7 @@
package net.sourceforge.pmd.lang.ast.impl.javacc;
import net.sourceforge.pmd.lang.ast.GenericToken;
import net.sourceforge.pmd.lang.ast.TextAvailableNode;
import net.sourceforge.pmd.lang.ast.impl.GenericNode;
@ -24,4 +25,11 @@ public interface JjtreeNode<N extends JjtreeNode<N>> extends GenericNode<N>, Tex
JavaccToken getLastToken();
/**
* Returns a token range, that includes the first and last token.
*/
default Iterable<JavaccToken> tokens() {
return GenericToken.range(getFirstToken(), getLastToken());
}
}

View File

@ -13,6 +13,7 @@ import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@ -20,6 +21,8 @@ import java.util.stream.StreamSupport;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.internal.util.IteratorUtil.AbstractIterator;
/**
* View on a string which doesn't copy the array for subsequence operations.
* This view is immutable. Since it uses a string internally it benefits from
@ -64,6 +67,12 @@ public final class Chars implements CharSequence {
}
/** Whether this slice is the empty string. */
public boolean isEmpty() {
return len == 0;
}
/**
* Wraps the given char sequence into a {@link Chars}. This may
* call {@link CharSequence#toString()}. If the sequence is already
@ -591,6 +600,40 @@ public final class Chars implements CharSequence {
return sb;
}
/**
* Split this slice into subslices, like {@link String#split(String)},
* except it's iterated lazily.
*/
public Iterable<Chars> splits(Pattern regex) {
return () -> new AbstractIterator<Chars>() {
final Matcher matcher = regex.matcher(Chars.this);
int lastPos = 0;
private boolean shouldRetry() {
if (matcher.find()) {
if (matcher.start() == 0 && matcher.end() == 0 && lastPos != len) {
return true; // zero length match at the start, we should retry once
}
setNext(subSequence(lastPos, matcher.start()));
lastPos = matcher.end();
} else if (lastPos != len) {
setNext(subSequence(lastPos, len));
} else {
done();
}
return false;
}
@Override
protected void computeNext() {
if (matcher.hitEnd()) {
done();
} else if (shouldRetry()) {
shouldRetry();
}
}
};
}
/**
* Returns a new reader for the whole contents of this char sequence.

View File

@ -19,6 +19,10 @@ import net.sourceforge.pmd.lang.document.FileLocation;
*/
public interface Reportable {
// todo add optional method to get the nearest node, to implement
// suppression that depends on tree structure (eg annotations) for
// not just nodes, for example, for comments or individual tokens
/**
* Returns the location at which this element should be reported.
*

View File

@ -16,11 +16,15 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import net.sourceforge.pmd.internal.util.IteratorUtil;
import net.sourceforge.pmd.util.CollectionUtil;
/**
@ -307,6 +311,39 @@ class CharsTest {
assertTrue(chars.contentEquals(Chars.wrap("A_B_C"), true));
}
@Test
void testSplits() {
Chars chars = Chars.wrap("a_a_b_c_s").slice(2, 5);
assertEquals("a_b_c", chars.toString());
testSplits(chars, "_");
testSplits(chars, "a");
testSplits(chars, "b");
testSplits(chars, "c");
assertEquals(listOf("", "_b_c"), listSplits(chars, "a"));
chars = chars.subSequence(1, 5);
assertEquals("_b_c", chars.toString());
assertEquals(listOf("", "b", "c"), listSplits(chars, "_"));
testSplits(Chars.wrap("abc"), "");
testSplits(Chars.wrap(""), "");
}
private List<String> listSplits(Chars chars, String regex) {
Pattern pattern = Pattern.compile(regex);
Iterator<Chars> splits = chars.splits(pattern).iterator();
return IteratorUtil.toList(IteratorUtil.map(splits, Chars::toString));
}
private void testSplits(Chars chars, String regex) {
List<String> splitList = listSplits(chars, regex);
List<String> expected = Arrays.asList(chars.toString().split(regex));
assertEquals(expected, splitList, "Split should behave like String#split");
}
@Test
void testSlice() {
// slice is offset + length

View File

@ -574,7 +574,7 @@ PARSER_END(JavaParserImpl)
TOKEN_MGR_DECLS :
{
protected List<Comment> comments = new ArrayList<Comment>();
protected List<JavaComment> comments = new ArrayList<JavaComment>();
}
/* WHITE SPACE */
@ -600,7 +600,7 @@ SPECIAL_TOKEN :
if (startOfNOPMD != -1) {
suppressMap.put(matchedToken.getBeginLine(), matchedToken.getImage().substring(startOfNOPMD + suppressMarker.length()));
}
comments.add(new SingleLineComment(matchedToken));
comments.add(new JavaComment(matchedToken));
}
}
@ -615,13 +615,13 @@ MORE :
<IN_FORMAL_COMMENT>
SPECIAL_TOKEN :
{
<FORMAL_COMMENT: "*/" > { comments.add(new FormalComment(matchedToken)); } : DEFAULT
<FORMAL_COMMENT: "*/" > { comments.add(new JavadocComment(matchedToken)); } : DEFAULT
}
<IN_MULTI_LINE_COMMENT>
SPECIAL_TOKEN :
{
<MULTI_LINE_COMMENT: "*/" > { comments.add(new MultiLineComment(matchedToken)); } : DEFAULT
<MULTI_LINE_COMMENT: "*/" > { comments.add(new JavaComment(matchedToken)); } : DEFAULT
}
<IN_FORMAL_COMMENT,IN_MULTI_LINE_COMMENT>

View File

@ -7,7 +7,6 @@ package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
import net.sourceforge.pmd.lang.java.ast.ASTList.ASTMaybeEmptyListOf;
import net.sourceforge.pmd.lang.java.ast.InternalInterfaces.AllChildrenAreOfType;
import net.sourceforge.pmd.lang.java.ast.internal.JavaAstUtils;
/**
* A block of code. This is a {@linkplain ASTStatement statement} that
@ -36,7 +35,7 @@ public final class ASTBlock extends ASTMaybeEmptyListOf<ASTStatement>
public boolean containsComment() {
JavaccToken t = getLastToken().getPreviousComment();
while (t != null) {
if (JavaAstUtils.isComment(t)) {
if (JavaComment.isComment(t)) {
return true;
}
t = t.getPreviousComment();

View File

@ -40,14 +40,14 @@ import net.sourceforge.pmd.lang.java.types.ast.LazyTypeResolver;
public final class ASTCompilationUnit extends AbstractJavaNode implements JavaNode, GenericNode<JavaNode>, RootNode {
private LazyTypeResolver lazyTypeResolver;
private List<Comment> comments;
private List<JavaComment> comments;
private AstInfo<ASTCompilationUnit> astInfo;
ASTCompilationUnit(int id) {
super(id);
}
public List<Comment> getComments() {
public List<JavaComment> getComments() {
return comments;
}
@ -60,7 +60,7 @@ public final class ASTCompilationUnit extends AbstractJavaNode implements JavaNo
return astInfo;
}
void setComments(List<Comment> comments) {
void setComments(List<JavaComment> comments) {
this.comments = comments;
}

View File

@ -253,7 +253,7 @@ final class AstDisambiguationPass {
* and in the worst case, the original {@link ASTAmbiguousName}.
*/
private static ASTExpression startResolve(ASTAmbiguousName name, ReferenceCtx ctx, boolean isPackageOrTypeOnly) {
Iterator<JavaccToken> tokens = TokenUtils.tokenRange(name);
Iterator<JavaccToken> tokens = name.tokens().iterator();
JavaccToken firstIdent = tokens.next();
TokenUtils.expectKind(firstIdent, JavaTokenKinds.IDENTIFIER);

View File

@ -1,127 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
import net.sourceforge.pmd.lang.document.FileLocation;
import net.sourceforge.pmd.reporting.Reportable;
public abstract class Comment implements Reportable {
// single regex, that captures: the start of a multi-line comment (/**|/*), the start of a single line comment (//)
// or the start of line within a multiline comment (*). It removes the end of the comment (*/) if existing.
private static final Pattern COMMENT_LINE_COMBINED = Pattern.compile("^(?://|/\\*\\*?|\\*)?(.*?)(?:\\*/|/)?$");
// Same as "\\R" - but \\R is only available with java8+
static final Pattern NEWLINES_PATTERN = Pattern.compile("\\u000D\\u000A|[\\u000A\\u000B\\u000C\\u000D\\u0085\\u2028\\u2029]");
private final JavaccToken token;
protected Comment(JavaccToken t) {
this.token = t;
}
@Override
public FileLocation getReportLocation() {
return token.getReportLocation();
}
/**
* @deprecated Use {@link #getText()}
*/
@Deprecated
public String getImage() {
return getToken().getImage();
}
public final JavaccToken getToken() {
return token;
}
public final CharSequence getText() {
return getToken().getImageCs();
}
/**
* Filters the comment by removing the leading comment marker (like {@code *}) of each line
* as well as the start markers ({@code //}, {@code /*} or {@code /**}
* and the end markers (<code>&#x2a;/</code>).
* Also leading and trailing empty lines are removed.
*
* @return the filtered comment
*/
public String getFilteredComment() {
List<String> lines = multiLinesIn();
lines = trim(lines);
return StringUtils.join(lines, PMD.EOL);
}
/**
* Removes the leading comment marker (like {@code *}) of each line
* of the comment as well as the start marker ({@code //}, {@code /*} or {@code /**}
* and the end markers (<code>&#x2a;/</code>).
*
* @return List of lines of the comments
*/
private List<String> multiLinesIn() {
String[] lines = NEWLINES_PATTERN.split(token.getImageCs());
List<String> filteredLines = new ArrayList<>(lines.length);
for (String rawLine : lines) {
String line = rawLine.trim();
Matcher allMatcher = COMMENT_LINE_COMBINED.matcher(line);
if (allMatcher.matches()) {
filteredLines.add(allMatcher.group(1).trim());
}
}
return filteredLines;
}
/**
* Similar to the String.trim() function, this one removes the leading and
* trailing empty/blank lines from the line list.
*
* @param lines the list of lines, which might contain empty lines
* @return the lines without leading or trailing blank lines.
*/
// note: this is only package private, since it is used by CommentUtil. Once CommentUtil is gone, this
// can be private
static List<String> trim(List<String> lines) {
if (lines == null) {
return Collections.emptyList();
}
List<String> result = new ArrayList<>(lines.size());
List<String> tempList = new ArrayList<>();
boolean foundFirstNonEmptyLine = false;
for (String line : lines) {
if (StringUtils.isNotBlank(line)) {
// new non-empty line: add all previous empty lines occurred before
result.addAll(tempList);
tempList.clear();
result.add(line);
foundFirstNonEmptyLine = true;
} else {
if (foundFirstNonEmptyLine) {
// add the empty line to a temporary list first
tempList.add(line);
}
}
}
return result;
}
}

View File

@ -19,22 +19,23 @@ import net.sourceforge.pmd.util.DataMap.SimpleDataKey;
final class CommentAssignmentPass {
private static final SimpleDataKey<FormalComment> FORMAL_COMMENT_KEY = DataMap.simpleDataKey("java.comment");
private static final SimpleDataKey<JavadocComment> FORMAL_COMMENT_KEY = DataMap.simpleDataKey("java.comment");
private CommentAssignmentPass() {
// utility class
}
static @Nullable FormalComment getComment(JavadocCommentOwner commentOwner) {
static @Nullable JavadocComment getComment(JavadocCommentOwner commentOwner) {
return commentOwner.getUserMap().get(CommentAssignmentPass.FORMAL_COMMENT_KEY);
}
private static void setComment(JavadocCommentOwner commentableNode, FormalComment comment) {
private static void setComment(JavadocCommentOwner commentableNode, JavadocComment comment) {
commentableNode.getUserMap().set(FORMAL_COMMENT_KEY, comment);
comment.setOwner(commentableNode);
}
public static void assignCommentsToDeclarations(ASTCompilationUnit root) {
final List<Comment> comments = root.getComments();
final List<JavaComment> comments = root.getComments();
if (comments.isEmpty()) {
return;
}
@ -45,11 +46,11 @@ final class CommentAssignmentPass {
for (JavaccToken maybeComment : GenericToken.previousSpecials(firstToken)) {
if (maybeComment.kind == JavaTokenKinds.FORMAL_COMMENT) {
FormalComment comment = new FormalComment(maybeComment);
JavadocComment comment = new JavadocComment(maybeComment);
// deduplicate the comment
int idx = Collections.binarySearch(comments, comment, Comparator.comparing(Comment::getReportLocation, FileLocation.COORDS_COMPARATOR));
int idx = Collections.binarySearch(comments, comment, Comparator.comparing(JavaComment::getReportLocation, FileLocation.COORDS_COMPARATOR));
assert idx >= 0 : "Formal comment not found? " + comment;
comment = (FormalComment) comments.get(idx);
comment = (JavadocComment) comments.get(idx);
setComment(commentableNode, comment);
continue outer;

View File

@ -1,47 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
import net.sourceforge.pmd.lang.java.javadoc.JavadocTag;
public class FormalComment extends Comment {
private static final Pattern JAVADOC_TAG = Pattern.compile("@([A-Za-z0-9]+)");
private final List<JavadocElement> children;
public FormalComment(JavaccToken t) {
super(t);
assert t.kind == JavaTokenKinds.FORMAL_COMMENT;
this.children = findJavadocs();
}
public List<JavadocElement> getChildren() {
return children;
}
private List<JavadocElement> findJavadocs() {
List<JavadocElement> kids = new ArrayList<>();
Matcher javadocTagMatcher = JAVADOC_TAG.matcher(getFilteredComment());
while (javadocTagMatcher.find()) {
JavadocTag tag = JavadocTag.tagFor(javadocTagMatcher.group(1));
int tagStartIndex = javadocTagMatcher.start(1);
if (tag != null) {
kids.add(new JavadocElement(getToken(), getBeginLine(), getBeginLine(),
// TODO valid?
tagStartIndex, tagStartIndex + tag.label.length() + 1, tag));
}
}
return kids;
}
}

View File

@ -0,0 +1,176 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import java.util.stream.Stream;
import net.sourceforge.pmd.internal.util.IteratorUtil;
import net.sourceforge.pmd.lang.ast.GenericToken;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
import net.sourceforge.pmd.lang.ast.impl.javacc.JjtreeNode;
import net.sourceforge.pmd.lang.document.Chars;
import net.sourceforge.pmd.lang.document.FileLocation;
import net.sourceforge.pmd.lang.java.ast.internal.JavaAstUtils;
import net.sourceforge.pmd.reporting.Reportable;
/**
* Wraps a comment token to provide some utilities.
* This is not a node, it's not part of the tree anywhere,
* just convenient.
*
* <p>This class represents any kind of comment. A specialized subclass
* provides more API for Javadoc comments, see {@link JavadocComment}.
*/
public class JavaComment implements Reportable {
//TODO maybe move part of this into pmd core
private final JavaccToken token;
JavaComment(JavaccToken t) {
this.token = t;
}
@Override
public FileLocation getReportLocation() {
return getToken().getReportLocation();
}
/**
* @deprecated Use {@link #getText()}
*/
@Deprecated
public String getImage() {
return getToken().getImage();
}
/** The token underlying this comment. */
public final JavaccToken getToken() {
return token;
}
public boolean isSingleLine() {
return token.kind == JavaTokenKinds.SINGLE_LINE_COMMENT;
}
public boolean hasJavadocContent() {
return token.kind == JavaTokenKinds.FORMAL_COMMENT;
}
/** Returns the full text of the comment. */
public Chars getText() {
return getToken().getImageCs();
}
/**
* Returns true if the given token has the kind
* of a comment token (there are three such kinds).
*/
public static boolean isComment(JavaccToken token) {
return JavaAstUtils.isComment(token);
}
/**
* Removes the leading comment marker (like {@code *}) of each line
* of the comment as well as the start marker ({@code //}, {@code /*} or {@code /**}
* and the end markers (<code>&#x2a;/</code>).
*
* <p>Empty lines are removed.
*
* @return List of lines of the comments
*/
public Iterable<Chars> getFilteredLines() {
return getFilteredLines(false);
}
public Iterable<Chars> getFilteredLines(boolean preserveEmptyLines) {
if (preserveEmptyLines) {
return () -> IteratorUtil.map(getText().lines().iterator(), JavaComment::removeCommentMarkup);
} else {
return () -> IteratorUtil.mapNotNull(
getText().lines().iterator(),
line -> {
line = removeCommentMarkup(line);
return line.isEmpty() ? null : line;
}
);
}
}
/**
* True if this is a comment delimiter or an asterisk. This
* tests the whole parameter and not a prefix/suffix.
*/
@SuppressWarnings("PMD.LiteralsFirstInComparisons") // a fp
public static boolean isMarkupWord(Chars word) {
return word.length() <= 3
&& (word.contentEquals("*")
|| word.contentEquals("//")
|| word.contentEquals("/*")
|| word.contentEquals("*/")
|| word.contentEquals("/**"));
}
/**
* Trim the start of the provided line to remove a comment
* markup opener ({@code //, /*, /**, *}) or closer {@code * /}.
*/
private static Chars removeCommentMarkup(Chars line) {
line = line.trim().removeSuffix("*/");
int subseqFrom = 0;
if (line.startsWith('/', 0)) {
if (line.startsWith("**", 1)) {
subseqFrom = 3;
} else if (line.startsWith('/', 1)
|| line.startsWith('*', 1)) {
subseqFrom = 2;
}
} else if (line.startsWith('*', 0)) {
subseqFrom = 1;
}
return line.subSequence(subseqFrom, line.length()).trim();
}
private static Stream<JavaccToken> getSpecialCommentsIn(JjtreeNode<?> node) {
return GenericToken.streamRange(node.getFirstToken(), node.getLastToken())
.flatMap(it -> IteratorUtil.toStream(GenericToken.previousSpecials(it).iterator()));
}
public static Stream<JavaComment> getLeadingComments(JavaNode node) {
if (node instanceof AccessNode) {
node = ((AccessNode) node).getModifiers();
}
return getSpecialCommentsIn(node).filter(JavaComment::isComment)
.map(JavaComment::toComment);
}
private static JavaComment toComment(JavaccToken tok) {
switch (tok.kind) {
case JavaTokenKinds.FORMAL_COMMENT:
return new JavadocComment(tok);
case JavaTokenKinds.MULTI_LINE_COMMENT:
case JavaTokenKinds.SINGLE_LINE_COMMENT:
return new JavaComment(tok);
default:
throw new IllegalArgumentException("Token is not a comment: " + tok);
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof JavaComment)) {
return false;
}
JavaComment that = (JavaComment) o;
return token.equals(that.token);
}
@Override
public int hashCode() {
return token.hashCode();
}
}

View File

@ -0,0 +1,35 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
/**
* A {@link JavaComment} that has Javadoc content.
*/
public final class JavadocComment extends JavaComment {
private JavadocCommentOwner owner;
JavadocComment(JavaccToken t) {
super(t);
assert t.kind == JavaTokenKinds.FORMAL_COMMENT;
}
void setOwner(JavadocCommentOwner owner) {
this.owner = owner;
}
/**
* Returns the owner of this comment. Null if this comment is
* misplaced.
*/
public @Nullable JavadocCommentOwner getOwner() {
return owner;
}
}

View File

@ -16,7 +16,7 @@ public interface JavadocCommentOwner extends JavaNode {
* Returns the javadoc comment that applies to this declaration. If
* there is none, returns null.
*/
default @Nullable FormalComment getJavadocComment() {
default @Nullable JavadocComment getJavadocComment() {
return CommentAssignmentPass.getComment(this);
}

View File

@ -1,32 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
import net.sourceforge.pmd.lang.document.FileLocation;
import net.sourceforge.pmd.lang.document.TextRange2d;
import net.sourceforge.pmd.lang.java.javadoc.JavadocTag;
public class JavadocElement extends Comment {
private final JavadocTag tag;
private final FileLocation reportLoc;
public JavadocElement(JavaccToken t, int theBeginLine, int theEndLine, int theBeginColumn, int theEndColumn, JavadocTag theTag) {
super(t);
this.tag = theTag;
this.reportLoc = FileLocation.range("TODO", TextRange2d.range2d(theBeginLine, theBeginColumn, theEndLine, theEndColumn));
}
public JavadocTag tag() {
return tag;
}
@Override
public FileLocation getReportLocation() {
return reportLoc;
}
}

View File

@ -1,15 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
public class MultiLineComment extends Comment {
public MultiLineComment(JavaccToken t) {
super(t);
}
}

View File

@ -1,15 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
public class SingleLineComment extends Comment {
public SingleLineComment(JavaccToken t) {
super(t);
}
}

View File

@ -4,7 +4,6 @@
package net.sourceforge.pmd.lang.java.ast;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
@ -25,19 +24,6 @@ final class TokenUtils {
}
public static <T extends GenericToken<T>> int compare(GenericToken<T> t1, GenericToken<T> t2) {
return t1.getRegion().compareTo(t2.getRegion());
}
public static <T extends GenericToken<T>> boolean isBefore(GenericToken<T> t1, GenericToken<T> t2) {
return t1.getRegion().compareTo(t2.getRegion()) < 0;
}
public static <T extends GenericToken<T>> boolean isAfter(GenericToken<T> t1, GenericToken<T> t2) {
return t1.getRegion().compareTo(t2.getRegion()) > 0;
}
public static <T extends GenericToken<T>> T nthFollower(T token, int n) {
if (n < 0) {
throw new IllegalArgumentException("Negative index?");
@ -103,7 +89,4 @@ final class TokenUtils {
assert token.kind == kind : "Expected " + token.getDocument().describeKind(kind) + ", got " + token;
}
public static Iterator<JavaccToken> tokenRange(JavaNode node) {
return GenericToken.range(node.getFirstToken(), node.getLastToken());
}
}

View File

@ -348,8 +348,8 @@ public final class JavaAstUtils {
// Since type and variable names obscure one another,
// it's ok to use a single renaming function.
Iterator<JavaccToken> thisIt = GenericToken.range(node.getFirstToken(), node.getLastToken());
Iterator<JavaccToken> thatIt = GenericToken.range(other.getFirstToken(), other.getLastToken());
Iterator<JavaccToken> thisIt = GenericToken.range(node.getFirstToken(), node.getLastToken()).iterator();
Iterator<JavaccToken> thatIt = GenericToken.range(other.getFirstToken(), other.getLastToken()).iterator();
int lastKind = 0;
while (thisIt.hasNext()) {
if (!thatIt.hasNext()) {

View File

@ -4,16 +4,13 @@
package net.sourceforge.pmd.lang.java.rule.codestyle;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
@ -21,7 +18,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTRecordDeclaration;
import net.sourceforge.pmd.lang.java.ast.AccessNode;
import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility;
import net.sourceforge.pmd.lang.java.ast.Comment;
import net.sourceforge.pmd.lang.java.ast.JavaComment;
import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
import net.sourceforge.pmd.lang.java.rule.internal.JavaPropertyUtil;
@ -38,6 +35,18 @@ import net.sourceforge.pmd.properties.PropertyFactory;
*/
public class CommentDefaultAccessModifierRule extends AbstractJavaRulechainRule {
private static final PropertyDescriptor<Pattern> REGEX_DESCRIPTOR =
PropertyFactory.regexProperty("regex")
.desc("Regular expression")
.defaultValue("\\/\\*\\s*(default|package)\\s*\\*\\/")
.build();
private static final PropertyDescriptor<Boolean> TOP_LEVEL_TYPES =
PropertyFactory.booleanProperty("checkTopLevelTypes")
.desc("Check for default access modifier in top-level classes, annotations, and enums")
.defaultValue(false)
.build();
private static final PropertyDescriptor<List<String>> IGNORED_ANNOTS =
JavaPropertyUtil.ignoredAnnotationsDescriptor(
"com.google.common.annotations.VisibleForTesting",
@ -53,35 +62,15 @@ public class CommentDefaultAccessModifierRule extends AbstractJavaRulechainRule
"org.junit.jupiter.api.AfterAll"
);
private static final PropertyDescriptor<Pattern> REGEX_DESCRIPTOR =
PropertyFactory.regexProperty("regex")
.desc("Regular expression")
.defaultValue("\\/\\*\\s*(default|package)\\s*\\*\\/").build();
private static final PropertyDescriptor<Boolean> TOP_LEVEL_TYPES =
PropertyFactory.booleanProperty("checkTopLevelTypes")
.desc("Check for default access modifier in top-level classes, annotations, and enums")
.defaultValue(false).build();
private static final String MESSAGE = "To avoid mistakes add a comment at the beginning of the {0} {1} if you want a default access modifier";
private final Set<Integer> interestingLineNumberComments = new HashSet<>();
public CommentDefaultAccessModifierRule() {
super(ASTCompilationUnit.class, ASTMethodDeclaration.class, ASTAnyTypeDeclaration.class,
ASTConstructorDeclaration.class, ASTFieldDeclaration.class);
super(ASTMethodDeclaration.class, ASTAnyTypeDeclaration.class,
ASTConstructorDeclaration.class, ASTFieldDeclaration.class);
definePropertyDescriptor(IGNORED_ANNOTS);
definePropertyDescriptor(REGEX_DESCRIPTOR);
definePropertyDescriptor(TOP_LEVEL_TYPES);
}
@Override
public Object visit(final ASTCompilationUnit node, final Object data) {
interestingLineNumberComments.clear();
for (final Comment comment : node.getComments()) {
if (getProperty(REGEX_DESCRIPTOR).matcher(comment.getText()).matches()) {
interestingLineNumberComments.add(comment.getBeginLine());
}
}
return data;
}
@Override
public Object visit(final ASTMethodDeclaration decl, final Object data) {
@ -141,7 +130,7 @@ public class CommentDefaultAccessModifierRule extends AbstractJavaRulechainRule
private void report(RuleContext ctx, AccessNode decl, String kind, String signature) {
ctx.addViolationWithMessage(decl, MESSAGE, kind, signature);
ctx.addViolation(decl, kind, signature);
}
private boolean shouldReportNonTopLevel(final AccessNode decl) {
@ -158,16 +147,22 @@ public class CommentDefaultAccessModifierRule extends AbstractJavaRulechainRule
return decl.getVisibility() == Visibility.V_PACKAGE
// if is a default access modifier check if there is a comment
// in this line
&& !interestingLineNumberComments.contains(decl.getBeginLine());
&& !hasOkComment(decl);
}
private boolean isNotIgnored(AccessNode decl) {
return getProperty(IGNORED_ANNOTS).stream().noneMatch(decl::isAnnotationPresent);
}
private boolean hasOkComment(AccessNode node) {
Pattern regex = getProperty(REGEX_DESCRIPTOR);
return JavaComment.getLeadingComments(node)
.anyMatch(it -> regex.matcher(it.getText()).matches());
}
private boolean shouldReportTypeDeclaration(ASTAnyTypeDeclaration decl) {
// don't report on interfaces
return !decl.isRegularInterface()
return !(decl.isRegularInterface() && !decl.isAnnotation())
&& isMissingComment(decl)
&& isNotIgnored(decl)
// either nested or top level and we should check it

View File

@ -20,8 +20,8 @@ import net.sourceforge.pmd.lang.java.ast.ASTMethodCall;
import net.sourceforge.pmd.lang.java.ast.ASTSwitchLabel;
import net.sourceforge.pmd.lang.java.ast.ASTSwitchLike;
import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess;
import net.sourceforge.pmd.lang.java.ast.Comment;
import net.sourceforge.pmd.lang.java.ast.FormalComment;
import net.sourceforge.pmd.lang.java.ast.JavaComment;
import net.sourceforge.pmd.lang.java.ast.JavadocComment;
import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.lang.java.symbols.JAccessibleElementSymbol;
@ -170,8 +170,8 @@ public class UnnecessaryImportRule extends AbstractJavaRule {
private void visitComments(ASTCompilationUnit node) {
// todo improve that when we have a javadoc parser
for (Comment comment : node.getComments()) {
if (!(comment instanceof FormalComment)) {
for (JavaComment comment : node.getComments()) {
if (!(comment instanceof JavadocComment)) {
continue;
}
for (Pattern p : PATTERNS) {
@ -245,7 +245,7 @@ public class UnnecessaryImportRule extends AbstractJavaRule {
if (node.getQualifier() == null) {
OverloadSelectionResult overload = node.getOverloadSelectionInfo();
if (overload.isFailed()) {
return null; // todo we're erring towards FPs
return null; // todo we're erring towards FPs
}
ShadowChainIterator<JMethodSig, ScopeInfo> scopeIter =
@ -307,7 +307,7 @@ public class UnnecessaryImportRule extends AbstractJavaRule {
if (!it.isStatic() && onlyStatic) {
return false;
}
// This is the class that contains the symbol
// This is the class that contains the symbol
// we're looking for.
// We have to test whether this symbol is contained
// by the imported type or package.

View File

@ -22,9 +22,11 @@ import java.util.stream.Stream;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTArgumentList;
import net.sourceforge.pmd.lang.java.ast.ASTArrayAccess;
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall;
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement;
@ -39,7 +41,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.java.ast.QualifiableExpression;
import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.lang.java.rule.internal.DataflowPass;
import net.sourceforge.pmd.lang.java.rule.internal.DataflowPass.AssignmentEntry;
import net.sourceforge.pmd.lang.java.rule.internal.DataflowPass.DataflowResult;
@ -73,7 +75,7 @@ import net.sourceforge.pmd.properties.PropertyFactory;
* @since 5.0
*
*/
public class LawOfDemeterRule extends AbstractJavaRulechainRule {
public class LawOfDemeterRule extends AbstractJavaRule {
private static final PropertyDescriptor<Integer> TRUST_RADIUS =
@ -86,7 +88,6 @@ public class LawOfDemeterRule extends AbstractJavaRulechainRule {
private static final String METHOD_CALL_ON_FOREIGN_VALUE = "Call to `{0}` on foreign value `{1}` (degree {2})";
public LawOfDemeterRule() {
super(ASTMethodCall.class, ASTFieldAccess.class);
definePropertyDescriptor(TRUST_RADIUS);
}
@ -100,7 +101,19 @@ public class LawOfDemeterRule extends AbstractJavaRulechainRule {
private final Map<ASTExpression, Integer> degreeCache = new LinkedHashMap<>();
@Override
public void end(RuleContext ctx) {
public void apply(Node target, RuleContext ctx) {
degreeCache.clear();
// reimplement our own traversal instead of using the rulechain,
// so that we have a stable traversal order.
((ASTCompilationUnit) target)
.descendants().crossFindBoundaries()
.forEach(it -> {
if (it instanceof ASTMethodCall) {
this.visit((ASTMethodCall) it, ctx);
} else if (it instanceof ASTFieldAccess) {
this.visit((ASTFieldAccess) it, ctx);
}
});
degreeCache.clear(); // avoid memory leak
}

View File

@ -4,152 +4,74 @@
package net.sourceforge.pmd.lang.java.rule.documentation;
import static net.sourceforge.pmd.properties.PropertyFactory.booleanProperty;
import static net.sourceforge.pmd.properties.PropertyFactory.stringListProperty;
import static net.sourceforge.pmd.properties.PropertyFactory.regexProperty;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.document.Chars;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.Comment;
import net.sourceforge.pmd.lang.java.ast.JavaComment;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.properties.PropertySource;
/**
* A rule that checks for illegal words in the comment text.
*
* TODO implement regex option
*
* @author Brian Remedios
*/
public class CommentContentRule extends AbstractJavaRulechainRule {
private boolean caseSensitive;
private List<String> originalBadWords;
private List<String> currentBadWords;
// ignored when property above == True
public static final PropertyDescriptor<Boolean> CASE_SENSITIVE_DESCRIPTOR = booleanProperty("caseSensitive").defaultValue(false).desc("Case sensitive").build();
public static final PropertyDescriptor<List<String>> DISSALLOWED_TERMS_DESCRIPTOR =
stringListProperty("disallowedTerms")
.desc("Illegal terms or phrases")
.defaultValues("idiot", "jerk").build(); // TODO make blank property? or add more defaults?
private static final Set<PropertyDescriptor<?>> NON_REGEX_PROPERTIES;
static {
NON_REGEX_PROPERTIES = new HashSet<>(1);
NON_REGEX_PROPERTIES.add(CASE_SENSITIVE_DESCRIPTOR);
}
private static final PropertyDescriptor<Pattern> DISSALLOWED_TERMS_DESCRIPTOR =
regexProperty("forbiddenRegex")
.desc("Illegal terms or phrases")
.defaultValue("idiot|jerk").build();
public CommentContentRule() {
super(ASTCompilationUnit.class);
definePropertyDescriptor(CASE_SENSITIVE_DESCRIPTOR);
definePropertyDescriptor(DISSALLOWED_TERMS_DESCRIPTOR);
}
/**
* Capture values and perform all the case-conversions once per run
*/
@Override
public void start(RuleContext ctx) {
originalBadWords = getProperty(DISSALLOWED_TERMS_DESCRIPTOR);
caseSensitive = getProperty(CASE_SENSITIVE_DESCRIPTOR);
if (caseSensitive) {
currentBadWords = originalBadWords;
} else {
currentBadWords = new ArrayList<>();
for (String badWord : originalBadWords) {
currentBadWords.add(badWord.toUpperCase(Locale.ROOT));
}
}
}
private List<String> illegalTermsIn(Comment comment) {
if (currentBadWords.isEmpty()) {
return Collections.emptyList();
}
String commentText = comment.getFilteredComment();
if (StringUtils.isBlank(commentText)) {
return Collections.emptyList();
}
if (!caseSensitive) {
commentText = commentText.toUpperCase(Locale.ROOT);
}
List<String> foundWords = new ArrayList<>();
for (int i = 0; i < currentBadWords.size(); i++) {
if (commentText.contains(currentBadWords.get(i))) {
foundWords.add(originalBadWords.get(i));
}
}
return foundWords;
}
private String errorMsgFor(List<String> badWords) {
StringBuilder msg = new StringBuilder(this.getMessage()).append(": ");
if (badWords.size() == 1) {
msg.append("Invalid term: '").append(badWords.get(0)).append('\'');
} else {
msg.append("Invalid terms: '");
msg.append(badWords.get(0));
for (int i = 1; i < badWords.size(); i++) {
msg.append("', '").append(badWords.get(i));
}
msg.append('\'');
}
return msg.toString();
}
@Override
public Object visit(ASTCompilationUnit cUnit, Object data) {
// NPE patch: Eclipse plugin doesn't call start() at onset?
if (currentBadWords == null) {
start(null);
}
Pattern pattern = getProperty(DISSALLOWED_TERMS_DESCRIPTOR);
for (Comment comment : cUnit.getComments()) {
List<String> badWords = illegalTermsIn(comment);
if (badWords.isEmpty()) {
for (JavaComment comment : cUnit.getComments()) {
List<Integer> lineNumbers = illegalTermsIn(comment, pattern);
if (lineNumbers.isEmpty()) {
continue;
}
addViolationWithMessage(data, cUnit, errorMsgFor(badWords), comment.getBeginLine(), comment.getEndLine());
int offset = comment.getBeginLine();
for (int lineNum : lineNumbers) {
int lineNumWithOff = lineNum + offset;
addViolationWithMessage(
data,
cUnit,
"Line matches forbidden content regex (" + pattern.pattern() + ")",
lineNumWithOff,
lineNumWithOff
);
}
}
return null;
}
private boolean hasDisallowedTerms() {
List<String> terms = getProperty(DISSALLOWED_TERMS_DESCRIPTOR);
return !terms.isEmpty();
private List<Integer> illegalTermsIn(JavaComment comment, Pattern violationRegex) {
List<Integer> lines = new ArrayList<>();
int i = 0;
for (Chars line : comment.getFilteredLines(true)) {
if (violationRegex.matcher(line).find()) {
lines.add(i);
}
}
return lines;
}
@Deprecated
public boolean hasDissallowedTerms() {
return this.hasDisallowedTerms();
}
/**
* @see PropertySource#dysfunctionReason()
*/
@Override
public String dysfunctionReason() {
return hasDissallowedTerms() ? null : "No disallowed terms specified";
}
}

View File

@ -13,12 +13,12 @@ import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import net.sourceforge.pmd.lang.document.Chars;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.Comment;
import net.sourceforge.pmd.lang.java.ast.JavaComment;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.properties.PropertyFactory;
import net.sourceforge.pmd.util.StringUtil;
/**
* A rule to manage those who just can't shut up...
@ -33,13 +33,15 @@ public class CommentSizeRule extends AbstractJavaRulechainRule {
.require(positive()).defaultValue(6).build();
public static final PropertyDescriptor<Integer> MAX_LINE_LENGTH
= PropertyFactory.intProperty("maxLineLength")
.desc("Maximum line length")
.require(positive()).defaultValue(80).build();
= PropertyFactory.intProperty("maxLineLength")
.desc("Maximum line length")
.require(positive()).defaultValue(80).build();
private static final String CR = "\n";
static final Set<String> IGNORED_LINES = setOf("//", "/*", "/**", "*", "*/");
static final Set<Chars> IGNORED_LINES = setOf(Chars.wrap("//"),
Chars.wrap("/*"),
Chars.wrap("/**"),
Chars.wrap("*"),
Chars.wrap("*/"));
public CommentSizeRule() {
super(ASTCompilationUnit.class);
@ -47,64 +49,14 @@ public class CommentSizeRule extends AbstractJavaRulechainRule {
definePropertyDescriptor(MAX_LINE_LENGTH);
}
private static boolean hasRealText(String line) {
return !StringUtils.isBlank(line) && !IGNORED_LINES.contains(line.trim());
}
private boolean hasTooManyLines(Comment comment) {
String[] lines = comment.getImage().split(CR);
int start = 0; // start from top
for (; start < lines.length; start++) {
if (hasRealText(lines[start])) {
break;
}
}
int end = lines.length - 1; // go up from bottom
for (; end > 0; end--) {
if (hasRealText(lines[end])) {
break;
}
}
int lineCount = end - start + 1;
return lineCount > getProperty(MAX_LINES);
}
private String withoutCommentMarkup(String text) {
return StringUtil.withoutPrefixes(text.trim(), "//", "*", "/**");
}
private List<Integer> overLengthLineIndicesIn(Comment comment) {
int maxLength = getProperty(MAX_LINE_LENGTH);
List<Integer> indices = new ArrayList<>();
String[] lines = comment.getImage().split(CR);
int offset = comment.getBeginLine();
for (int i = 0; i < lines.length; i++) {
String cleaned = withoutCommentMarkup(lines[i]);
if (cleaned.length() > maxLength) {
indices.add(i + offset);
}
}
return indices;
}
@Override
public Object visit(ASTCompilationUnit cUnit, Object data) {
for (Comment comment : cUnit.getComments()) {
for (JavaComment comment : cUnit.getComments()) {
if (hasTooManyLines(comment)) {
addViolationWithMessage(data, cUnit, this.getMessage() + ": Too many lines", comment.getBeginLine(),
comment.getEndLine());
addViolationWithMessage(data, cUnit, this.getMessage()
+ ": Too many lines", comment.getBeginLine(), comment.getEndLine());
}
List<Integer> lineNumbers = overLengthLineIndicesIn(comment);
@ -112,11 +64,60 @@ public class CommentSizeRule extends AbstractJavaRulechainRule {
continue;
}
for (Integer lineNum : lineNumbers) {
addViolationWithMessage(data, cUnit, this.getMessage() + ": Line too long", lineNum, lineNum);
int offset = comment.getBeginLine();
for (int lineNum : lineNumbers) {
int lineNumWithOff = lineNum + offset;
addViolationWithMessage(
data,
cUnit,
this.getMessage() + ": Line too long",
lineNumWithOff,
lineNum
);
}
}
return null;
}
private static boolean hasRealText(Chars line) {
return !StringUtils.isBlank(line) && !IGNORED_LINES.contains(line.trim());
}
private boolean hasTooManyLines(JavaComment comment) {
int firstLineWithText = -1;
int lastLineWithText;
int i = 0;
int maxLines = getProperty(MAX_LINES);
for (Chars line : comment.getText().lines()) {
boolean real = hasRealText(line);
if (real) {
lastLineWithText = i;
if (firstLineWithText == -1) {
firstLineWithText = i;
}
if (lastLineWithText - firstLineWithText + 1 > maxLines) {
return true;
}
}
i++;
}
return false;
}
private List<Integer> overLengthLineIndicesIn(JavaComment comment) {
int maxLength = getProperty(MAX_LINE_LENGTH);
List<Integer> indices = new ArrayList<>();
int i = 0;
for (Chars line : comment.getFilteredLines(true)) {
if (line.length() > maxLength) {
indices.add(i);
}
}
return indices;
}
}

View File

@ -8,7 +8,7 @@ import java.util.List;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.Comment;
import net.sourceforge.pmd.lang.java.ast.JavaComment;
import net.sourceforge.pmd.lang.rule.xpath.internal.AstElementNode;
import net.sf.saxon.expr.XPathContext;
@ -63,10 +63,10 @@ public class GetCommentOnFunction extends BaseJavaXPathFunction {
int codeBeginLine = contextNode.getBeginLine();
int codeEndLine = contextNode.getEndLine();
List<Comment> commentList = contextNode.getFirstParentOfType(ASTCompilationUnit.class).getComments();
for (Comment comment : commentList) {
List<JavaComment> commentList = contextNode.getFirstParentOfType(ASTCompilationUnit.class).getComments();
for (JavaComment comment : commentList) {
if (comment.getBeginLine() == codeBeginLine || comment.getEndLine() == codeEndLine) {
return new StringValue(comment.getImage());
return new StringValue(comment.getText());
}
}
return EmptyAtomicSequence.INSTANCE;

View File

@ -292,7 +292,7 @@ public class Éléphant {}
language="java"
since="5.4.0"
class="net.sourceforge.pmd.lang.java.rule.codestyle.CommentDefaultAccessModifierRule"
message="Missing commented default access modifier"
message="Missing commented default access modifier on {0} ''{1}''"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_codestyle.html#commentdefaultaccessmodifier">
<description>
To avoid mistakes if we want that an Annotation, Class, Enum, Method, Constructor or Field have a default access modifier

View File

@ -7,11 +7,14 @@ package net.sourceforge.pmd.lang.java.ast;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test;
import net.sourceforge.pmd.lang.java.BaseParserTest;
@ -28,14 +31,17 @@ class CommentAssignmentTest extends BaseParserTest {
+ " /** a formal comment with blank lines\n\n\n */"
+ "}");
Comment comment = node.getComments().get(0);
JavaComment comment = node.getComments().get(0);
assertThat(comment, instanceOf(MultiLineComment.class));
assertEquals("multi line comment with blank lines", comment.getFilteredComment());
assertFalse(comment.isSingleLine());
assertFalse(comment.hasJavadocContent());
assertEquals("multi line comment with blank lines", StringUtils.join(comment.getFilteredLines(), ' '));
comment = node.getComments().get(1);
assertThat(comment, instanceOf(FormalComment.class));
assertEquals("a formal comment with blank lines", comment.getFilteredComment());
assertFalse(comment.isSingleLine());
assertTrue(comment.hasJavadocContent());
assertThat(comment, instanceOf(JavadocComment.class));
assertEquals("a formal comment with blank lines", StringUtils.join(comment.getFilteredLines(), ' '));
}
@ -51,7 +57,7 @@ class CommentAssignmentTest extends BaseParserTest {
+ " /** Comment 3 */\n"
+ " public void method2() {}" + "}");
List<ASTMethodDeclaration> methods = node.findDescendantsOfType(ASTMethodDeclaration.class);
List<ASTMethodDeclaration> methods = node.descendants(ASTMethodDeclaration.class).toList();
assertCommentEquals(methods.get(0), "/** Comment 1 */");
assertCommentEquals(methods.get(1), "/** Comment 3 */");
}

View File

@ -9,7 +9,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.lang.java.BaseParserTest;
class CommentTest extends BaseParserTest {
@ -46,7 +45,7 @@ class CommentTest extends BaseParserTest {
+ " */\n";
String filtered = filter(comment);
assertEquals(2, lineCount(filtered));
assertEquals("line 1" + PMD.EOL + "line 2", filtered);
assertEquals("line 1\nline 2", filtered);
}
@Test
@ -58,7 +57,7 @@ class CommentTest extends BaseParserTest {
+ " */\r\n";
String filtered = filter(comment);
assertEquals(2, lineCount(filtered));
assertEquals("line 1" + PMD.EOL + "line 2", filtered);
assertEquals("line 1\nline 2", filtered);
}
@Test
@ -70,7 +69,7 @@ class CommentTest extends BaseParserTest {
+ " */\n";
String filtered = filter(comment);
assertEquals(2, lineCount(filtered));
assertEquals("line 1" + PMD.EOL + "line 2", filtered);
assertEquals("line 1\nline 2", filtered);
}
@Test
@ -82,7 +81,7 @@ class CommentTest extends BaseParserTest {
+ " */\r\n";
String filtered = filter(comment);
assertEquals(2, lineCount(filtered));
assertEquals("line 1" + PMD.EOL + "line 2", filtered);
assertEquals("line 1\nline 2", filtered);
}
@Test
@ -95,14 +94,15 @@ class CommentTest extends BaseParserTest {
+ " */\n";
String filtered = filter(comment);
assertEquals(2, lineCount(filtered));
assertEquals("line 1" + PMD.EOL + "line 2", filtered);
assertEquals("line 1\nline 2", filtered);
}
private String filter(String comment) {
return java.parse(comment).getComments().get(0).getFilteredComment();
JavaComment firstComment = java.parse(comment).getComments().get(0);
return StringUtils.join(firstComment.getFilteredLines(), '\n');
}
private int lineCount(String filtered) {
return StringUtils.countMatches(filtered, PMD.EOL) + 1;
return StringUtils.countMatches(filtered, '\n') + 1;
}
}

Some files were not shown because too many files have changed in this diff Show More