From 4a6bc933e196d0f281bf6851e49729125a733699 Mon Sep 17 00:00:00 2001 From: kenji Date: Mon, 27 Jul 2020 11:47:26 +0200 Subject: [PATCH 001/154] Manage swift5 string literals --- .../pmd/lang/swift/antlr4/Swift.g4 | 9 ++++- .../lang/swift/cpd/testdata/Swift5.0.swift | 8 +++++ .../pmd/lang/swift/cpd/testdata/Swift5.0.txt | 33 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 b/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 index 01ee4ad84c..c71b5fbda7 100644 --- a/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 +++ b/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 @@ -1018,7 +1018,7 @@ ImplicitParameterName : '$' DecimalLiteral ; // TODO: don't allow '_' here // GRAMMAR OF A LITERAL booleanLiteral: BooleanLiteral ; -literal : numericLiteral | MultiStringLiteral | SingleStringLiteral | BooleanLiteral | NilLiteral ; +literal : numericLiteral | MultiStringLiteral | DashedSingleStringLiteral | SingleStringLiteral | BooleanLiteral | NilLiteral ; // GRAMMAR OF AN INTEGER LITERAL @@ -1078,6 +1078,13 @@ fragment MultiQuotedTextItem : MultiInterpolatedString ; fragment MultiInterpolatedString: '\\(' (MultiQuotedTextItem | SingleStringLiteral)* ')'; +DashedSingleStringLiteral : '#"' DashedMultiQuotedText? '"#' ; +fragment DashedMultiQuotedText : DashedMultiQuotedTextItem+ ; +fragment DashedMultiQuotedTextItem : EscapedCharacter | DashedMultiInterpolatedString + | ~[\\\u000A\u000D] + ; +fragment DashedMultiInterpolatedString: '\\#(' (DashedMultiQuotedTextItem | DashedSingleStringLiteral)* ')'; + // StringLiteral : '"' QuotedText? '"' ; SingleStringLiteral : '"' QuotedText? '"' ; fragment SingleDoubleQuote : '"' | ~["] ; diff --git a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift index 6e77f1dd29..cf326c21c7 100644 --- a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift +++ b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift @@ -38,3 +38,11 @@ var x = 2 print(x[keyPath: id]) // prints 2 x[keyPath: id] = 3 print(x[keyPath: id]) // prints 3 + +// https://www.swiftbysundell.com/articles/string-literals-in-swift/ +let rawString = #"Press "Continue" to close this dialog."# +extension URL { + func html(withTitle title: String) -> String { + return #"\#(title)"# + } +} diff --git a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt index 5eaa13610c..0a89406a83 100644 --- a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt +++ b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt @@ -197,4 +197,37 @@ L40 [id] 18 19 [\]] 20 20 [)] 21 21 +L43 + [let] 1 3 + [rawString] 5 13 + [=] 15 15 + [#"Press "Continue" to close this d[ 17 58 +L44 + [extension] 1 9 + [URL] 11 13 + [{] 15 15 +L45 + [func] 5 8 + [html] 10 13 + [(] 14 14 + [withTitle] 15 23 + [title] 25 29 + [:] 30 30 + [String] 32 37 + [)] 38 38 + [->] 40 41 + [String] 43 48 + [{] 50 50 +L46 + [return] 9 14 + [#"\\[ 16 61 +L47 + [}] 5 5 +L48 + [}] 1 1 +L49 + [let] 1 3 + [regex] 5 9 + [=] 11 11 + [#"w+"#] 13 18 EOF From af31b3a7736fe328e5688247aaf4c05602701158 Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Mon, 30 Nov 2020 16:58:58 +0100 Subject: [PATCH 002/154] Add GenericToken.getKind() to interface --- .../java/net/sourceforge/pmd/cpd/token/AntlrToken.java | 1 + .../java/net/sourceforge/pmd/lang/ast/GenericToken.java | 7 +++++++ .../pmd/cpd/token/internal/BaseTokenFilterTest.java | 5 +++++ pmd-cpp/src/main/ant/alljavacc.xml | 5 +++++ pmd-java/src/main/ant/alljavacc.xml | 5 +++++ pmd-javascript/src/main/ant/alljavacc.xml | 5 +++++ pmd-jsp/src/main/ant/alljavacc.xml | 5 +++++ pmd-matlab/src/main/ant/alljavacc.xml | 5 +++++ pmd-modelica/src/main/ant/alljavacc.xml | 5 +++++ pmd-objectivec/src/main/ant/alljavacc.xml | 5 +++++ pmd-plsql/src/main/ant/alljavacc.xml | 5 +++++ pmd-python/src/main/ant/alljavacc.xml | 5 +++++ .../java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java | 5 +++++ pmd-visualforce/src/main/ant/alljavacc.xml | 5 +++++ pmd-vm/src/main/ant/alljavacc.xml | 5 +++++ 15 files changed, 73 insertions(+) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrToken.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrToken.java index 05c38caaff..0a36e9419a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrToken.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrToken.java @@ -132,6 +132,7 @@ public class AntlrToken implements GenericToken { } } + @Override public int getKind() { return token.getType(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java index 18798da868..479fa67a81 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java @@ -51,4 +51,11 @@ public interface GenericToken { * @return a non-negative integer containing the begin column */ int getEndColumn(); + + /** + * Gets a unique integer representing the kind of token this is. + * + * The semantics of this kind depend on the language. + */ + int getKind(); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilterTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilterTest.java index 2478e0c913..3f5d8a7750 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilterTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilterTest.java @@ -63,6 +63,11 @@ public class BaseTokenFilterTest { public int getEndColumn() { return 0; } + + @Override + public int getKind() { + return 0; + } } class StringTokenManager implements TokenManager { diff --git a/pmd-cpp/src/main/ant/alljavacc.xml b/pmd-cpp/src/main/ant/alljavacc.xml index 800af4264b..aa41ee0817 100644 --- a/pmd-cpp/src/main/ant/alljavacc.xml +++ b/pmd-cpp/src/main/ant/alljavacc.xml @@ -88,6 +88,11 @@ public class Token implements GenericToken, java.io.Serializable]]> diff --git a/pmd-java/src/main/ant/alljavacc.xml b/pmd-java/src/main/ant/alljavacc.xml index d2c6296b63..ee9bcfe7b8 100644 --- a/pmd-java/src/main/ant/alljavacc.xml +++ b/pmd-java/src/main/ant/alljavacc.xml @@ -130,6 +130,11 @@ public class Token implements GenericToken, java.io.Serializable]]> diff --git a/pmd-javascript/src/main/ant/alljavacc.xml b/pmd-javascript/src/main/ant/alljavacc.xml index 405026b5cd..43c075273e 100644 --- a/pmd-javascript/src/main/ant/alljavacc.xml +++ b/pmd-javascript/src/main/ant/alljavacc.xml @@ -88,6 +88,11 @@ public class Token implements GenericToken, java.io.Serializable]]> diff --git a/pmd-jsp/src/main/ant/alljavacc.xml b/pmd-jsp/src/main/ant/alljavacc.xml index 80c1b43001..367e3e14cc 100644 --- a/pmd-jsp/src/main/ant/alljavacc.xml +++ b/pmd-jsp/src/main/ant/alljavacc.xml @@ -108,6 +108,11 @@ public class Token implements GenericToken, java.io.Serializable]]> diff --git a/pmd-matlab/src/main/ant/alljavacc.xml b/pmd-matlab/src/main/ant/alljavacc.xml index 646b4740f1..a6295481be 100644 --- a/pmd-matlab/src/main/ant/alljavacc.xml +++ b/pmd-matlab/src/main/ant/alljavacc.xml @@ -88,6 +88,11 @@ public class Token implements GenericToken, java.io.Serializable]]> diff --git a/pmd-modelica/src/main/ant/alljavacc.xml b/pmd-modelica/src/main/ant/alljavacc.xml index 0b6c12a533..06e0de1824 100644 --- a/pmd-modelica/src/main/ant/alljavacc.xml +++ b/pmd-modelica/src/main/ant/alljavacc.xml @@ -120,6 +120,11 @@ public class Token implements GenericToken, java.io.Serializable]]> diff --git a/pmd-objectivec/src/main/ant/alljavacc.xml b/pmd-objectivec/src/main/ant/alljavacc.xml index 16a4dd9e84..27f2b9e62b 100644 --- a/pmd-objectivec/src/main/ant/alljavacc.xml +++ b/pmd-objectivec/src/main/ant/alljavacc.xml @@ -88,6 +88,11 @@ public class Token implements GenericToken, java.io.Serializable]]> diff --git a/pmd-plsql/src/main/ant/alljavacc.xml b/pmd-plsql/src/main/ant/alljavacc.xml index 5d0e23135a..d0752af8bf 100644 --- a/pmd-plsql/src/main/ant/alljavacc.xml +++ b/pmd-plsql/src/main/ant/alljavacc.xml @@ -141,6 +141,11 @@ public class Token implements GenericToken, java.io.Serializable]]> diff --git a/pmd-python/src/main/ant/alljavacc.xml b/pmd-python/src/main/ant/alljavacc.xml index 1996f86bf1..e62797eed7 100644 --- a/pmd-python/src/main/ant/alljavacc.xml +++ b/pmd-python/src/main/ant/alljavacc.xml @@ -88,6 +88,11 @@ public class Token implements GenericToken, java.io.Serializable]]> diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java index c4842eccf9..5904cea775 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java @@ -56,6 +56,11 @@ public class ScalaTokenAdapter implements GenericToken { return token.pos().endColumn() + 1; } + @Override + public int getKind() { + throw new UnsupportedOperationException(); + } + @Override public String toString() { return "ScalaTokenAdapter{" diff --git a/pmd-visualforce/src/main/ant/alljavacc.xml b/pmd-visualforce/src/main/ant/alljavacc.xml index fff5966898..b8b6b922c9 100644 --- a/pmd-visualforce/src/main/ant/alljavacc.xml +++ b/pmd-visualforce/src/main/ant/alljavacc.xml @@ -108,6 +108,11 @@ public class Token implements GenericToken, java.io.Serializable]]> diff --git a/pmd-vm/src/main/ant/alljavacc.xml b/pmd-vm/src/main/ant/alljavacc.xml index 5be48f1bc5..4650be5669 100644 --- a/pmd-vm/src/main/ant/alljavacc.xml +++ b/pmd-vm/src/main/ant/alljavacc.xml @@ -121,6 +121,11 @@ public class Token implements GenericToken, java.io.Serializable]]> From 3d7fe9fb24aff7c84c195348257e584e04274f21 Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Thu, 26 Nov 2020 17:18:57 +0100 Subject: [PATCH 003/154] C++ can now ignore sequences of literals This enables the --ignore-literal-sequences option for the C++ language. This was previously only implemented for C#. --- .../java/net/sourceforge/pmd/cpd/GUI.java | 11 +- .../net/sourceforge/pmd/cpd/CPPTokenizer.java | 73 +++++++- .../sourceforge/pmd/cpd/CPPTokenizerTest.java | 20 +- .../lang/cpp/cpd/testdata/listOfNumbers.cpp | 21 +++ .../lang/cpp/cpd/testdata/listOfNumbers.txt | 176 ++++++++++++++++++ .../cpd/testdata/listOfNumbers_ignored.txt | 31 +++ 6 files changed, 326 insertions(+), 6 deletions(-) create mode 100644 pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.cpp create mode 100644 pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.txt create mode 100644 pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers_ignored.txt diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index 3f44d76b34..17c5b098f5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -167,7 +167,16 @@ public class GUI implements CPDListener { @Override public boolean canIgnoreLiteralSequences() { - return "cs".equals(terseName); + if (terseName == null) { + return false; + } + switch(terseName) { + case "cpp": + case "cs": + return true; + default: + return false; + } } }; } diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java index 5cf29e3d4f..98f809ee57 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java @@ -11,8 +11,12 @@ import java.util.Properties; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; +import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; +import net.sourceforge.pmd.cpd.token.TokenFilter; import net.sourceforge.pmd.lang.TokenManager; +import net.sourceforge.pmd.lang.ast.GenericToken; import net.sourceforge.pmd.lang.cpp.CppTokenManager; +import net.sourceforge.pmd.lang.cpp.ast.CppParserConstants; import net.sourceforge.pmd.util.IOUtil; /** @@ -23,12 +27,12 @@ public class CPPTokenizer extends JavaCCTokenizer { private boolean skipBlocks = true; private String skipBlocksStart; private String skipBlocksEnd; + private boolean ignoreLiteralSequences = false; /** * Sets the possible options for the C++ tokenizer. * - * @param properties - * the properties + * @param properties the properties * @see #OPTION_SKIP_BLOCKS * @see #OPTION_SKIP_BLOCKS_PATTERN */ @@ -44,6 +48,7 @@ public class CPPTokenizer extends JavaCCTokenizer { skipBlocksEnd = split[1]; } } + ignoreLiteralSequences = Boolean.parseBoolean(properties.getProperty(OPTION_IGNORE_LITERAL_SEQUENCES, "false")); } private String maybeSkipBlocks(String test) throws IOException { @@ -80,4 +85,68 @@ public class CPPTokenizer extends JavaCCTokenizer { throw new RuntimeException(e); } } + + @Override + protected TokenFilter getTokenFilter(final TokenManager tokenManager) { + return new CppTokenFilter(tokenManager, ignoreLiteralSequences); + } + + private static class CppTokenFilter extends JavaCCTokenFilter { + private final boolean ignoreLiteralSequences; + private boolean discardingLiterals = false; + private boolean discardCurrent = false; + + CppTokenFilter(final TokenManager tokenManager, final boolean ignoreLiteralSequences) { + super(tokenManager); + this.ignoreLiteralSequences = ignoreLiteralSequences; + } + + @Override + protected void analyzeTokens(final GenericToken currentToken, final Iterable remainingTokens) { + discardCurrent = false; + skipLiteralSequences(currentToken, remainingTokens); + } + + private void skipLiteralSequences(final GenericToken currentToken, final Iterable remainingTokens) { + if (ignoreLiteralSequences) { + final int kind = currentToken.getKind(); + if (kind == CppParserConstants.LCURLYBRACE && isSequenceOfLiterals(remainingTokens)) { + discardingLiterals = true; + } else if (kind == CppParserConstants.RCURLYBRACE && discardingLiterals) { + discardingLiterals = false; + discardCurrent = true; + } + } + } + + private boolean isSequenceOfLiterals(final Iterable remainingTokens) { + boolean seenLiteral = false; + for (final GenericToken token : remainingTokens) { + switch (token.getKind()) { + case CppParserConstants.BINARY_INT_LITERAL: + case CppParserConstants.DECIMAL_INT_LITERAL: + case CppParserConstants.FLOAT_LITERAL: + case CppParserConstants.HEXADECIMAL_INT_LITERAL: + case CppParserConstants.OCTAL_INT_LITERAL: + case CppParserConstants.ZERO: + seenLiteral = true; + break; // can be skipped; continue to the next token + case CppParserConstants.COMMA: + break; // can be skipped; continue to the next token + case CppParserConstants.RCURLYBRACE: + // end of the list; skip all contents + return seenLiteral; + default: + // some other token than the expected ones; this is not a sequence of literals + return false; + } + } + return false; + } + + @Override + protected boolean isLanguageSpecificDiscarding() { + return discardingLiterals || discardCurrent; + } + } } diff --git a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java b/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java index 65338f07c9..899f7f3af8 100644 --- a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java +++ b/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java @@ -129,9 +129,18 @@ public class CPPTokenizerTest extends CpdTextComparisonTest { doTest("tabWidth"); } + @Test + public void testLongListsOfNumbersAreNotIgnored() { + doTest("listOfNumbers"); + } + + @Test + public void testLongListsOfNumbersAreIgnored() { + doTest("listOfNumbers", "_ignored", skipLiteralSequences()); + } private static Properties skipBlocks(String skipPattern) { - return properties(true, skipPattern); + return properties(true, skipPattern, false); } private static Properties skipBlocks() { @@ -139,15 +148,20 @@ public class CPPTokenizerTest extends CpdTextComparisonTest { } private static Properties dontSkipBlocks() { - return properties(false, null); + return properties(false, null, false); } - private static Properties properties(boolean skipBlocks, String skipPattern) { + private static Properties skipLiteralSequences() { + return properties(false, null, true); + } + + private static Properties properties(boolean skipBlocks, String skipPattern, boolean skipLiteralSequences) { Properties properties = new Properties(); properties.setProperty(Tokenizer.OPTION_SKIP_BLOCKS, Boolean.toString(skipBlocks)); if (skipPattern != null) { properties.setProperty(Tokenizer.OPTION_SKIP_BLOCKS_PATTERN, skipPattern); } + properties.setProperty(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES, Boolean.toString(skipLiteralSequences)); return properties; } } diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.cpp b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.cpp new file mode 100644 index 0000000000..0a527345d8 --- /dev/null +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.cpp @@ -0,0 +1,21 @@ +#include +int main() { + int a[50] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + double b[50] = { + 157, // decimal literal + 0377, // octal literal + 36'000'000, // literal with digit separators + 0x3fff, // hexadecimal literal + 0X3FFF, // same hexadecimal literal + 328u, // unsigned value + 0x7FFFFFL, // long value + 0776745ul, // unsigned long value + 18.46, // double with number after decimal point + 38., // double without number after decimal point + 18.46e0, // double with exponent + 18.46e1, // double with exponent + 0B001101, // C++ 14 binary literal + 0b000001, // C++ 14 binary literal + }; + return 0; +} diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.txt b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.txt new file mode 100644 index 0000000000..016efd8a92 --- /dev/null +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.txt @@ -0,0 +1,176 @@ + [Image] or [Truncated image[ Bcol Ecol +L2 + [int] 1 3 + [main] 5 8 + [(] 9 9 + [)] 10 10 + [{] 12 12 +L3 + [int] 3 5 + [a] 7 7 + [\[] 8 8 + [50] 9 10 + [\]] 11 11 + [=] 13 13 + [{] 15 15 + [0] 16 16 + [,] 17 17 + [0] 18 18 + [,] 19 19 + [0] 20 20 + [,] 21 21 + [0] 22 22 + [,] 23 23 + [0] 24 24 + [,] 25 25 + [0] 26 26 + [,] 27 27 + [0] 28 28 + [,] 29 29 + [0] 30 30 + [,] 31 31 + [0] 32 32 + [,] 33 33 + [0] 34 34 + [,] 35 35 + [0] 36 36 + [,] 37 37 + [0] 38 38 + [,] 39 39 + [0] 40 40 + [,] 41 41 + [0] 42 42 + [,] 43 43 + [0] 44 44 + [,] 45 45 + [0] 46 46 + [,] 47 47 + [0] 48 48 + [,] 49 49 + [0] 50 50 + [,] 51 51 + [0] 52 52 + [,] 53 53 + [0] 54 54 + [,] 55 55 + [0] 56 56 + [,] 57 57 + [0] 58 58 + [,] 59 59 + [0] 60 60 + [,] 61 61 + [0] 62 62 + [,] 63 63 + [0] 64 64 + [,] 65 65 + [0] 66 66 + [,] 67 67 + [0] 68 68 + [,] 69 69 + [0] 70 70 + [,] 71 71 + [0] 72 72 + [,] 73 73 + [0] 74 74 + [,] 75 75 + [0] 76 76 + [,] 77 77 + [0] 78 78 + [,] 79 79 + [0] 80 80 + [,] 81 81 + [0] 82 82 + [,] 83 83 + [0] 84 84 + [,] 85 85 + [0] 86 86 + [,] 87 87 + [0] 88 88 + [,] 89 89 + [0] 90 90 + [,] 91 91 + [0] 92 92 + [,] 93 93 + [0] 94 94 + [,] 95 95 + [0] 96 96 + [,] 97 97 + [0] 98 98 + [,] 99 99 + [0] 100 100 + [,] 101 101 + [0] 102 102 + [,] 103 103 + [0] 104 104 + [,] 105 105 + [0] 106 106 + [,] 107 107 + [0] 108 108 + [,] 109 109 + [0] 110 110 + [,] 111 111 + [0] 112 112 + [,] 113 113 + [0] 114 114 + [}] 115 115 + [;] 116 116 +L4 + [double] 3 8 + [b] 10 10 + [\[] 11 11 + [50] 12 13 + [\]] 14 14 + [=] 16 16 + [{] 18 18 +L5 + [157] 5 7 + [,] 8 8 +L6 + [0377] 5 8 + [,] 9 9 +L7 + [36'000'000] 5 14 + [,] 15 15 +L8 + [0x3fff] 5 10 + [,] 11 11 +L9 + [0X3FFF] 5 10 + [,] 11 11 +L10 + [328u] 5 8 + [,] 9 9 +L11 + [0x7FFFFFL] 5 13 + [,] 14 14 +L12 + [0776745ul] 5 13 + [,] 14 14 +L13 + [18.46] 5 9 + [,] 10 10 +L14 + [38.] 5 7 + [,] 8 8 +L15 + [18.46e0] 5 11 + [,] 12 12 +L16 + [18.46e1] 5 11 + [,] 12 12 +L17 + [0B001101] 5 12 + [,] 13 13 +L18 + [0b000001] 5 12 + [,] 13 13 +L19 + [}] 3 3 + [;] 4 4 +L20 + [return] 3 8 + [0] 10 10 + [;] 11 11 +L21 + [}] 1 1 +EOF diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers_ignored.txt b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers_ignored.txt new file mode 100644 index 0000000000..c6e7424b52 --- /dev/null +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers_ignored.txt @@ -0,0 +1,31 @@ + [Image] or [Truncated image[ Bcol Ecol +L2 + [int] 1 3 + [main] 5 8 + [(] 9 9 + [)] 10 10 + [{] 12 12 +L3 + [int] 3 5 + [a] 7 7 + [\[] 8 8 + [50] 9 10 + [\]] 11 11 + [=] 13 13 + [;] 116 116 +L4 + [double] 3 8 + [b] 10 10 + [\[] 11 11 + [50] 12 13 + [\]] 14 14 + [=] 16 16 +L19 + [;] 4 4 +L20 + [return] 3 8 + [0] 10 10 + [;] 11 11 +L21 + [}] 1 1 +EOF From 55598d844a66bf11135fc72bde3c6d6d4ce7c146 Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Wed, 9 Dec 2020 17:31:43 +0100 Subject: [PATCH 004/154] Filter out 2D arrays as well --- .../net/sourceforge/pmd/cpd/CPPTokenizer.java | 41 +++++-- .../lang/cpp/cpd/testdata/listOfNumbers.cpp | 7 +- .../lang/cpp/cpd/testdata/listOfNumbers.txt | 112 +++++++++++++++++- .../cpd/testdata/listOfNumbers_ignored.txt | 81 ++++++++++++- 4 files changed, 224 insertions(+), 17 deletions(-) diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java index 98f809ee57..e0d42d85bd 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java @@ -93,7 +93,7 @@ public class CPPTokenizer extends JavaCCTokenizer { private static class CppTokenFilter extends JavaCCTokenFilter { private final boolean ignoreLiteralSequences; - private boolean discardingLiterals = false; + private GenericToken discardingLiteralsUntil = null; private boolean discardCurrent = false; CppTokenFilter(final TokenManager tokenManager, final boolean ignoreLiteralSequences) { @@ -110,17 +110,21 @@ public class CPPTokenizer extends JavaCCTokenizer { private void skipLiteralSequences(final GenericToken currentToken, final Iterable remainingTokens) { if (ignoreLiteralSequences) { final int kind = currentToken.getKind(); - if (kind == CppParserConstants.LCURLYBRACE && isSequenceOfLiterals(remainingTokens)) { - discardingLiterals = true; - } else if (kind == CppParserConstants.RCURLYBRACE && discardingLiterals) { - discardingLiterals = false; - discardCurrent = true; + if (isDiscardingLiterals()) { + if (currentToken == discardingLiteralsUntil) { // NOPMD - intentional check for reference equality + discardingLiteralsUntil = null; + discardCurrent = true; + } + } else if (kind == CppParserConstants.LCURLYBRACE) { + final GenericToken finalToken = findEndOfSequenceOfLiterals(remainingTokens); + discardingLiteralsUntil = finalToken; } } } - private boolean isSequenceOfLiterals(final Iterable remainingTokens) { + private static GenericToken findEndOfSequenceOfLiterals(final Iterable remainingTokens) { boolean seenLiteral = false; + int braceCount = 0; for (final GenericToken token : remainingTokens) { switch (token.getKind()) { case CppParserConstants.BINARY_INT_LITERAL: @@ -133,20 +137,33 @@ public class CPPTokenizer extends JavaCCTokenizer { break; // can be skipped; continue to the next token case CppParserConstants.COMMA: break; // can be skipped; continue to the next token + case CppParserConstants.LCURLYBRACE: + braceCount++; + break; // curly braces are allowed, as long as they're balanced case CppParserConstants.RCURLYBRACE: - // end of the list; skip all contents - return seenLiteral; + braceCount--; + if (braceCount < 0) { + // end of the list; skip all contents + return seenLiteral ? token : null; + } else { + // curly braces are not yet balanced; continue to the next token + break; + } default: // some other token than the expected ones; this is not a sequence of literals - return false; + return null; } } - return false; + return null; + } + + private boolean isDiscardingLiterals() { + return discardingLiteralsUntil != null; } @Override protected boolean isLanguageSpecificDiscarding() { - return discardingLiterals || discardCurrent; + return isDiscardingLiterals() || discardCurrent; } } } diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.cpp b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.cpp index 0a527345d8..c9900ff83d 100644 --- a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.cpp +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.cpp @@ -1,7 +1,7 @@ #include int main() { int a[50] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - double b[50] = { + double b[14] = { 157, // decimal literal 0377, // octal literal 36'000'000, // literal with digit separators @@ -17,5 +17,10 @@ int main() { 0B001101, // C++ 14 binary literal 0b000001, // C++ 14 binary literal }; + int c[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}}; // multi-dimensional array + int d[3] = {a, a, a}; // identifiers should not be filtered out + int e[1][3] = {{a, a, a}}; // identifiers in multi-dimensional array + int f[1] = {main()}; // method invocations should not be filtered out + int g[1][1] = {{main()}}; // method invocation in multi-dimensional array return 0; } diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.txt b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.txt index 016efd8a92..c91cc9a9b6 100644 --- a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.txt +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers.txt @@ -118,7 +118,7 @@ L4 [double] 3 8 [b] 10 10 [\[] 11 11 - [50] 12 13 + [14] 12 13 [\]] 14 14 [=] 16 16 [{] 18 18 @@ -168,9 +168,117 @@ L19 [}] 3 3 [;] 4 4 L20 + [int] 3 5 + [c] 7 7 + [\[] 8 8 + [3] 9 9 + [\]] 10 10 + [\[] 11 11 + [4] 12 12 + [\]] 13 13 + [=] 15 15 + [{] 17 17 + [{] 18 18 + [0] 19 19 + [,] 20 20 + [1] 21 21 + [,] 22 22 + [2] 23 23 + [,] 24 24 + [3] 25 25 + [}] 26 26 + [,] 27 27 + [{] 28 28 + [4] 29 29 + [,] 30 30 + [5] 31 31 + [,] 32 32 + [6] 33 33 + [,] 34 34 + [7] 35 35 + [}] 36 36 + [,] 37 37 + [{] 38 38 + [8] 39 39 + [,] 40 40 + [9] 41 41 + [,] 42 42 + [10] 43 44 + [,] 45 45 + [11] 46 47 + [}] 48 48 + [}] 49 49 + [;] 50 50 +L21 + [int] 3 5 + [d] 7 7 + [\[] 8 8 + [3] 9 9 + [\]] 10 10 + [=] 12 12 + [{] 14 14 + [a] 15 15 + [,] 16 16 + [a] 18 18 + [,] 19 19 + [a] 21 21 + [}] 22 22 + [;] 23 23 +L22 + [int] 3 5 + [e] 7 7 + [\[] 8 8 + [1] 9 9 + [\]] 10 10 + [\[] 11 11 + [3] 12 12 + [\]] 13 13 + [=] 15 15 + [{] 17 17 + [{] 18 18 + [a] 19 19 + [,] 20 20 + [a] 22 22 + [,] 23 23 + [a] 25 25 + [}] 26 26 + [}] 27 27 + [;] 28 28 +L23 + [int] 3 5 + [f] 7 7 + [\[] 8 8 + [1] 9 9 + [\]] 10 10 + [=] 12 12 + [{] 14 14 + [main] 15 18 + [(] 19 19 + [)] 20 20 + [}] 21 21 + [;] 22 22 +L24 + [int] 3 5 + [g] 7 7 + [\[] 8 8 + [1] 9 9 + [\]] 10 10 + [\[] 11 11 + [1] 12 12 + [\]] 13 13 + [=] 15 15 + [{] 17 17 + [{] 18 18 + [main] 19 22 + [(] 23 23 + [)] 24 24 + [}] 25 25 + [}] 26 26 + [;] 27 27 +L25 [return] 3 8 [0] 10 10 [;] 11 11 -L21 +L26 [}] 1 1 EOF diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers_ignored.txt b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers_ignored.txt index c6e7424b52..503116ecf3 100644 --- a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers_ignored.txt +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/listOfNumbers_ignored.txt @@ -17,15 +17,92 @@ L4 [double] 3 8 [b] 10 10 [\[] 11 11 - [50] 12 13 + [14] 12 13 [\]] 14 14 [=] 16 16 L19 [;] 4 4 L20 + [int] 3 5 + [c] 7 7 + [\[] 8 8 + [3] 9 9 + [\]] 10 10 + [\[] 11 11 + [4] 12 12 + [\]] 13 13 + [=] 15 15 + [;] 50 50 +L21 + [int] 3 5 + [d] 7 7 + [\[] 8 8 + [3] 9 9 + [\]] 10 10 + [=] 12 12 + [{] 14 14 + [a] 15 15 + [,] 16 16 + [a] 18 18 + [,] 19 19 + [a] 21 21 + [}] 22 22 + [;] 23 23 +L22 + [int] 3 5 + [e] 7 7 + [\[] 8 8 + [1] 9 9 + [\]] 10 10 + [\[] 11 11 + [3] 12 12 + [\]] 13 13 + [=] 15 15 + [{] 17 17 + [{] 18 18 + [a] 19 19 + [,] 20 20 + [a] 22 22 + [,] 23 23 + [a] 25 25 + [}] 26 26 + [}] 27 27 + [;] 28 28 +L23 + [int] 3 5 + [f] 7 7 + [\[] 8 8 + [1] 9 9 + [\]] 10 10 + [=] 12 12 + [{] 14 14 + [main] 15 18 + [(] 19 19 + [)] 20 20 + [}] 21 21 + [;] 22 22 +L24 + [int] 3 5 + [g] 7 7 + [\[] 8 8 + [1] 9 9 + [\]] 10 10 + [\[] 11 11 + [1] 12 12 + [\]] 13 13 + [=] 15 15 + [{] 17 17 + [{] 18 18 + [main] 19 22 + [(] 23 23 + [)] 24 24 + [}] 25 25 + [}] 26 26 + [;] 27 27 +L25 [return] 3 8 [0] 10 10 [;] 11 11 -L21 +L26 [}] 1 1 EOF From 769cf5b316eac0668b28d1a7e99bbcc8c35429c9 Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Thu, 10 Dec 2020 13:21:48 +0100 Subject: [PATCH 005/154] Update C# grammar for additional C# 7 and C# 8 features --- .../pmd/lang/cs/antlr4/CSharpLexer.g4 | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/pmd-cs/src/main/antlr4/net/sourceforge/pmd/lang/cs/antlr4/CSharpLexer.g4 b/pmd-cs/src/main/antlr4/net/sourceforge/pmd/lang/cs/antlr4/CSharpLexer.g4 index f381392fa9..e1c213b93b 100644 --- a/pmd-cs/src/main/antlr4/net/sourceforge/pmd/lang/cs/antlr4/CSharpLexer.g4 +++ b/pmd-cs/src/main/antlr4/net/sourceforge/pmd/lang/cs/antlr4/CSharpLexer.g4 @@ -19,7 +19,8 @@ private boolean verbatium; BYTE_ORDER_MARK: '\u00EF\u00BB\u00BF'; SINGLE_LINE_DOC_COMMENT: '///' InputCharacter* -> channel(COMMENTS_CHANNEL); -DELIMITED_DOC_COMMENT: '/**' .*? '*/' -> channel(COMMENTS_CHANNEL); +EMPTY_DELIMITED_DOC_COMMENT: '/***/' -> channel(COMMENTS_CHANNEL); +DELIMITED_DOC_COMMENT: '/**' ~'/' .*? '*/' -> channel(COMMENTS_CHANNEL); SINGLE_LINE_COMMENT: '//' InputCharacter* -> channel(COMMENTS_CHANNEL); DELIMITED_COMMENT: '/*' .*? '*/' -> channel(COMMENTS_CHANNEL); @@ -119,6 +120,7 @@ TYPEOF: 'typeof'; UINT: 'uint'; ULONG: 'ulong'; UNCHECKED: 'unchecked'; +UNMANAGED: 'unmanaged'; UNSAFE: 'unsafe'; USHORT: 'ushort'; USING: 'using'; @@ -138,10 +140,11 @@ IDENTIFIER: '@'? IdentifierOrKeyword; //B.1.8 Literals // 0.Equals() would be parsed as an invalid real (1. branch) causing a lexer error -LITERAL_ACCESS: [0-9]+ IntegerTypeSuffix? '.' '@'? IdentifierOrKeyword; -INTEGER_LITERAL: [0-9]+ IntegerTypeSuffix?; -HEX_INTEGER_LITERAL: '0' [xX] HexDigit+ IntegerTypeSuffix?; -REAL_LITERAL: [0-9]* '.' [0-9]+ ExponentPart? [FfDdMm]? | [0-9]+ ([FfDdMm] | ExponentPart [FfDdMm]?); +LITERAL_ACCESS: [0-9] ('_'* [0-9])* IntegerTypeSuffix? '.' '@'? IdentifierOrKeyword; +INTEGER_LITERAL: [0-9] ('_'* [0-9])* IntegerTypeSuffix?; +HEX_INTEGER_LITERAL: '0' [xX] ('_'* HexDigit)+ IntegerTypeSuffix?; +BIN_INTEGER_LITERAL: '0' [bB] ('_'* [01])+ IntegerTypeSuffix?; +REAL_LITERAL: ([0-9] ('_'* [0-9])*)? '.' [0-9] ('_'* [0-9])* ExponentPart? [FfDdMm]? | [0-9] ('_'* [0-9])* ([FfDdMm] | ExponentPart [FfDdMm]?); CHARACTER_LITERAL: '\'' (~['\\\r\n\u0085\u2028\u2029] | CommonCharacter) '\''; REGULAR_STRING: '"' (~["\\\r\n\u0085\u2028\u2029] | CommonCharacter)* '"'; @@ -234,6 +237,8 @@ OP_OR_ASSIGNMENT: '|='; OP_XOR_ASSIGNMENT: '^='; OP_LEFT_SHIFT: '<<'; OP_LEFT_SHIFT_ASSIGNMENT: '<<='; +OP_COALESCING_ASSIGNMENT: '??='; +OP_RANGE: '..'; // https://msdn.microsoft.com/en-us/library/dn961160.aspx mode INTERPOLATION_STRING; @@ -271,6 +276,7 @@ WARNING: 'warning' Whitespace+ -> channel(DIREC REGION: 'region' Whitespace* -> channel(DIRECTIVE), mode(DIRECTIVE_TEXT); ENDREGION: 'endregion' Whitespace* -> channel(DIRECTIVE), mode(DIRECTIVE_TEXT); PRAGMA: 'pragma' Whitespace+ -> channel(DIRECTIVE), mode(DIRECTIVE_TEXT); +NULLABLE: 'nullable' Whitespace+ -> channel(DIRECTIVE), mode(DIRECTIVE_TEXT); DIRECTIVE_DEFAULT: 'default' -> channel(DIRECTIVE), type(DEFAULT); DIRECTIVE_HIDDEN: 'hidden' -> channel(DIRECTIVE); DIRECTIVE_OPEN_PARENS: '(' -> channel(DIRECTIVE), type(OPEN_PARENS); @@ -303,7 +309,7 @@ fragment NewLineCharacter ; fragment IntegerTypeSuffix: [lL]? [uU] | [uU]? [lL]; -fragment ExponentPart: [eE] ('+' | '-')? [0-9]+; +fragment ExponentPart: [eE] ('+' | '-')? [0-9] ('_'* [0-9])*; fragment CommonCharacter : SimpleEscapeSequence @@ -1102,4 +1108,4 @@ fragment UnicodeClassND | '\uaa50'..'\uaa59' | '\uabf0'..'\uabf9' | '\uff10'..'\uff19' - ; \ No newline at end of file + ; From 85c807ed1feeba9e6ffa8851ba951e307e434730 Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Thu, 10 Dec 2020 12:00:48 +0100 Subject: [PATCH 006/154] Add additional test cases for 2D arrays and more --- .../net/sourceforge/pmd/cpd/CsTokenizer.java | 42 +- .../pmd/lang/cs/cpd/testdata/listOfNumbers.cs | 18 +- .../lang/cs/cpd/testdata/listOfNumbers.txt | 693 ++++++++++-------- .../cs/cpd/testdata/listOfNumbers_ignored.txt | 25 + 4 files changed, 465 insertions(+), 313 deletions(-) diff --git a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java index 5ae96cbfa1..58cff112dc 100644 --- a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java +++ b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java @@ -63,7 +63,7 @@ public class CsTokenizer extends AntlrTokenizer { private final boolean ignoreLiteralSequences; private boolean discardingUsings = false; private boolean discardingNL = false; - private boolean discardingLiterals = false; + private AntlrToken discardingLiteralsUntil = null; private boolean discardCurrent = false; CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings, boolean ignoreLiteralSequences) { @@ -161,19 +161,24 @@ public class CsTokenizer extends AntlrTokenizer { private void skipLiteralSequences(final AntlrToken currentToken, final Iterable remainingTokens) { if (ignoreLiteralSequences) { final int type = currentToken.getKind(); - if (type == CSharpLexer.OPEN_BRACE && isSequenceOfLiterals(remainingTokens)) { - discardingLiterals = true; - } else if (type == CSharpLexer.CLOSE_BRACE && discardingLiterals) { - discardingLiterals = false; - discardCurrent = true; + if (isDiscardingLiterals()) { + if (currentToken == discardingLiteralsUntil) { // NOPMD - intentional check for reference equality + discardingLiteralsUntil = null; + discardCurrent = true; + } + } else if (type == CSharpLexer.OPEN_BRACE) { + final AntlrToken finalToken = findEndOfSequenceOfLiterals(remainingTokens); + discardingLiteralsUntil = finalToken; } } } - private boolean isSequenceOfLiterals(final Iterable remainingTokens) { + private AntlrToken findEndOfSequenceOfLiterals(final Iterable remainingTokens) { boolean seenLiteral = false; + int braceCount = 0; for (final AntlrToken token : remainingTokens) { switch (token.getKind()) { + case CSharpLexer.BIN_INTEGER_LITERAL: case CSharpLexer.CHARACTER_LITERAL: case CSharpLexer.HEX_INTEGER_LITERAL: case CSharpLexer.INTEGER_LITERAL: @@ -182,20 +187,33 @@ public class CsTokenizer extends AntlrTokenizer { break; // can be skipped; continue to the next token case CSharpLexer.COMMA: break; // can be skipped; continue to the next token + case CSharpLexer.OPEN_BRACE: + braceCount++; + break; // curly braces are allowed, as long as they're balanced case CSharpLexer.CLOSE_BRACE: - // end of the list; skip all contents - return seenLiteral; + braceCount--; + if (braceCount < 0) { + // end of the list; skip all contents + return seenLiteral ? token : null; + } else { + // curly braces are not yet balanced; continue to the next token + break; + } default: // some other token than the expected ones; this is not a sequence of literals - return false; + return null; } } - return false; + return null; + } + + public boolean isDiscardingLiterals() { + return discardingLiteralsUntil != null; } @Override protected boolean isLanguageSpecificDiscarding() { - return discardingUsings || discardingNL || discardingLiterals || discardCurrent; + return discardingUsings || discardingNL || isDiscardingLiterals() || discardCurrent; } } } diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.cs b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.cs index a3ceee1dca..6ebda7d3ff 100644 --- a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.cs +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.cs @@ -3,6 +3,22 @@ using System.Collections; using System.Collections.Generic; public class LongLists { List l = new List { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + }; + byte[,] a = {1,2,3,4,5}; + byte[,] b = {{1,2},{3,4},{5,6}}; + int[,] c = { + 157, // decimal literal + 0377, // octal literal + 36_000_000, // literal with digit separators + 0x3fff, // hexadecimal literal + 0X3FFF, // same hexadecimal literal + 328u, // unsigned value + 0x7FFFFFL, // long value + 0776745ul, // unsigned long value + 18.46, // float + 18.46e0, // double with exponent + 18.46e1, // double with exponent + 0b000001, // binary literal }; } diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.txt index 17d20f914d..765029a808 100644 --- a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.txt +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.txt @@ -36,309 +36,402 @@ L5 [>] 33 33 [{] 35 35 L6 - [0] 6 6 - [,] 7 7 - [0] 8 8 - [,] 9 9 - [0] 10 10 - [,] 11 11 - [0] 12 12 - [,] 13 13 - [0] 14 14 - [,] 15 15 - [0] 16 16 - [,] 17 17 - [0] 18 18 - [,] 19 19 - [0] 20 20 - [,] 21 21 - [0] 22 22 - [,] 23 23 - [0] 24 24 - [,] 25 25 - [0] 26 26 - [,] 27 27 - [0] 28 28 - [,] 29 29 - [0] 30 30 - [,] 31 31 - [0] 32 32 - [,] 33 33 - [0] 34 34 - [,] 35 35 - [0] 36 36 - [,] 37 37 - [0] 38 38 - [,] 39 39 - [0] 40 40 - [,] 41 41 - [0] 42 42 - [,] 43 43 - [0] 44 44 - [,] 45 45 - [0] 46 46 - [,] 47 47 - [0] 48 48 - [,] 49 49 - [0] 50 50 - [,] 51 51 - [0] 52 52 - [,] 53 53 - [0] 54 54 - [,] 55 55 - [0] 56 56 - [,] 57 57 - [0] 58 58 - [,] 59 59 - [0] 60 60 - [,] 61 61 - [0] 62 62 - [,] 63 63 - [0] 64 64 - [,] 65 65 - [0] 66 66 - [,] 67 67 - [0] 68 68 - [,] 69 69 - [0] 70 70 - [,] 71 71 - [0] 72 72 - [,] 73 73 - [0] 74 74 - [,] 75 75 - [0] 76 76 - [,] 77 77 - [0] 78 78 - [,] 79 79 - [0] 80 80 - [,] 81 81 - [0] 82 82 - [,] 83 83 - [0] 84 84 - [,] 85 85 - [0] 86 86 - [,] 87 87 - [0] 88 88 - [,] 89 89 - [0] 90 90 - [,] 91 91 - [0] 92 92 - [,] 93 93 - [0] 94 94 - [,] 95 95 - [0] 96 96 - [,] 97 97 - [0] 98 98 - [,] 99 99 - [0] 100 100 - [,] 101 101 - [0] 102 102 - [,] 103 103 - [0] 104 104 - [,] 105 105 - [0] 106 106 - [,] 107 107 - [0] 108 108 - [,] 109 109 - [0] 110 110 - [,] 111 111 - [0] 112 112 - [,] 113 113 - [0] 114 114 - [,] 115 115 - [0] 116 116 - [,] 117 117 - [0] 118 118 - [,] 119 119 - [0] 120 120 - [,] 121 121 - [0] 122 122 - [,] 123 123 - [0] 124 124 - [,] 125 125 - [0] 126 126 - [,] 127 127 - [0] 128 128 - [,] 129 129 - [0] 130 130 - [,] 131 131 - [0] 132 132 - [,] 133 133 - [0] 134 134 - [,] 135 135 - [0] 136 136 - [,] 137 137 - [0] 138 138 - [,] 139 139 - [0] 140 140 - [,] 141 141 - [0] 142 142 - [,] 143 143 - [0] 144 144 - [,] 145 145 - [0] 146 146 - [,] 147 147 - [0] 148 148 - [,] 149 149 - [0] 150 150 - [,] 151 151 - [0] 152 152 - [,] 153 153 - [0] 154 154 - [,] 155 155 - [0] 156 156 - [,] 157 157 - [0] 158 158 - [,] 159 159 - [0] 160 160 - [,] 161 161 - [0] 162 162 - [,] 163 163 - [0] 164 164 - [,] 165 165 - [0] 166 166 - [,] 167 167 - [0] 168 168 - [,] 169 169 - [0] 170 170 - [,] 171 171 - [0] 172 172 - [,] 173 173 - [0] 174 174 - [,] 175 175 - [0] 176 176 - [,] 177 177 - [0] 178 178 - [,] 179 179 - [0] 180 180 - [,] 181 181 - [0] 182 182 - [,] 183 183 - [0] 184 184 - [,] 185 185 - [0] 186 186 - [,] 187 187 - [0] 188 188 - [,] 189 189 - [0] 190 190 - [,] 191 191 - [0] 192 192 - [,] 193 193 - [0] 194 194 - [,] 195 195 - [0] 196 196 - [,] 197 197 - [0] 198 198 - [,] 199 199 - [0] 200 200 - [,] 201 201 - [0] 202 202 - [,] 203 203 - [0] 204 204 - [,] 205 205 - [0] 206 206 - [,] 207 207 - [0] 208 208 - [,] 209 209 - [0] 210 210 - [,] 211 211 - [0] 212 212 - [,] 213 213 - [0] 214 214 - [,] 215 215 - [0] 216 216 - [,] 217 217 - [0] 218 218 - [,] 219 219 - [0] 220 220 - [,] 221 221 - [0] 222 222 - [,] 223 223 - [0] 224 224 - [,] 225 225 - [0] 226 226 - [,] 227 227 - [0] 228 228 - [,] 229 229 - [0] 230 230 - [,] 231 231 - [0] 232 232 - [,] 233 233 - [0] 234 234 - [,] 235 235 - [0] 236 236 - [,] 237 237 - [0] 238 238 - [,] 239 239 - [0] 240 240 - [,] 241 241 - [0] 242 242 - [,] 243 243 - [0] 244 244 - [,] 245 245 - [0] 246 246 - [,] 247 247 - [0] 248 248 - [,] 249 249 - [0] 250 250 - [,] 251 251 - [0] 252 252 - [,] 253 253 - [0] 254 254 - [,] 255 255 - [0] 256 256 - [,] 257 257 - [0] 258 258 - [,] 259 259 - [0] 260 260 - [,] 261 261 - [0] 262 262 - [,] 263 263 - [0] 264 264 - [,] 265 265 - [0] 266 266 - [,] 267 267 - [0] 268 268 - [,] 269 269 - [0] 270 270 - [,] 271 271 - [0] 272 272 - [,] 273 273 - [0] 274 274 - [,] 275 275 - [0] 276 276 - [,] 277 277 - [0] 278 278 - [,] 279 279 - [0] 280 280 - [,] 281 281 - [0] 282 282 - [,] 283 283 - [0] 284 284 - [,] 285 285 - [0] 286 286 - [,] 287 287 - [0] 288 288 - [,] 289 289 - [0] 290 290 - [,] 291 291 - [0] 292 292 - [,] 293 293 - [0] 294 294 - [,] 295 295 - [0] 296 296 - [,] 297 297 - [0] 298 298 - [,] 299 299 - [0] 300 300 - [,] 301 301 - [0] 302 302 - [,] 303 303 - [0] 304 304 - [,] 305 305 + [0] 7 7 + [,] 8 8 + [0] 9 9 + [,] 10 10 + [0] 11 11 + [,] 12 12 + [0] 13 13 + [,] 14 14 + [0] 15 15 + [,] 16 16 + [0] 17 17 + [,] 18 18 + [0] 19 19 + [,] 20 20 + [0] 21 21 + [,] 22 22 + [0] 23 23 + [,] 24 24 + [0] 25 25 + [,] 26 26 + [0] 27 27 + [,] 28 28 + [0] 29 29 + [,] 30 30 + [0] 31 31 + [,] 32 32 + [0] 33 33 + [,] 34 34 + [0] 35 35 + [,] 36 36 + [0] 37 37 + [,] 38 38 + [0] 39 39 + [,] 40 40 + [0] 41 41 + [,] 42 42 + [0] 43 43 + [,] 44 44 + [0] 45 45 + [,] 46 46 + [0] 47 47 + [,] 48 48 + [0] 49 49 + [,] 50 50 + [0] 51 51 + [,] 52 52 + [0] 53 53 + [,] 54 54 + [0] 55 55 + [,] 56 56 + [0] 57 57 + [,] 58 58 + [0] 59 59 + [,] 60 60 + [0] 61 61 + [,] 62 62 + [0] 63 63 + [,] 64 64 + [0] 65 65 + [,] 66 66 + [0] 67 67 + [,] 68 68 + [0] 69 69 + [,] 70 70 + [0] 71 71 + [,] 72 72 + [0] 73 73 + [,] 74 74 + [0] 75 75 + [,] 76 76 + [0] 77 77 + [,] 78 78 + [0] 79 79 + [,] 80 80 + [0] 81 81 + [,] 82 82 + [0] 83 83 + [,] 84 84 + [0] 85 85 + [,] 86 86 + [0] 87 87 + [,] 88 88 + [0] 89 89 + [,] 90 90 + [0] 91 91 + [,] 92 92 + [0] 93 93 + [,] 94 94 + [0] 95 95 + [,] 96 96 + [0] 97 97 + [,] 98 98 + [0] 99 99 + [,] 100 100 + [0] 101 101 + [,] 102 102 + [0] 103 103 + [,] 104 104 + [0] 105 105 + [,] 106 106 + [0] 107 107 + [,] 108 108 + [0] 109 109 + [,] 110 110 + [0] 111 111 + [,] 112 112 + [0] 113 113 + [,] 114 114 + [0] 115 115 + [,] 116 116 + [0] 117 117 + [,] 118 118 + [0] 119 119 + [,] 120 120 + [0] 121 121 + [,] 122 122 + [0] 123 123 + [,] 124 124 + [0] 125 125 + [,] 126 126 + [0] 127 127 + [,] 128 128 + [0] 129 129 + [,] 130 130 + [0] 131 131 + [,] 132 132 + [0] 133 133 + [,] 134 134 + [0] 135 135 + [,] 136 136 + [0] 137 137 + [,] 138 138 + [0] 139 139 + [,] 140 140 + [0] 141 141 + [,] 142 142 + [0] 143 143 + [,] 144 144 + [0] 145 145 + [,] 146 146 + [0] 147 147 + [,] 148 148 + [0] 149 149 + [,] 150 150 + [0] 151 151 + [,] 152 152 + [0] 153 153 + [,] 154 154 + [0] 155 155 + [,] 156 156 + [0] 157 157 + [,] 158 158 + [0] 159 159 + [,] 160 160 + [0] 161 161 + [,] 162 162 + [0] 163 163 + [,] 164 164 + [0] 165 165 + [,] 166 166 + [0] 167 167 + [,] 168 168 + [0] 169 169 + [,] 170 170 + [0] 171 171 + [,] 172 172 + [0] 173 173 + [,] 174 174 + [0] 175 175 + [,] 176 176 + [0] 177 177 + [,] 178 178 + [0] 179 179 + [,] 180 180 + [0] 181 181 + [,] 182 182 + [0] 183 183 + [,] 184 184 + [0] 185 185 + [,] 186 186 + [0] 187 187 + [,] 188 188 + [0] 189 189 + [,] 190 190 + [0] 191 191 + [,] 192 192 + [0] 193 193 + [,] 194 194 + [0] 195 195 + [,] 196 196 + [0] 197 197 + [,] 198 198 + [0] 199 199 + [,] 200 200 + [0] 201 201 + [,] 202 202 + [0] 203 203 + [,] 204 204 + [0] 205 205 + [,] 206 206 + [0] 207 207 + [,] 208 208 + [0] 209 209 + [,] 210 210 + [0] 211 211 + [,] 212 212 + [0] 213 213 + [,] 214 214 + [0] 215 215 + [,] 216 216 + [0] 217 217 + [,] 218 218 + [0] 219 219 + [,] 220 220 + [0] 221 221 + [,] 222 222 + [0] 223 223 + [,] 224 224 + [0] 225 225 + [,] 226 226 + [0] 227 227 + [,] 228 228 + [0] 229 229 + [,] 230 230 + [0] 231 231 + [,] 232 232 + [0] 233 233 + [,] 234 234 + [0] 235 235 + [,] 236 236 + [0] 237 237 + [,] 238 238 + [0] 239 239 + [,] 240 240 + [0] 241 241 + [,] 242 242 + [0] 243 243 + [,] 244 244 + [0] 245 245 + [,] 246 246 + [0] 247 247 + [,] 248 248 + [0] 249 249 + [,] 250 250 + [0] 251 251 + [,] 252 252 + [0] 253 253 + [,] 254 254 + [0] 255 255 + [,] 256 256 + [0] 257 257 + [,] 258 258 + [0] 259 259 + [,] 260 260 + [0] 261 261 + [,] 262 262 + [0] 263 263 + [,] 264 264 + [0] 265 265 + [,] 266 266 + [0] 267 267 + [,] 268 268 + [0] 269 269 + [,] 270 270 + [0] 271 271 + [,] 272 272 + [0] 273 273 + [,] 274 274 + [0] 275 275 + [,] 276 276 + [0] 277 277 + [,] 278 278 + [0] 279 279 + [,] 280 280 + [0] 281 281 + [,] 282 282 + [0] 283 283 + [,] 284 284 + [0] 285 285 + [,] 286 286 + [0] 287 287 + [,] 288 288 + [0] 289 289 + [,] 290 290 + [0] 291 291 + [,] 292 292 + [0] 293 293 + [,] 294 294 + [0] 295 295 + [,] 296 296 + [0] 297 297 + [,] 298 298 + [0] 299 299 + [,] 300 300 + [0] 301 301 + [,] 302 302 + [0] 303 303 + [,] 304 304 + [0] 305 305 + [,] 306 306 L7 [}] 5 5 [;] 6 6 L8 + [byte] 5 8 + [\[] 9 9 + [,] 10 10 + [\]] 11 11 + [a] 13 13 + [=] 15 15 + [{] 17 17 + [1] 18 18 + [,] 19 19 + [2] 20 20 + [,] 21 21 + [3] 22 22 + [,] 23 23 + [4] 24 24 + [,] 25 25 + [5] 26 26 + [}] 27 27 + [;] 28 28 +L9 + [byte] 5 8 + [\[] 9 9 + [,] 10 10 + [\]] 11 11 + [b] 13 13 + [=] 15 15 + [{] 17 17 + [{] 18 18 + [1] 19 19 + [,] 20 20 + [2] 21 21 + [}] 22 22 + [,] 23 23 + [{] 24 24 + [3] 25 25 + [,] 26 26 + [4] 27 27 + [}] 28 28 + [,] 29 29 + [{] 30 30 + [5] 31 31 + [,] 32 32 + [6] 33 33 + [}] 34 34 + [}] 35 35 + [;] 36 36 +L10 + [int] 5 7 + [\[] 8 8 + [,] 9 9 + [\]] 10 10 + [c] 12 12 + [=] 14 14 + [{] 16 16 +L11 + [157] 7 9 + [,] 10 10 +L12 + [0377] 7 10 + [,] 11 11 +L13 + [36_000_000] 7 16 + [,] 17 17 +L14 + [0x3fff] 7 12 + [,] 13 13 +L15 + [0X3FFF] 7 12 + [,] 13 13 +L16 + [328u] 7 10 + [,] 11 11 +L17 + [0x7FFFFFL] 7 15 + [,] 16 16 +L18 + [0776745ul] 7 15 + [,] 16 16 +L19 + [18.46] 7 11 + [,] 12 12 +L20 + [18.46e0] 7 13 + [,] 14 14 +L21 + [18.46e1] 7 13 + [,] 14 14 +L22 + [0b000001] 7 14 + [,] 15 15 +L23 + [}] 5 5 + [;] 6 6 +L24 [}] 1 1 EOF diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers_ignored.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers_ignored.txt index 03f835fdca..79b782b67d 100644 --- a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers_ignored.txt +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers_ignored.txt @@ -37,5 +37,30 @@ L5 L7 [;] 6 6 L8 + [byte] 5 8 + [\[] 9 9 + [,] 10 10 + [\]] 11 11 + [a] 13 13 + [=] 15 15 + [;] 28 28 +L9 + [byte] 5 8 + [\[] 9 9 + [,] 10 10 + [\]] 11 11 + [b] 13 13 + [=] 15 15 + [;] 36 36 +L10 + [int] 5 7 + [\[] 8 8 + [,] 9 9 + [\]] 10 10 + [c] 12 12 + [=] 14 14 +L23 + [;] 6 6 +L24 [}] 1 1 EOF From cce0a35cb132ac3ab3050caeff8a94e385fb81e8 Mon Sep 17 00:00:00 2001 From: Mason Fox Date: Fri, 11 Dec 2020 18:53:53 -0500 Subject: [PATCH 007/154] [java] NPE in UseCollectionIsEmptyRule with enums #2833 --- .../UseCollectionIsEmptyRule.java | 10 +- .../xml/UseCollectionIsEmpty.xml | 105 ++++++++++++++---- 2 files changed, 88 insertions(+), 27 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java index 552d1116a7..b5acfc0195 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java @@ -14,6 +14,7 @@ import java.util.Map; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; +import net.sourceforge.pmd.lang.java.ast.ASTEnumBody; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; @@ -112,11 +113,14 @@ public class UseCollectionIsEmptyRule extends AbstractInefficientZeroCheck { } private ASTClassOrInterfaceType getTypeOfVariableByName(String varName, ASTPrimaryExpression expr) { - ASTClassOrInterfaceBody classBody = expr.getFirstParentOfType(ASTClassOrInterfaceBody.class); - List varDeclarators = classBody.findDescendantsOfType(ASTVariableDeclarator.class); + Node classOrEnumBody = expr.getFirstParentOfType(ASTClassOrInterfaceBody.class); + if (classOrEnumBody == null) { + classOrEnumBody = expr.getFirstParentOfType(ASTEnumBody.class); + } + List varDeclarators = classOrEnumBody.findDescendantsOfType(ASTVariableDeclarator.class); for (ASTVariableDeclarator varDeclarator : varDeclarators) { if (varDeclarator.getName().equals(varName)) { - return varDeclarator.getFirstDescendantOfType(ASTClassOrInterfaceType.class); + return varDeclarator.getParent().getFirstDescendantOfType(ASTClassOrInterfaceType.class); } } return null; diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml index d4f085f0df..d9d1f5002e 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml @@ -163,8 +163,8 @@ public class Foo { #1214 UseCollectionIsEmpty misses some usage - 5 - 25,28,31,34,37 + 10 + 8,11,14,17,20,23,26,29,32,35 testObject = new ArrayList(); - // These ones are flagged -// if (testObject.size() == 0) { -// System.out.println("List is empty"); -// } -// if (testObject.size() != 0) { -// System.out.println("List is empty"); -// } -// if (0 == testObject.size()) { -// System.out.println("List is empty"); -// } -// if (0 != testObject.size()) { -// System.out.println("List is empty"); -// } -// if (testObject.size() > 0) { -// System.out.println("List is empty"); -// } - - // These ones are not flagged, but should be flagged + // These should be flagged + if (testObject.size() == 0) { + System.out.println("List is empty"); + } + if (testObject.size() != 0) { + System.out.println("List is empty"); + } + if (0 == testObject.size()) { + System.out.println("List is empty"); + } + if (0 != testObject.size()) { + System.out.println("List is empty"); + } + if (testObject.size() > 0) { + System.out.println("List is empty"); + } if (testObject.size() < 1) { System.out.println("List is empty"); } @@ -232,7 +230,7 @@ public class IsEmptyTest { public static void main(String args[]) { ArrayList testObject = new ArrayList(); - // these should be flagged (as they are equivalent to == 0) and are + // These should be flagged if (testObject.size() < 1) { // line 8 System.out.println("List is empty"); } @@ -240,7 +238,7 @@ public class IsEmptyTest { System.out.println("List is empty"); } - // these should not be flagged, and are not + // These should not be flagged if (testObject.size() <= 1) { // line 16 System.out.println("List may or may not be empty"); } @@ -248,7 +246,7 @@ public class IsEmptyTest { System.out.println("List may or may not be empty"); } - // these should be flagged (as they are equivalent to != 0) and are not + // These should be flagged (as they are equivalent to != 0) and are not if (testObject.size() >= 1) { // line 24 System.out.println("List is not empty"); } @@ -256,7 +254,7 @@ public class IsEmptyTest { System.out.println("List is not empty"); } - // these should not be flagged, yet are + // These should not be flagged if (testObject.size() > 1) { // line 32 System.out.println("List is not empty, but not all non-empty lists will trigger this"); } @@ -348,4 +346,63 @@ public class Foo { } ]]> + + + + #2833 NPE in UseCollectionIsEmptyRule with enums + 0 + + + + + ##2833 NPE in UseCollectionIsEmptyRule with enums (sanity check) + 1 + 12 + list; + private String size; + + ComponentSize(String size) { + if (list.size() == 0) { + this.size = size; + } + } + + @Override + public String toString() { + return size; + } + +} + ]]> + + From 275e66d77a0f5f214fa4f159337f15e6a3429cb7 Mon Sep 17 00:00:00 2001 From: jr Date: Mon, 14 Dec 2020 14:36:51 -0800 Subject: [PATCH 008/154] [apex] New Rule: override equals and hashcode rule --- .../OverrideBothEqualsAndHashcodeRule.java | 66 +++++++ .../resources/category/apex/errorprone.xml | 34 ++++ .../OverrideBothEqualsAndHashcodeTest.java | 11 ++ .../xml/OverrideBothEqualsAndHashcode.xml | 166 ++++++++++++++++++ 4 files changed, 277 insertions(+) create mode 100644 pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java create mode 100644 pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java create mode 100644 pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java new file mode 100644 index 0000000000..5adf03c2b1 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java @@ -0,0 +1,66 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.rule.errorprone; + +import net.sourceforge.pmd.lang.apex.ast.ASTMethod; +import net.sourceforge.pmd.lang.apex.ast.ASTParameter; +import net.sourceforge.pmd.lang.apex.ast.ASTUserClass; +import net.sourceforge.pmd.lang.apex.ast.ApexNode; +import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule; + +public class OverrideBothEqualsAndHashcodeRule extends AbstractApexRule { + + @Override + public Object visit(ASTUserClass node, Object data) { + super.visit(node, data); + + ApexNode equalsNode = null; + ApexNode hashNode = null; + for (ASTMethod method : node.findChildrenOfType(ASTMethod.class)) { + if (equalsNode == null && isEquals(method)) { + equalsNode = method; + } + if (hashNode == null && isHashCode(method)) { + hashNode = method; + } + if (hashNode != null && equalsNode != null) { + break; + } + } + + if (equalsNode != null && hashNode == null) { + addViolation(data, equalsNode); + } else if (hashNode != null && equalsNode == null) { + addViolation(data, hashNode); + } + + return data; + } + + private Boolean isEquals(ASTMethod node) { + int numParams = 0; + String paramType = null; + for (int ix = 0; ix < node.getNumChildren(); ix++) { + ApexNode sn = node.getChild(ix); + if (sn instanceof ASTParameter) { + numParams++; + paramType = ((ASTParameter) sn).getType(); + } + } + return numParams == 1 && node.hasImageEqualTo("equals") && "Object".equalsIgnoreCase(paramType); + } + + private Boolean isHashCode(ASTMethod node) { + int numParams = 0; + for (int ix = 0; ix < node.getNumChildren(); ix++) { + ApexNode sn = node.getChild(ix); + if (sn instanceof ASTParameter) { + numParams++; + } + } + + return numParams == 0 && node.hasImageEqualTo("hashCode"); + } +} diff --git a/pmd-apex/src/main/resources/category/apex/errorprone.xml b/pmd-apex/src/main/resources/category/apex/errorprone.xml index 24d5483197..a9d56f5d04 100644 --- a/pmd-apex/src/main/resources/category/apex/errorprone.xml +++ b/pmd-apex/src/main/resources/category/apex/errorprone.xml @@ -291,6 +291,40 @@ public void bar(Integer a, Integer b) { // empty! } } +]]> + + + + + +Override both public boolean Object.equals(Object other), and public int Object.hashCode(), or override neither. Even if you are inheriting a hashCode() from a parent class, consider implementing hashCode and explicitly delegating to your superclass. + + 3 + + diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java new file mode 100644 index 0000000000..0b334a5f9f --- /dev/null +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java @@ -0,0 +1,11 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.rule.errorprone; + +import net.sourceforge.pmd.testframework.PmdRuleTst; + +public class OverrideBothEqualsAndHashcodeTest extends PmdRuleTst { + // no additional unit tests +} diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml new file mode 100644 index 0000000000..c5305e28e9 --- /dev/null +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml @@ -0,0 +1,166 @@ + + + + + hash code only + 1 + + + + + nested hash code only + 1 + + + + + equals only + 1 + + + + + nested equals only + 1 + + + + + overrides both + 0 + + + + + nested overrides both + 0 + + + + + overrides neither + 0 + + + + + equals sig uses String, not Object + 1 + + + + + interface + 0 + + + + + implements equals but with 2 args + 0 + + + + + overloaded hashCode + 0 + + + + + overloaded both + 0 + + + + + overloaded hashCode, should fail on equals + 1 + + + + + implements hashCode but with args + 0 + a) { + return 0; + } +} + ]]> + + From d0fb81de1f9eb616893f1c4b7f0bf63fc0982f1b Mon Sep 17 00:00:00 2001 From: Ozan Gulle Date: Wed, 16 Dec 2020 21:49:38 +0100 Subject: [PATCH 009/154] LiteralsFirstInComparisonsRule also checks for constants --- .../LiteralsFirstInComparisonsRule.java | 39 +++++++++++++++++-- .../xml/LiteralsFirstInComparisons.xml | 39 +++++++++++++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java index 3419dd8d65..2c425eb570 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java @@ -19,6 +19,10 @@ import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { @@ -123,14 +127,15 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { private boolean isStringLiteralFirstArgumentOfSuffix(ASTPrimarySuffix primarySuffix) { try { - JavaNode firstArg = getFirstArgument(primarySuffix); - return isStringLiteral(firstArg); + JavaNode firstLiteralArg = getFirstLiteralArgument(primarySuffix); + JavaNode firstNameArg = getFirstNameArgument(primarySuffix); + return isStringLiteral(firstLiteralArg) || isConstantString(firstNameArg); } catch (NullPointerException e) { return false; } } - private JavaNode getFirstArgument(ASTPrimarySuffix primarySuffix) { + private JavaNode getFirstLiteralArgument(ASTPrimarySuffix primarySuffix) { ASTArguments arguments = primarySuffix.getFirstChildOfType(ASTArguments.class); ASTArgumentList argumentList = arguments.getFirstChildOfType(ASTArgumentList.class); ASTExpression expression = argumentList.getFirstChildOfType(ASTExpression.class); @@ -139,6 +144,15 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { return primaryPrefix.getFirstChildOfType(ASTLiteral.class); } + private JavaNode getFirstNameArgument(ASTPrimarySuffix primarySuffix) { + ASTArguments arguments = primarySuffix.getFirstChildOfType(ASTArguments.class); + ASTArgumentList argumentList = arguments.getFirstChildOfType(ASTArgumentList.class); + ASTExpression expression = argumentList.getFirstChildOfType(ASTExpression.class); + ASTPrimaryExpression primaryExpression = expression.getFirstChildOfType(ASTPrimaryExpression.class); + ASTPrimaryPrefix primaryPrefix = primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); + return primaryPrefix.getFirstChildOfType(ASTName.class); + } + private boolean isStringLiteral(JavaNode node) { if (node instanceof ASTLiteral) { ASTLiteral literal = (ASTLiteral) node; @@ -147,6 +161,25 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { return false; } + private boolean isConstantString(JavaNode node) { + if (node instanceof ASTName) { + ASTName name = (ASTName) node; + ASTClassOrInterfaceBody classBody = name.getFirstParentOfType(ASTClassOrInterfaceBody.class); + ASTClassOrInterfaceBodyDeclaration classOrInterfaceBodyDeclaration = classBody.getFirstChildOfType(ASTClassOrInterfaceBodyDeclaration.class); + List fieldDeclarations = classOrInterfaceBodyDeclaration.findChildrenOfType(ASTFieldDeclaration.class); + for (ASTFieldDeclaration fieldDeclaration : fieldDeclarations) { + ASTVariableDeclarator declaration = fieldDeclaration.getFirstChildOfType(ASTVariableDeclarator.class); + if (declaration.getName().equals(name.getImage()) + && "class java.lang.String".equals(declaration.getType().toString()) + && fieldDeclaration.isFinal() + && fieldDeclaration.isStatic()) { + return true; + } + } + } + return false; + } + private boolean isNotWithinNullComparison(ASTPrimaryExpression node) { return !isWithinNullComparison(node); } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml index f76ba2658f..e1d2a48dbf 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml @@ -315,6 +315,45 @@ public class Foo { if (getStr("b").equals("ab")) { } // nok if ("ab".equals(getStr("b"))) { } // ok } +} + ]]> + + + + #575 PositionLiteralsFirstInComparisons to consider constant fields, i.e. static final Strings + 1 + + + + + #575 PositionLiteralsFirstInComparisons must not trigger if the field is not final + 0 + + + + + #575 PositionLiteralsFirstInComparisons must not trigger if the field is not static + 0 + From ae12a6db3542d6a491b3f750062b70b2a221668e Mon Sep 17 00:00:00 2001 From: Ozan Gulle Date: Wed, 16 Dec 2020 22:09:11 +0100 Subject: [PATCH 010/154] Put duplicated code in a separate method --- .../LiteralsFirstInComparisonsRule.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java index 2c425eb570..2c489a44fc 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java @@ -136,21 +136,19 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { } private JavaNode getFirstLiteralArgument(ASTPrimarySuffix primarySuffix) { - ASTArguments arguments = primarySuffix.getFirstChildOfType(ASTArguments.class); - ASTArgumentList argumentList = arguments.getFirstChildOfType(ASTArgumentList.class); - ASTExpression expression = argumentList.getFirstChildOfType(ASTExpression.class); - ASTPrimaryExpression primaryExpression = expression.getFirstChildOfType(ASTPrimaryExpression.class); - ASTPrimaryPrefix primaryPrefix = primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); - return primaryPrefix.getFirstChildOfType(ASTLiteral.class); + return getArgumentPrimaryPrefixFromSuffix(primarySuffix).getFirstChildOfType(ASTLiteral.class); } private JavaNode getFirstNameArgument(ASTPrimarySuffix primarySuffix) { + return getArgumentPrimaryPrefixFromSuffix(primarySuffix).getFirstChildOfType(ASTName.class); + } + + private JavaNode getArgumentPrimaryPrefixFromSuffix(ASTPrimarySuffix primarySuffix) { ASTArguments arguments = primarySuffix.getFirstChildOfType(ASTArguments.class); ASTArgumentList argumentList = arguments.getFirstChildOfType(ASTArgumentList.class); ASTExpression expression = argumentList.getFirstChildOfType(ASTExpression.class); ASTPrimaryExpression primaryExpression = expression.getFirstChildOfType(ASTPrimaryExpression.class); - ASTPrimaryPrefix primaryPrefix = primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); - return primaryPrefix.getFirstChildOfType(ASTName.class); + return primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); } private boolean isStringLiteral(JavaNode node) { From 35ee2a534e0d2a4db77013ff41552ca27890e7fc Mon Sep 17 00:00:00 2001 From: rmohan Date: Wed, 16 Dec 2020 13:37:14 -0800 Subject: [PATCH 011/154] Redoing HTML Style tag changes on updated Typecheck logic --- .../rule/security/VfHtmlStyleTagXssRule.java | 152 ++++++++++++ .../vf/rule/security/VfUnescapeElRule.java | 220 ++---------------- .../rule/security/lib/ElEscapeDetector.java | 168 +++++++++++++ .../main/resources/category/vf/security.xml | 23 ++ .../rule/security/VfHtmlStyleTagXssTest.java | 11 + ...HtmlXssStyleTagUrlPatternMatchingTest.java | 60 +++++ .../rule/security/xml/VfHtmlStyleTagXss.xml | 149 ++++++++++++ 7 files changed, 582 insertions(+), 201 deletions(-) create mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java create mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/lib/ElEscapeDetector.java create mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssTest.java create mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlXssStyleTagUrlPatternMatchingTest.java create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/xml/VfHtmlStyleTagXss.xml diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java new file mode 100644 index 0000000000..494a8f0e1b --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java @@ -0,0 +1,152 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.rule.security; + +import java.util.EnumSet; +import java.util.regex.Pattern; + +import net.sourceforge.pmd.lang.vf.ast.ASTContent; +import net.sourceforge.pmd.lang.vf.ast.ASTElExpression; +import net.sourceforge.pmd.lang.vf.ast.ASTElement; +import net.sourceforge.pmd.lang.vf.ast.ASTText; +import net.sourceforge.pmd.lang.vf.ast.VfNode; +import net.sourceforge.pmd.lang.vf.rule.AbstractVfRule; +import net.sourceforge.pmd.lang.vf.rule.security.lib.ElEscapeDetector; + + +public class VfHtmlStyleTagXssRule extends AbstractVfRule { + private static final String STYLE_TAG = "style"; + private static final String APEX_PREFIX = "apex:"; + + private final ElEscapeDetector escapeDetector = new ElEscapeDetector(); + + @Override + public Object visit(ASTElExpression node, Object data) { + final VfNode nodeParent = node.getParent(); + if (!(nodeParent instanceof ASTContent)) { + // nothing to do here. + // We care only if parent is available and is an ASTContent + return super.visit(node, data); + } + final ASTContent contentNode = (ASTContent) nodeParent; + + final VfNode nodeGrandParent = contentNode.getParent(); + if (!(nodeGrandParent instanceof ASTElement)) { + // nothing to do here. + // We care only if grandparent is available and is an ASTElement + return super.visit(node, data); + } + final ASTElement elementNode = (ASTElement) nodeGrandParent; + + // make sure elementNode does not have an "apex:" prefix + if (isApexPrefixed(elementNode)) { + // nothing to do here. + // This rule does not deal with inbuilt-visualforce tags + return super.visit(node, data); + } + + verifyEncoding(node, contentNode, elementNode, data); + + return super.visit(node, data); + } + + private void verifyEncoding( + ASTElExpression node, + ASTContent contentNode, + ASTElement elementNode, + Object data) { + final String previousText = getPreviousText(contentNode, node); + + if (isStyleTag(elementNode)) { + verifyStyleEncoding(node, previousText, data); + } else { + // El expression in any other plain HTML tag should be encoded + verifyGeneralHtmlEncoding(node, data); + } + } + + private boolean isStyleTag(ASTElement elementNode) { + // are we dealing with HTML tag? + return STYLE_TAG.equalsIgnoreCase(elementNode.getLocalName()); + } + + private void verifyStyleEncoding(ASTElExpression elExpressionNode, String previousText, Object data) { + final boolean isWithinSafeResource = escapeDetector.startsWithSafeResource(elExpressionNode); + // check if we are within a URL expression + if (isWithinUrlMethod(previousText)) { + verifyEncodingWithinUrl(elExpressionNode, isWithinSafeResource, data); + } else { + verifyEncodingWithoutUrl(elExpressionNode, isWithinSafeResource, data); + } + } + + private void verifyEncodingWithinUrl(ASTElExpression elExpressionNode, boolean isWithinSafeResource, Object data) { + + // only allow URLENCODING or JSINHTMLENCODING + if (escapeDetector.doesElContainAnyUnescapedIdentifiers( + elExpressionNode, + EnumSet.of(ElEscapeDetector.Escaping.URLENCODE, ElEscapeDetector.Escaping.JSINHTMLENCODE)) + && !isWithinSafeResource) { + addViolationWithMessage( + data, + elExpressionNode, + "Dynamic EL content within URL in style tag should be URLENCODED or JSINHTMLENCODED as appropriate"); + } + + } + + private void verifyEncodingWithoutUrl(ASTElExpression elExpressionNode, boolean isWithinSafeResource, Object data) { + if (escapeDetector.doesElContainAnyUnescapedIdentifiers( + elExpressionNode, + EnumSet.of(ElEscapeDetector.Escaping.ANY)) + && !isWithinSafeResource) { + addViolationWithMessage( + data, + elExpressionNode, + "Dynamic EL content in style tag should be appropriately encoded"); + } + } + + private void verifyGeneralHtmlEncoding(ASTElExpression elExpressionNode, Object data) { + if (escapeDetector.doesElContainAnyUnescapedIdentifiers( + elExpressionNode, + ElEscapeDetector.Escaping.HTMLENCODE) + && !escapeDetector.startsWithSafeResource(elExpressionNode)) { + addViolationWithMessage( + data, + elExpressionNode, + "Dynamic content in plain HTML tags should be HTMLENCODED"); + } + } + + private boolean isApexPrefixed(ASTElement node) { + return node.isHasNamespacePrefix() + && APEX_PREFIX.equalsIgnoreCase(node.getNamespacePrefix()); + } + + private String getPreviousText(ASTContent content, ASTElExpression elExpressionNode) { + final int indexInParent = elExpressionNode.getIndexInParent(); + final VfNode previous = indexInParent > 0 ? content.getChild(indexInParent - 1) : null; + return previous instanceof ASTText ? previous.getImage() : ""; + } + + // visible for unit testing + boolean isWithinUrlMethod(String previousText) { + // match for a pattern that + // 1. contains "url" (case insensitive), + // 2. followed by any number of whitespaces, + // 3. a starting bracket "(" + // 4. and anything else but an ending bracket ")" + // For example: + // Matches: "div { background: url('", "div { background: Url ( blah" + // Does not match: "div { background: url('myUrl')", "div { background: myStyle('" + + final String urlMethodPattern = "url\\s*\\([^)]*$"; + return Pattern.compile(urlMethodPattern, Pattern.CASE_INSENSITIVE) + .matcher(previousText) + .find(); + } + +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java index 51817b896c..0a537f92bf 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java @@ -12,20 +12,17 @@ import java.util.Set; import java.util.regex.Pattern; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.vf.ast.ASTArguments; import net.sourceforge.pmd.lang.vf.ast.ASTAttribute; import net.sourceforge.pmd.lang.vf.ast.ASTContent; -import net.sourceforge.pmd.lang.vf.ast.ASTDotExpression; import net.sourceforge.pmd.lang.vf.ast.ASTElExpression; import net.sourceforge.pmd.lang.vf.ast.ASTElement; import net.sourceforge.pmd.lang.vf.ast.ASTExpression; import net.sourceforge.pmd.lang.vf.ast.ASTHtmlScript; -import net.sourceforge.pmd.lang.vf.ast.ASTIdentifier; import net.sourceforge.pmd.lang.vf.ast.ASTLiteral; -import net.sourceforge.pmd.lang.vf.ast.ASTNegationExpression; import net.sourceforge.pmd.lang.vf.ast.ASTText; -import net.sourceforge.pmd.lang.vf.ast.AbstractVFNode; import net.sourceforge.pmd.lang.vf.rule.AbstractVfRule; +import net.sourceforge.pmd.lang.vf.rule.security.lib.ElEscapeDetector; + /** * @author sergey.gorbaty February 2017 @@ -50,6 +47,8 @@ public class VfUnescapeElRule extends AbstractVfRule { private static final Pattern ON_EVENT = Pattern.compile("^on(\\w)+$"); private static final Pattern PLACEHOLDERS = Pattern.compile("\\{(\\w|,|\\.|'|:|\\s)*\\}"); + private ElEscapeDetector escapeDetector = new ElEscapeDetector(); + @Override public Object visit(ASTHtmlScript node, Object data) { checkIfCorrectlyEscaped(node, data); @@ -87,16 +86,16 @@ public class VfUnescapeElRule extends AbstractVfRule { } if (quoted) { // check escaping too - if (!(jsonParse || startsWithSafeResource(elExpression) || containsSafeFields(elExpression))) { - if (doesElContainAnyUnescapedIdentifiers(elExpression, - EnumSet.of(Escaping.JSENCODE, Escaping.JSINHTMLENCODE))) { + if (!(jsonParse || escapeDetector.startsWithSafeResource(elExpression) || escapeDetector.containsSafeFields(elExpression))) { + if (escapeDetector.doesElContainAnyUnescapedIdentifiers(elExpression, + EnumSet.of(ElEscapeDetector.Escaping.JSENCODE, ElEscapeDetector.Escaping.JSINHTMLENCODE))) { addViolation(data, elExpression); } } } else { - if (!(startsWithSafeResource(elExpression) || containsSafeFields(elExpression))) { - final boolean hasUnscaped = doesElContainAnyUnescapedIdentifiers(elExpression, - EnumSet.of(Escaping.JSENCODE, Escaping.JSINHTMLENCODE)); + if (!(escapeDetector.startsWithSafeResource(elExpression) || escapeDetector.containsSafeFields(elExpression))) { + final boolean hasUnscaped = escapeDetector.doesElContainAnyUnescapedIdentifiers(elExpression, + EnumSet.of(ElEscapeDetector.Escaping.JSENCODE, ElEscapeDetector.Escaping.JSINHTMLENCODE)); if (!(jsonParse && !hasUnscaped)) { addViolation(data, elExpression); } @@ -180,11 +179,11 @@ public class VfUnescapeElRule extends AbstractVfRule { break; } - if (startsWithSafeResource(el)) { + if (escapeDetector.startsWithSafeResource(el)) { break; } - if (doesElContainAnyUnescapedIdentifiers(el, Escaping.URLENCODE)) { + if (escapeDetector.doesElContainAnyUnescapedIdentifiers(el, ElEscapeDetector.Escaping.URLENCODE)) { isEL = true; toReport.add(el); } @@ -215,12 +214,12 @@ public class VfUnescapeElRule extends AbstractVfRule { if (ON_EVENT.matcher(name).matches()) { final List elsInVal = attr.findDescendantsOfType(ASTElExpression.class); for (ASTElExpression el : elsInVal) { - if (startsWithSafeResource(el)) { + if (escapeDetector.startsWithSafeResource(el)) { continue; } - if (doesElContainAnyUnescapedIdentifiers(el, - EnumSet.of(Escaping.ANY))) { + if (escapeDetector.doesElContainAnyUnescapedIdentifiers(el, + EnumSet.of(ElEscapeDetector.Escaping.ANY))) { isEL = true; toReport.add(el); } @@ -237,78 +236,6 @@ public class VfUnescapeElRule extends AbstractVfRule { } - private boolean startsWithSafeResource(final ASTElExpression el) { - final ASTExpression expression = el.getFirstChildOfType(ASTExpression.class); - if (expression != null) { - final ASTNegationExpression negation = expression.getFirstChildOfType(ASTNegationExpression.class); - if (negation != null) { - return true; - } - - final ASTIdentifier id = expression.getFirstChildOfType(ASTIdentifier.class); - if (id != null) { - String lowerCaseId = id.getImage().toLowerCase(Locale.ROOT); - List args = expression.findChildrenOfType(ASTArguments.class); - if (!args.isEmpty()) { - switch (lowerCaseId) { - case "urlfor": - case "casesafeid": - case "begins": - case "contains": - case "len": - case "getrecordids": - case "linkto": - case "sqrt": - case "round": - case "mod": - case "log": - case "ln": - case "exp": - case "abs": - case "floor": - case "ceiling": - case "nullvalue": - case "isnumber": - case "isnull": - case "isnew": - case "isblank": - case "isclone": - case "year": - case "month": - case "day": - case "datetimevalue": - case "datevalue": - case "date": - case "now": - case "today": - return true; - - default: - } - } else { - // has no arguments - switch (lowerCaseId) { - case "$action": - case "$page": - case "$site": - case "$resource": - case "$label": - case "$objecttype": - case "$component": - case "$remoteaction": - case "$messagechannel": - return true; - - default: - } - } - } - - } - - return false; - } - private boolean startsWithSlashLiteral(final ASTElExpression elExpression) { final ASTExpression expression = elExpression.getFirstChildOfType(ASTExpression.class); if (expression != null) { @@ -351,11 +278,11 @@ public class VfUnescapeElRule extends AbstractVfRule { final List elsInVal = attr.findDescendantsOfType(ASTElExpression.class); for (ASTElExpression el : elsInVal) { - if (startsWithSafeResource(el)) { + if (escapeDetector.startsWithSafeResource(el)) { continue; } - if (doesElContainAnyUnescapedIdentifiers(el, Escaping.HTMLENCODE)) { + if (escapeDetector.doesElContainAnyUnescapedIdentifiers(el, ElEscapeDetector.Escaping.HTMLENCODE)) { isEL = true; toReport.add(el); } @@ -389,96 +316,6 @@ public class VfUnescapeElRule extends AbstractVfRule { } } - private boolean doesElContainAnyUnescapedIdentifiers(final ASTElExpression elExpression, Escaping escape) { - return doesElContainAnyUnescapedIdentifiers(elExpression, EnumSet.of(escape)); - - } - - private boolean doesElContainAnyUnescapedIdentifiers(final ASTElExpression elExpression, - EnumSet escapes) { - if (elExpression == null) { - return false; - } - - final Set nonEscapedIds = new HashSet<>(); - - final List exprs = elExpression.findChildrenOfType(ASTExpression.class); - for (final ASTExpression expr : exprs) { - - if (innerContainsSafeFields(expr)) { - continue; - } - - final List ids = expr.findChildrenOfType(ASTIdentifier.class); - - for (final ASTIdentifier id : ids) { - boolean isEscaped = false; - - for (Escaping e : escapes) { - - if (id.getImage().equalsIgnoreCase(e.toString())) { - isEscaped = true; - break; - } - - if (e.equals(Escaping.ANY)) { - for (Escaping esc : Escaping.values()) { - if (id.getImage().equalsIgnoreCase(esc.toString())) { - isEscaped = true; - break; - } - } - } - - } - - if (!isEscaped) { - nonEscapedIds.add(id); - } - } - - } - - return !nonEscapedIds.isEmpty(); - } - - private boolean containsSafeFields(final AbstractVFNode expression) { - final ASTExpression ex = expression.getFirstChildOfType(ASTExpression.class); - - return ex != null && innerContainsSafeFields(ex); - - } - - private boolean innerContainsSafeFields(final AbstractVFNode expression) { - for (int i = 0; i < expression.getNumChildren(); i++) { - Node child = expression.getChild(i); - - if (child instanceof ASTIdentifier) { - switch (child.getImage().toLowerCase(Locale.ROOT)) { - case "id": - case "size": - case "caseNumber": - return true; - default: - } - } - - if (child instanceof ASTArguments) { - if (containsSafeFields((ASTArguments) child)) { - return true; - } - } - - if (child instanceof ASTDotExpression) { - if (innerContainsSafeFields((ASTDotExpression) child)) { - return true; - } - } - - } - - return false; - } private boolean doesTagSupportEscaping(final ASTElement node) { if (node.getName() == null) { @@ -508,11 +345,11 @@ public class VfUnescapeElRule extends AbstractVfRule { for (ASTAttribute attrib : innerAttributes) { final List elsInVal = attrib.findDescendantsOfType(ASTElExpression.class); for (final ASTElExpression el : elsInVal) { - if (startsWithSafeResource(el)) { + if (escapeDetector.startsWithSafeResource(el)) { continue; } - if (doesElContainAnyUnescapedIdentifiers(el, Escaping.HTMLENCODE)) { + if (escapeDetector.doesElContainAnyUnescapedIdentifiers(el, ElEscapeDetector.Escaping.HTMLENCODE)) { toReturn.add(el); } @@ -525,23 +362,4 @@ public class VfUnescapeElRule extends AbstractVfRule { return toReturn; } - enum Escaping { - HTMLENCODE("HTMLENCODE"), - URLENCODE("URLENCODE"), - JSINHTMLENCODE("JSINHTMLENCODE"), - JSENCODE("JSENCODE"), - ANY("ANY"); - - private final String text; - - Escaping(final String text) { - this.text = text; - } - - @Override - public String toString() { - return text; - } - } - } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/lib/ElEscapeDetector.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/lib/ElEscapeDetector.java new file mode 100644 index 0000000000..5ec6de729b --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/lib/ElEscapeDetector.java @@ -0,0 +1,168 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.rule.security.lib; + +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.vf.ast.ASTArguments; +import net.sourceforge.pmd.lang.vf.ast.ASTDotExpression; +import net.sourceforge.pmd.lang.vf.ast.ASTElExpression; +import net.sourceforge.pmd.lang.vf.ast.ASTExpression; +import net.sourceforge.pmd.lang.vf.ast.ASTIdentifier; +import net.sourceforge.pmd.lang.vf.ast.ASTNegationExpression; +import net.sourceforge.pmd.lang.vf.ast.AbstractVFNode; + +/** + * Helps detect visualforce encoding in EL Expressions + * (porting over code previously living in VfUnescapeElRule for reusability) + */ + +public class ElEscapeDetector { + + private static final List FUNCTIONS_WITH_ARGS = Arrays.asList( + "urlfor", "casesafeid", "begins", "contains", "len", "getrecordids", "linkto", + "sqrt", "round", "mod", "log", "ln", "exp", "abs", "floor", "ceiling", + "nullvalue", "isnumber", "isnull", "isnew", "isblank", "isclone", + "year", "month", "day", "datetimevalue", "datevalue", "date", "now", "today"); + + private static final List FUNCTIONS_NO_ARGS = Arrays.asList( + "$action", "$page", "$site", "$resource", "$label", "$objecttype", + "$component", "$remoteaction", "$messagechannel"); + + public boolean innerContainsSafeFields(final AbstractVFNode expression) { + for (int i = 0; i < expression.getNumChildren(); i++) { + Node child = expression.getChild(i); + + if (child instanceof ASTIdentifier) { + switch (child.getImage().toLowerCase(Locale.ROOT)) { + case "id": + case "size": + case "caseNumber": + return true; + default: + } + } + + if (child instanceof ASTArguments) { + if (containsSafeFields((ASTArguments) child)) { + return true; + } + } + + if (child instanceof ASTDotExpression) { + if (innerContainsSafeFields((ASTDotExpression) child)) { + return true; + } + } + + } + + return false; + } + + public boolean containsSafeFields(final AbstractVFNode expression) { + final ASTExpression ex = expression.getFirstChildOfType(ASTExpression.class); + + return ex != null && innerContainsSafeFields(ex); + + } + + public boolean startsWithSafeResource(final ASTElExpression el) { + final ASTExpression expression = el.getFirstChildOfType(ASTExpression.class); + if (expression != null) { + final ASTNegationExpression negation = expression.getFirstChildOfType(ASTNegationExpression.class); + if (negation != null) { + return true; + } + + final ASTIdentifier id = expression.getFirstChildOfType(ASTIdentifier.class); + if (id != null) { + String lowerCaseId = id.getImage().toLowerCase(Locale.ROOT); + List args = expression.findChildrenOfType(ASTArguments.class); + + return args.isEmpty() ? FUNCTIONS_NO_ARGS.contains(lowerCaseId) : FUNCTIONS_WITH_ARGS.contains(lowerCaseId); + } + } + return false; + } + + public boolean doesElContainAnyUnescapedIdentifiers(final ASTElExpression elExpression, Escaping escape) { + return doesElContainAnyUnescapedIdentifiers(elExpression, EnumSet.of(escape)); + + } + + public boolean doesElContainAnyUnescapedIdentifiers(final ASTElExpression elExpression, + EnumSet escapes) { + if (elExpression == null) { + return false; + } + + final Set nonEscapedIds = new HashSet<>(); + + final List exprs = elExpression.findChildrenOfType(ASTExpression.class); + for (final ASTExpression expr : exprs) { + + if (innerContainsSafeFields(expr)) { + continue; + } + + final List ids = expr.findChildrenOfType(ASTIdentifier.class); + + for (final ASTIdentifier id : ids) { + boolean isEscaped = false; + + for (Escaping e : escapes) { + + if (id.getImage().equalsIgnoreCase(e.toString())) { + isEscaped = true; + break; + } + + if (e.equals(Escaping.ANY)) { + for (Escaping esc : Escaping.values()) { + if (id.getImage().equalsIgnoreCase(esc.toString())) { + isEscaped = true; + break; + } + } + } + + } + + if (!isEscaped) { + nonEscapedIds.add(id); + } + } + + } + + return !nonEscapedIds.isEmpty(); + } + + public enum Escaping { + HTMLENCODE("HTMLENCODE"), + URLENCODE("URLENCODE"), + JSINHTMLENCODE("JSINHTMLENCODE"), + JSENCODE("JSENCODE"), + ANY("ANY"); + + private final String text; + + Escaping(final String text) { + this.text = text; + } + + @Override + public String toString() { + return text; + } + } +} diff --git a/pmd-visualforce/src/main/resources/category/vf/security.xml b/pmd-visualforce/src/main/resources/category/vf/security.xml index fcb7c49a37..29b414402d 100644 --- a/pmd-visualforce/src/main/resources/category/vf/security.xml +++ b/pmd-visualforce/src/main/resources/category/vf/security.xml @@ -26,6 +26,29 @@ Avoid calling VF action upon page load as the action becomes vulnerable to CSRF. + + + Use relevant encoding with EL in html tags + + 3 + + + + +]]> + + + + + + + sObject field assigned to regular html + 1 + + ${!XSSHere} + + ]]> + + + + sObject field assigned to non-style html + 1 + +
+ ${!XSSHere} +
+ + ]]>
+
+ + + sObject field (from controller) assigned to url variable in style tag (html encoded) + 1 + + + + ]]> + + + + sObject field (from controller) assigned to url variable in style tag (JS encoded) + 1 + + + + ]]> + + + + sObject field (from controller) assigned to url variable in style tag (JSINHTML encoded) + 0 + + + + ]]> + + + + sObject field (from controller) assigned to url variable in style tag (URL encoded) + 0 + + + + ]]> + + + + sObject field (from controller) assigned to url variable in style tag + 1 + + + + ]]> + + + + sObject field (from controller) assigned to variable in style tag + 1 + + + + ]]> + + + + sObject field (from controller) assigned to variable in style tag with some encoding + 0 + + + + ]]> + + + + EL assigned to url variable in style tag within a safe resource + 0 + + + + ]]> + + + + EL assigned to plain html within safe resource + 0 + +
+ {!URLFOR($Resource.valid)} +
+ + ]]>
+
+ +
\ No newline at end of file From 129790ddfc6270c3592a51fae9dc0da0de001bcc Mon Sep 17 00:00:00 2001 From: Ozan Gulle Date: Wed, 16 Dec 2020 22:44:33 +0100 Subject: [PATCH 012/154] Fixed the order of imports --- .../bestpractices/LiteralsFirstInComparisonsRule.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java index 2c489a44fc..83200e98d6 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java @@ -8,21 +8,21 @@ import java.util.List; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; import net.sourceforge.pmd.lang.java.ast.ASTArguments; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression; import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression; import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTLiteral; import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; +import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { From 94a3eb636a5d7c888b2f8e68973a1340e8775c1a Mon Sep 17 00:00:00 2001 From: rmohan Date: Wed, 16 Dec 2020 14:28:35 -0800 Subject: [PATCH 013/154] Changes to fix apex namespace bug --- .../rule/security/VfHtmlStyleTagXssRule.java | 2 +- .../rule/security/xml/VfHtmlStyleTagXss.xml | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java index 494a8f0e1b..3239a2167e 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java @@ -18,7 +18,7 @@ import net.sourceforge.pmd.lang.vf.rule.security.lib.ElEscapeDetector; public class VfHtmlStyleTagXssRule extends AbstractVfRule { private static final String STYLE_TAG = "style"; - private static final String APEX_PREFIX = "apex:"; + private static final String APEX_PREFIX = "apex"; private final ElEscapeDetector escapeDetector = new ElEscapeDetector(); diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/xml/VfHtmlStyleTagXss.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/xml/VfHtmlStyleTagXss.xml index c22a0b778a..0b31b943ec 100644 --- a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/xml/VfHtmlStyleTagXss.xml +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/xml/VfHtmlStyleTagXss.xml @@ -26,6 +26,30 @@ ]]> + + sObject field assigned to apex tag value + 0 + + + ${!XSSHere} + + + ]]> + + + + sObject field assigned to apex tag attribute + 0 + + + some output content + + + ]]> + + sObject field (from controller) assigned to url variable in style tag (html encoded) 1 From c37f208d3e9740825ddb8c91779499eb39e52bec Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 17 Dec 2020 18:31:13 +0100 Subject: [PATCH 014/154] Call jjtree/javacc with UTF-8 as default encoding Fixes #2970 --- pmd-core/src/main/ant/alljavacc.xml | 22 ++++++++++----- pmd-cpp/src/main/ant/alljavacc.xml | 14 +++++---- pmd-java/src/main/ant/alljavacc.xml | 24 ++++++++++------ pmd-javascript/src/main/ant/alljavacc.xml | 14 +++++---- pmd-jsp/src/main/ant/alljavacc.xml | 28 +++++++++++------- pmd-matlab/src/main/ant/alljavacc.xml | 14 +++++---- pmd-modelica/src/main/ant/alljavacc.xml | 24 ++++++++++------ pmd-objectivec/src/main/ant/alljavacc.xml | 14 +++++---- pmd-plsql/src/main/ant/alljavacc.xml | 24 ++++++++++------ pmd-python/src/main/ant/alljavacc.xml | 14 +++++---- pmd-visualforce/src/main/ant/alljavacc.xml | 33 ++++++++++++++-------- pmd-vm/src/main/ant/alljavacc.xml | 28 +++++++++++------- 12 files changed, 165 insertions(+), 88 deletions(-) diff --git a/pmd-core/src/main/ant/alljavacc.xml b/pmd-core/src/main/ant/alljavacc.xml index 2940d3ee58..ff3cb30801 100644 --- a/pmd-core/src/main/ant/alljavacc.xml +++ b/pmd-core/src/main/ant/alljavacc.xml @@ -30,15 +30,23 @@ Using JavaCC home: ${javacc-home.path} - + + + + + - + + + + + + diff --git a/pmd-cpp/src/main/ant/alljavacc.xml b/pmd-cpp/src/main/ant/alljavacc.xml index 800af4264b..c5afa6a939 100644 --- a/pmd-cpp/src/main/ant/alljavacc.xml +++ b/pmd-cpp/src/main/ant/alljavacc.xml @@ -29,11 +29,15 @@ - + + + + + + + diff --git a/pmd-java/src/main/ant/alljavacc.xml b/pmd-java/src/main/ant/alljavacc.xml index d2c6296b63..d6fcddd710 100644 --- a/pmd-java/src/main/ant/alljavacc.xml +++ b/pmd-java/src/main/ant/alljavacc.xml @@ -28,15 +28,23 @@ - + + + + + - + + + + + + + diff --git a/pmd-javascript/src/main/ant/alljavacc.xml b/pmd-javascript/src/main/ant/alljavacc.xml index 405026b5cd..fa4e9998b3 100644 --- a/pmd-javascript/src/main/ant/alljavacc.xml +++ b/pmd-javascript/src/main/ant/alljavacc.xml @@ -29,11 +29,15 @@ - + + + + + + + diff --git a/pmd-jsp/src/main/ant/alljavacc.xml b/pmd-jsp/src/main/ant/alljavacc.xml index 80c1b43001..d50b37c6f6 100644 --- a/pmd-jsp/src/main/ant/alljavacc.xml +++ b/pmd-jsp/src/main/ant/alljavacc.xml @@ -28,17 +28,25 @@ - + + + + + - + + + + + + + + + diff --git a/pmd-matlab/src/main/ant/alljavacc.xml b/pmd-matlab/src/main/ant/alljavacc.xml index 646b4740f1..41c6972b31 100644 --- a/pmd-matlab/src/main/ant/alljavacc.xml +++ b/pmd-matlab/src/main/ant/alljavacc.xml @@ -29,11 +29,15 @@ - + + + + + + + diff --git a/pmd-modelica/src/main/ant/alljavacc.xml b/pmd-modelica/src/main/ant/alljavacc.xml index 0b6c12a533..111640936c 100644 --- a/pmd-modelica/src/main/ant/alljavacc.xml +++ b/pmd-modelica/src/main/ant/alljavacc.xml @@ -28,15 +28,23 @@ - + + + + + - + + + + + + + diff --git a/pmd-objectivec/src/main/ant/alljavacc.xml b/pmd-objectivec/src/main/ant/alljavacc.xml index 16a4dd9e84..0502a8eb7f 100644 --- a/pmd-objectivec/src/main/ant/alljavacc.xml +++ b/pmd-objectivec/src/main/ant/alljavacc.xml @@ -29,11 +29,15 @@ - + + + + + + + diff --git a/pmd-plsql/src/main/ant/alljavacc.xml b/pmd-plsql/src/main/ant/alljavacc.xml index 5d0e23135a..441d04ae02 100644 --- a/pmd-plsql/src/main/ant/alljavacc.xml +++ b/pmd-plsql/src/main/ant/alljavacc.xml @@ -28,15 +28,23 @@ - + + + + + - + + + + + + + diff --git a/pmd-python/src/main/ant/alljavacc.xml b/pmd-python/src/main/ant/alljavacc.xml index 1996f86bf1..a52efb5b48 100644 --- a/pmd-python/src/main/ant/alljavacc.xml +++ b/pmd-python/src/main/ant/alljavacc.xml @@ -29,11 +29,15 @@ - + + + + + + + diff --git a/pmd-visualforce/src/main/ant/alljavacc.xml b/pmd-visualforce/src/main/ant/alljavacc.xml index fff5966898..2d4e2ad549 100644 --- a/pmd-visualforce/src/main/ant/alljavacc.xml +++ b/pmd-visualforce/src/main/ant/alljavacc.xml @@ -28,18 +28,27 @@ - - - + + + + + + + + + + + + + + + + + diff --git a/pmd-vm/src/main/ant/alljavacc.xml b/pmd-vm/src/main/ant/alljavacc.xml index 5be48f1bc5..7e4a54cb97 100644 --- a/pmd-vm/src/main/ant/alljavacc.xml +++ b/pmd-vm/src/main/ant/alljavacc.xml @@ -28,17 +28,25 @@ - + + + + + - + + + + + + + + + From ed8fbccb1cae9d140f8b1e4ee07dffc945fe7347 Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Fri, 18 Dec 2020 14:46:24 +0100 Subject: [PATCH 015/154] Add tests for additional C# 7 and C# 8 features --- .../sourceforge/pmd/cpd/CsTokenizerTest.java | 5 + .../cs/cpd/testdata/csharp7And8Additions.cs | 26 +++ .../cs/cpd/testdata/csharp7And8Additions.txt | 155 ++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.cs create mode 100644 pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.txt diff --git a/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java b/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java index 6b61c658bb..63c9cd5aee 100644 --- a/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java +++ b/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java @@ -100,6 +100,11 @@ public class CsTokenizerTest extends CpdTextComparisonTest { doTest("listOfNumbers", "_ignored", skipLiteralSequences()); } + @Test + public void testCSharp7And8Additions() { + doTest("csharp7And8Additions"); + } + private Properties ignoreUsings() { return properties(true, false); } diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.cs b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.cs new file mode 100644 index 0000000000..6d14774d75 --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.cs @@ -0,0 +1,26 @@ +#nullable enable +using System; +using System.Collections.Generic; + +class CSharp7And8Additions +{ + private static void Literals() + { + int x = 30_000_000; // digit separators + int b = 0b00101000; // boolean literal + } + + private static unsafe void DisplaySize() where T : unmanaged // unmanaged keyword + { + Console.WriteLine($"{typeof(T)} is unmanaged and its size is {sizeof(T)} bytes"); + } + + private static void Operators() + { + List? l = null; + (l ??= new List()).Add(5); // null-coalescing assignment operator + + var array = new int[] { 1, 2, 3, 4, 5 }; + var slice1 = array[2..^3]; // range operator + } +} diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.txt new file mode 100644 index 0000000000..5d689e8505 --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.txt @@ -0,0 +1,155 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [#] 1 1 +L2 + [using] 1 5 + [System] 7 12 + [;] 13 13 +L3 + [using] 1 5 + [System] 7 12 + [.] 13 13 + [Collections] 14 24 + [.] 25 25 + [Generic] 26 32 + [;] 33 33 +L5 + [class] 1 5 + [CSharp7And8Additions] 7 26 +L6 + [{] 1 1 +L7 + [private] 5 11 + [static] 13 18 + [void] 20 23 + [Literals] 25 32 + [(] 33 33 + [)] 34 34 +L8 + [{] 5 5 +L9 + [int] 9 11 + [x] 13 13 + [=] 15 15 + [30_000_000] 17 26 + [;] 27 27 +L10 + [int] 9 11 + [b] 13 13 + [=] 15 15 + [0b00101000] 17 26 + [;] 27 27 +L11 + [}] 5 5 +L13 + [private] 5 11 + [static] 13 18 + [unsafe] 20 25 + [void] 27 30 + [DisplaySize] 32 42 + [<] 43 43 + [T] 44 44 + [>] 45 45 + [(] 46 46 + [)] 47 47 + [where] 49 53 + [T] 55 55 + [:] 57 57 + [unmanaged] 59 67 +L14 + [{] 5 5 +L15 + [Console] 9 15 + [.] 16 16 + [WriteLine] 17 25 + [(] 26 26 + [$"] 27 28 + [typeof] 30 35 + [(] 36 36 + [T] 37 37 + [)] 38 38 + [ is unmanaged and its size is ] 40 69 + [sizeof] 71 76 + [(] 77 77 + [T] 78 78 + [)] 79 79 + [ bytes] 81 86 + ["] 87 87 + [)] 88 88 + [;] 89 89 +L16 + [}] 5 5 +L18 + [private] 5 11 + [static] 13 18 + [void] 20 23 + [Operators] 25 33 + [(] 34 34 + [)] 35 35 +L19 + [{] 5 5 +L20 + [List] 9 12 + [<] 13 13 + [int] 14 16 + [>] 17 17 + [?] 18 18 + [l] 20 20 + [=] 22 22 + [null] 24 27 + [;] 28 28 +L21 + [(] 9 9 + [l] 10 10 + [??=] 12 14 + [new] 16 18 + [List] 20 23 + [<] 24 24 + [int] 25 27 + [>] 28 28 + [(] 29 29 + [)] 30 30 + [)] 31 31 + [.] 32 32 + [Add] 33 35 + [(] 36 36 + [5] 37 37 + [)] 38 38 + [;] 39 39 +L23 + [var] 9 11 + [array] 13 17 + [=] 19 19 + [new] 21 23 + [int] 25 27 + [\[] 28 28 + [\]] 29 29 + [{] 31 31 + [1] 33 33 + [,] 34 34 + [2] 36 36 + [,] 37 37 + [3] 39 39 + [,] 40 40 + [4] 42 42 + [,] 43 43 + [5] 45 45 + [}] 47 47 + [;] 48 48 +L24 + [var] 9 11 + [slice1] 13 18 + [=] 20 20 + [array] 22 26 + [\[] 27 27 + [2] 28 28 + [..] 29 30 + [^] 31 31 + [3] 32 32 + [\]] 33 33 + [;] 34 34 +L25 + [}] 5 5 +L26 + [}] 1 1 +EOF From 2bbf56ec064b62389d32e8ae8389cdbd47adeea8 Mon Sep 17 00:00:00 2001 From: Ozan Gulle Date: Mon, 21 Dec 2020 21:37:09 +0100 Subject: [PATCH 016/154] Added a new test case --- .../xml/LiteralsFirstInComparisons.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml index e1d2a48dbf..28cd529172 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml @@ -354,6 +354,19 @@ public class Foo { public boolean test(String someString) { return someString.equals(TEST_CONSTANT); } +} + ]]> + + + + #575 PositionLiteralsFirstInComparisons must not trigger if the constant field is not a String + 0 + From 49fbb90cb133f6933122c885cea1d8d9d711565d Mon Sep 17 00:00:00 2001 From: Ozan Gulle Date: Mon, 21 Dec 2020 21:37:22 +0100 Subject: [PATCH 017/154] Changed naming of the method --- .../rule/bestpractices/LiteralsFirstInComparisonsRule.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java index 83200e98d6..9a036cf7b9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java @@ -136,14 +136,14 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { } private JavaNode getFirstLiteralArgument(ASTPrimarySuffix primarySuffix) { - return getArgumentPrimaryPrefixFromSuffix(primarySuffix).getFirstChildOfType(ASTLiteral.class); + return getArgumentPrimaryPrefix(primarySuffix).getFirstChildOfType(ASTLiteral.class); } private JavaNode getFirstNameArgument(ASTPrimarySuffix primarySuffix) { - return getArgumentPrimaryPrefixFromSuffix(primarySuffix).getFirstChildOfType(ASTName.class); + return getArgumentPrimaryPrefix(primarySuffix).getFirstChildOfType(ASTName.class); } - private JavaNode getArgumentPrimaryPrefixFromSuffix(ASTPrimarySuffix primarySuffix) { + private JavaNode getArgumentPrimaryPrefix(ASTPrimarySuffix primarySuffix) { ASTArguments arguments = primarySuffix.getFirstChildOfType(ASTArguments.class); ASTArgumentList argumentList = arguments.getFirstChildOfType(ASTArgumentList.class); ASTExpression expression = argumentList.getFirstChildOfType(ASTExpression.class); From 77292da7af58a3a57394ce34eaf3e70ef2cc265d Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Wed, 23 Dec 2020 10:46:34 +0100 Subject: [PATCH 018/154] Add test for empty C# comment. This was previously incorrectly tokenized, because it was recognized as the start of a documentation comment. --- .../net/sourceforge/pmd/lang/cs/cpd/testdata/comments.cs | 2 ++ .../net/sourceforge/pmd/lang/cs/cpd/testdata/comments.txt | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.cs b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.cs index 1757cf2afd..fe3d2575ce 100644 --- a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.cs +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.cs @@ -1,3 +1,5 @@ + /**/ +// the previous comment is an empty delimited comment and not a document comment class Foo { /// class X /* aaa diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.txt index 92214cb4e9..0134960679 100644 --- a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.txt +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.txt @@ -1,8 +1,8 @@ [Image] or [Truncated image[ Bcol Ecol -L1 +L3 [class] 1 5 [Foo] 7 9 [{] 11 11 -L6 +L8 [}] 2 2 EOF From 539f457d8171db34bb5a74543b9eb913b247ea5f Mon Sep 17 00:00:00 2001 From: jr Date: Thu, 24 Dec 2020 14:21:44 -0800 Subject: [PATCH 019/154] fix: use rulechain --- .../rule/errorprone/OverrideBothEqualsAndHashcodeRule.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java index 5adf03c2b1..e9dbc37f4f 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java @@ -12,10 +12,12 @@ import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule; public class OverrideBothEqualsAndHashcodeRule extends AbstractApexRule { + public OverrideBothEqualsAndHashcodeRule() { + addRuleChainVisit(ASTUserClass.class); + } + @Override public Object visit(ASTUserClass node, Object data) { - super.visit(node, data); - ApexNode equalsNode = null; ApexNode hashNode = null; for (ASTMethod method : node.findChildrenOfType(ASTMethod.class)) { From 49bab051e8d970b8967e71f61fdab56523258b2b Mon Sep 17 00:00:00 2001 From: jr Date: Thu, 24 Dec 2020 14:22:15 -0800 Subject: [PATCH 020/154] fix: use primitive --- .../rule/errorprone/OverrideBothEqualsAndHashcodeRule.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java index e9dbc37f4f..c63367e7dc 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java @@ -41,7 +41,7 @@ public class OverrideBothEqualsAndHashcodeRule extends AbstractApexRule { return data; } - private Boolean isEquals(ASTMethod node) { + private boolean isEquals(ASTMethod node) { int numParams = 0; String paramType = null; for (int ix = 0; ix < node.getNumChildren(); ix++) { @@ -54,7 +54,7 @@ public class OverrideBothEqualsAndHashcodeRule extends AbstractApexRule { return numParams == 1 && node.hasImageEqualTo("equals") && "Object".equalsIgnoreCase(paramType); } - private Boolean isHashCode(ASTMethod node) { + private boolean isHashCode(ASTMethod node) { int numParams = 0; for (int ix = 0; ix < node.getNumChildren(); ix++) { ApexNode sn = node.getChild(ix); From c5fa4cbfa0585832a5473f5e507d9e5cce691698 Mon Sep 17 00:00:00 2001 From: Vincent Maurin Date: Mon, 4 Jan 2021 15:51:27 +0100 Subject: [PATCH 021/154] Fix severity strings Code Climate issue severity are specified here https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#issues as `info`, `minor`, `major`, `critical`, or `blocker`. It was mapped to a value `normal` that is not recognized by tools compatible with Code Climate's format. I have mapped the five PMD priority values on the five Code Climate severity levels --- .../sourceforge/pmd/renderers/CodeClimateRenderer.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java index c738fac851..7430d4bb6f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java @@ -84,12 +84,16 @@ public class CodeClimateRenderer extends AbstractIncrementingRenderer { switch (rule.getPriority()) { case HIGH: - issue.severity = "critical"; + issue.severity = "blocker"; break; case MEDIUM_HIGH: + issue.severity = "critical"; + break; case MEDIUM: + issue.severity = "major"; + break; case MEDIUM_LOW: - issue.severity = "normal"; + issue.severity = "minor"; break; case LOW: default: From 59493d1e825021e556394912fe563ca4390bd0f1 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 7 Jan 2021 12:09:10 +0100 Subject: [PATCH 022/154] [doc] Prefer github discussions for questions --- CONTRIBUTING.md | 2 +- README.md | 8 +++++--- docs/pages/pmd/about/help.md | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 372eab4e2d..0172b4acc9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,7 +45,7 @@ There are various channels, on which you can ask questions: * On [StackOverflow](https://stackoverflow.com/questions/tagged/pmd): Make sure, to tag your question with "pmd". -* Create a issue for your question at . +* Create a new discussion for your question at . * Ask your question on Gitter . diff --git a/README.md b/README.md index ca8a2941b8..f639615e8a 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,12 @@ Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex, Scala, Swift, ## Support -* How do I? -- Ask a question on [StackOverflow](https://stackoverflow.com/questions/tagged/pmd). -* I got this error, why? -- Ask a question on [StackOverflow](https://stackoverflow.com/questions/tagged/pmd). +* How do I? -- Ask a question on [StackOverflow](https://stackoverflow.com/questions/tagged/pmd) + or on [discussions](https://github.com/pmd/pmd/discussions). +* I got this error, why? -- Ask a question on [StackOverflow](https://stackoverflow.com/questions/tagged/pmd) + or on [discussions](https://github.com/pmd/pmd/discussions). * I got this error and I'm sure it's a bug -- file an [issue](https://github.com/pmd/pmd/issues). -* I have an idea/request/question -- file an [issue](https://github.com/pmd/pmd/issues). +* I have an idea/request/question -- create a new [discussion](https://github.com/pmd/pmd/discussions). * I have a quick question -- ask on our [Gitter chat](https://gitter.im/pmd/pmd). * Where's your documentation? -- diff --git a/docs/pages/pmd/about/help.md b/docs/pages/pmd/about/help.md index c20f9fd066..8e45c96b8b 100644 --- a/docs/pages/pmd/about/help.md +++ b/docs/pages/pmd/about/help.md @@ -1,8 +1,8 @@ --- title: Getting Help permalink: pmd_about_help.html -author: Andreas Dangel -last_updated: September 2017 +author: Andreas Dangel +last_updated: January 2021 --- There are numerous ways of getting help: @@ -13,7 +13,7 @@ There are numerous ways of getting help: * If you found a bug, please create a new [github issue](https://github.com/pmd/pmd/issues). -* You can also ask questions in our [sourceforge forum](https://sourceforge.net/p/pmd/discussion/). +* You can also ask questions on [github discussions](https://github.com/pmd/pmd/discussions). * Or you can join the [Mailing List](https://lists.sourceforge.net/lists/listinfo/pmd-devel) or browse through the archives ([archive1](http://java-pmd.30631.n5.nabble.com/), [archive2](http://web.archive.org/web/20160715035623/http://blog.gmane.org:80/gmane.comp.java.audit.pmd.devel)). From 5ed003c6862aa14d9f6d477f924661a0a2071c79 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 7 Jan 2021 15:28:06 +0100 Subject: [PATCH 023/154] Update gems CVE-2020-26247 https://github.com/advisories/GHSA-vr8q-g5c7-m54m --- Gemfile.lock | 20 +++++++------- docs/Gemfile.lock | 67 ++++++++++++++++++++++++++--------------------- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9bdbcf639f..70b6a0223e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -34,18 +34,19 @@ GEM fugit (1.4.1) et-orbi (~> 1.1, >= 1.1.8) raabro (~> 1.4) - git (1.7.0) + git (1.8.1) rchardet (~> 1.8) kramdown (1.17.0) - liquid (4.0.3) + liquid (5.0.0) logger-colors (1.0.0) - mini_portile2 (2.4.0) + mini_portile2 (2.5.0) multipart-post (2.1.1) nap (1.1.0) no_proxy_fix (0.1.2) - nokogiri (1.10.10) - mini_portile2 (~> 2.4.0) - octokit (4.19.0) + nokogiri (1.11.1) + mini_portile2 (~> 2.5.0) + racc (~> 1.4) + octokit (4.20.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) open4 (1.3.4) @@ -58,9 +59,10 @@ GEM slop (~> 4.6) public_suffix (4.0.6) raabro (1.4.0) + racc (1.5.2) rchardet (1.8.0) - rouge (3.25.0) - rufus-scheduler (3.6.0) + rouge (3.26.0) + rufus-scheduler (3.7.0) fugit (~> 1.1, >= 1.1.6) safe_yaml (1.0.5) sawyer (0.8.2) @@ -69,7 +71,7 @@ GEM slop (4.8.2) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - tzinfo (2.0.3) + tzinfo (2.0.4) concurrent-ruby (~> 1.0) unicode-display_width (1.7.0) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 739f66ebe2..e474166447 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.3.2) + activesupport (6.0.3.4) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -17,37 +17,40 @@ GEM commonmarker (0.17.13) ruby-enum (~> 0.5) concurrent-ruby (1.1.7) - dnsruby (1.61.4) + dnsruby (1.61.5) simpleidn (~> 0.1) - em-websocket (0.5.1) + em-websocket (0.5.2) eventmachine (>= 0.12.9) http_parser.rb (~> 0.6.0) ethon (0.12.0) ffi (>= 1.3.0) eventmachine (1.2.7) execjs (2.7.0) - faraday (1.0.1) + faraday (1.3.0) + faraday-net_http (~> 1.0) multipart-post (>= 1.2, < 3) - ffi (1.13.1) + ruby2_keywords + faraday-net_http (1.0.0) + ffi (1.14.2) forwardable-extended (2.6.0) gemoji (3.0.1) - github-pages (207) + github-pages (209) github-pages-health-check (= 1.16.1) jekyll (= 3.9.0) jekyll-avatar (= 0.7.0) jekyll-coffeescript (= 1.1.1) jekyll-commonmark-ghpages (= 0.1.6) jekyll-default-layout (= 0.1.4) - jekyll-feed (= 0.13.0) + jekyll-feed (= 0.15.1) jekyll-gist (= 1.5.0) jekyll-github-metadata (= 2.13.0) - jekyll-mentions (= 1.5.1) + jekyll-mentions (= 1.6.0) jekyll-optional-front-matter (= 0.3.2) jekyll-paginate (= 1.1.0) jekyll-readme-index (= 0.3.0) - jekyll-redirect-from (= 0.15.0) + jekyll-redirect-from (= 0.16.0) jekyll-relative-links (= 0.6.1) - jekyll-remote-theme (= 0.4.1) + jekyll-remote-theme (= 0.4.2) jekyll-sass-converter (= 1.5.2) jekyll-seo-tag (= 2.6.1) jekyll-sitemap (= 1.4.0) @@ -55,7 +58,7 @@ GEM jekyll-theme-architect (= 0.1.1) jekyll-theme-cayman (= 0.1.1) jekyll-theme-dinky (= 0.1.1) - jekyll-theme-hacker (= 0.1.1) + jekyll-theme-hacker (= 0.1.2) jekyll-theme-leap-day (= 0.1.1) jekyll-theme-merlot (= 0.1.1) jekyll-theme-midnight (= 0.1.1) @@ -66,14 +69,14 @@ GEM jekyll-theme-tactile (= 0.1.1) jekyll-theme-time-machine (= 0.1.1) jekyll-titles-from-headings (= 0.5.3) - jemoji (= 0.11.1) + jemoji (= 0.12.0) kramdown (= 2.3.0) kramdown-parser-gfm (= 1.1.0) liquid (= 4.0.3) mercenary (~> 0.3) minima (= 2.5.1) nokogiri (>= 1.10.4, < 2.0) - rouge (= 3.19.0) + rouge (= 3.23.0) terminal-table (~> 1.4) github-pages-health-check (1.16.1) addressable (~> 2.3) @@ -114,14 +117,14 @@ GEM rouge (>= 2.0, < 4.0) jekyll-default-layout (0.1.4) jekyll (~> 3.0) - jekyll-feed (0.13.0) + jekyll-feed (0.15.1) jekyll (>= 3.7, < 5.0) jekyll-gist (1.5.0) octokit (~> 4.2) jekyll-github-metadata (2.13.0) jekyll (>= 3.4, < 5.0) octokit (~> 4.0, != 4.4.0) - jekyll-mentions (1.5.1) + jekyll-mentions (1.6.0) html-pipeline (~> 2.3) jekyll (>= 3.7, < 5.0) jekyll-optional-front-matter (0.3.2) @@ -129,14 +132,15 @@ GEM jekyll-paginate (1.1.0) jekyll-readme-index (0.3.0) jekyll (>= 3.0, < 5.0) - jekyll-redirect-from (0.15.0) + jekyll-redirect-from (0.16.0) jekyll (>= 3.3, < 5.0) jekyll-relative-links (0.6.1) jekyll (>= 3.3, < 5.0) - jekyll-remote-theme (0.4.1) + jekyll-remote-theme (0.4.2) addressable (~> 2.0) jekyll (>= 3.5, < 5.0) - rubyzip (>= 1.3.0) + jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) + rubyzip (>= 1.3.0, < 3.0) jekyll-sass-converter (1.5.2) sass (~> 3.4) jekyll-seo-tag (2.6.1) @@ -153,8 +157,8 @@ GEM jekyll-theme-dinky (0.1.1) jekyll (~> 3.5) jekyll-seo-tag (~> 2.0) - jekyll-theme-hacker (0.1.1) - jekyll (~> 3.5) + jekyll-theme-hacker (0.1.2) + jekyll (> 3.5, < 5.0) jekyll-seo-tag (~> 2.0) jekyll-theme-leap-day (0.1.1) jekyll (~> 3.5) @@ -188,7 +192,7 @@ GEM jekyll (>= 3.3, < 5.0) jekyll-watch (2.2.1) listen (~> 3.0) - jemoji (0.11.1) + jemoji (0.12.0) gemoji (~> 3.0) html-pipeline (~> 2.2) jekyll (>= 3.0, < 5.0) @@ -197,32 +201,35 @@ GEM kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) liquid (4.0.3) - listen (3.2.1) + listen (3.4.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.3.6) - mini_portile2 (2.4.0) + mini_portile2 (2.5.0) minima (2.5.1) jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.14.1) + minitest (5.14.3) multipart-post (2.1.1) - nokogiri (1.10.10) - mini_portile2 (~> 2.4.0) - octokit (4.18.0) + nokogiri (1.11.1) + mini_portile2 (~> 2.5.0) + racc (~> 1.4) + octokit (4.20.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) pathutil (0.16.2) forwardable-extended (~> 2.6) public_suffix (3.1.1) + racc (1.5.2) rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) rexml (3.2.4) - rouge (3.19.0) + rouge (3.23.0) ruby-enum (0.8.0) i18n + ruby2_keywords (0.0.2) rubyzip (2.3.0) safe_yaml (1.0.5) sass (3.7.4) @@ -240,13 +247,13 @@ GEM thread_safe (0.3.6) typhoeus (1.4.0) ethon (>= 0.9.0) - tzinfo (1.2.7) + tzinfo (1.2.9) thread_safe (~> 0.1) unf (0.1.4) unf_ext unf_ext (0.0.7.7) unicode-display_width (1.7.0) - zeitwerk (2.4.0) + zeitwerk (2.4.2) PLATFORMS ruby From 4b99f42657babdc7b9e248c839d2827d9aac8dca Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 7 Jan 2021 15:31:45 +0100 Subject: [PATCH 024/154] [doc] Fix link to deprecated RulesetsFactoryUtils --- docs/pages/next_major_development.md | 2 +- docs/pages/release_notes_old.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pages/next_major_development.md b/docs/pages/next_major_development.md index d8b0d6a550..94d7546cbd 100644 --- a/docs/pages/next_major_development.md +++ b/docs/pages/next_major_development.md @@ -131,7 +131,7 @@ we may decide to remove some APIs that were not tagged as deprecated, though we' ###### Around RuleSet parsing -* {% jdoc core::RuleSetFactory %} and {% jdoc core::RuleSetFactoryUtils %} have been deprecated in favor of {% jdoc core::RuleSetLoader %}. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. +* {% jdoc core::RuleSetFactory %} and {% jdoc core::RulesetsFactoryUtils %} have been deprecated in favor of {% jdoc core::RuleSetLoader %}. This is easier to configure, and more maintainable than the multiple overloads of `RulesetsFactoryUtils`. * Some static creation methods have been added to {% jdoc core::RuleSet %} for simple cases, eg {% jdoc core::RuleSet#forSingleRule(core::Rule) %}. These replace some counterparts in {% jdoc core::RuleSetFactory %} * Since {% jdoc core::RuleSets %} is also deprecated, many APIs that require a RuleSets instance now are deprecated, and have a counterpart that expects a `List`. * {% jdoc core::RuleSetReferenceId %}, {% jdoc core::RuleSetReference %}, {% jdoc core::RuleSetFactoryCompatibility %} are deprecated. They are most likely not relevant outside of the implementation of pmd-core. diff --git a/docs/pages/release_notes_old.md b/docs/pages/release_notes_old.md index cc2bb4a7f7..85489a2b5e 100644 --- a/docs/pages/release_notes_old.md +++ b/docs/pages/release_notes_old.md @@ -70,7 +70,7 @@ Thanks to Jeff Bartolotta and Roopa Mohan for contributing this! ##### Around RuleSet parsing -*
RuleSetFactory and RulesetsFactoryUtils have been deprecated in favor of RuleSetLoader. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. +* RuleSetFactory and RulesetsFactoryUtils have been deprecated in favor of RuleSetLoader. This is easier to configure, and more maintainable than the multiple overloads of `RulesetsFactoryUtils`. * Some static creation methods have been added to RuleSet for simple cases, eg forSingleRule. These replace some counterparts in RuleSetFactory * Since RuleSets is also deprecated, many APIs that require a RuleSets instance now are deprecated, and have a counterpart that expects a `List`. * RuleSetReferenceId, RuleSetReference, RuleSetFactoryCompatibility are deprecated. They are most likely not relevant outside of the implementation of pmd-core. From 740d832dc1af9485b35827c3e3f29e86161dcd83 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 7 Jan 2021 19:51:23 +0100 Subject: [PATCH 025/154] Change from PR review #2983 --- .../bestpractices/LiteralsFirstInComparisonsRule.java | 2 +- .../rule/bestpractices/xml/LiteralsFirstInComparisons.xml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java index 9a036cf7b9..d89bcedf5e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java @@ -168,7 +168,7 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { for (ASTFieldDeclaration fieldDeclaration : fieldDeclarations) { ASTVariableDeclarator declaration = fieldDeclaration.getFirstChildOfType(ASTVariableDeclarator.class); if (declaration.getName().equals(name.getImage()) - && "class java.lang.String".equals(declaration.getType().toString()) + && String.class.equals(declaration.getType()) && fieldDeclaration.isFinal() && fieldDeclaration.isStatic()) { return true; diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml index 28cd529172..903bb45279 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml @@ -320,7 +320,7 @@ public class Foo { - #575 PositionLiteralsFirstInComparisons to consider constant fields, i.e. static final Strings + #575 LiteralsFirstInComparisons to consider constant fields, i.e. static final Strings 1 - #575 PositionLiteralsFirstInComparisons must not trigger if the field is not final + #575 LiteralsFirstInComparisons must not trigger if the field is not final 0 - #575 PositionLiteralsFirstInComparisons must not trigger if the field is not static + #575 LiteralsFirstInComparisons must not trigger if the field is not static 0 - #575 PositionLiteralsFirstInComparisons must not trigger if the constant field is not a String + #575 LiteralsFirstInComparisons must not trigger if the constant field is not a String 0 Date: Thu, 7 Jan 2021 19:54:51 +0100 Subject: [PATCH 026/154] [doc] Update release notes, refs #2983, fixes #575 --- docs/pages/release_notes.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index b8f8783555..89cb352488 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -16,9 +16,14 @@ This is a {{ site.pmd.release_type }} release. ### Fixed Issues +* java-bestpractices + * [#575](https://github.com/pmd/pmd/issues/575): \[java] LiteralsFirstInComparisons should consider constant fields + ### API Changes ### External Contributions +* [#2983](https://github.com/pmd/pmd/pull/2983): \[java] LiteralsFirstInComparisons should consider constant fields - [Ozan Gulle](https://github.com/ozangulle) + {% endtocmaker %} From c7cd242ab2465bdcd6a1b0397c0dcd687c5a15ea Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 7 Jan 2021 20:14:29 +0100 Subject: [PATCH 027/154] [core] Renderers - adjust tests for different rule priorities (#2994) --- .../sourceforge/pmd/renderers/AbstractRendererTest.java | 9 +++++++-- .../net/sourceforge/pmd/renderers/CSVRendererTest.java | 2 +- .../pmd/renderers/CodeClimateRendererTest.java | 4 ++-- .../net/sourceforge/pmd/renderers/JsonRendererTest.java | 1 + .../net/sourceforge/pmd/renderers/XMLRendererTest.java | 2 +- .../pmd/renderers/json/expected-multiple.json | 2 +- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java index 97e0e6664d..bff13f27ef 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java @@ -16,6 +16,7 @@ import net.sourceforge.pmd.Report.ConfigurationError; import net.sourceforge.pmd.Report.ProcessingError; import net.sourceforge.pmd.ReportTest; import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.RuleWithProperties; import net.sourceforge.pmd.lang.ast.DummyNode; @@ -69,8 +70,12 @@ public abstract class AbstractRendererTest { private Report reportTwoViolations() { Report report = new Report(); - report.addRuleViolation(newRuleViolation(1)); - report.addRuleViolation(newRuleViolation(2)); + RuleViolation informationalRuleViolation = newRuleViolation(1); + informationalRuleViolation.getRule().setPriority(RulePriority.LOW); + report.addRuleViolation(informationalRuleViolation); + RuleViolation severeRuleViolation = newRuleViolation(2); + severeRuleViolation.getRule().setPriority(RulePriority.HIGH); + report.addRuleViolation(severeRuleViolation); return report; } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CSVRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CSVRendererTest.java index 029dd303d8..5ef24218fe 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CSVRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CSVRendererTest.java @@ -30,7 +30,7 @@ public class CSVRendererTest extends AbstractRendererTest { public String getExpectedMultiple() { return getHeader() + "\"1\",\"\",\"" + getSourceCodeFilename() + "\",\"5\",\"1\",\"blah\",\"RuleSet\",\"Foo\"" + PMD.EOL - + "\"2\",\"\",\"" + getSourceCodeFilename() + "\",\"5\",\"1\",\"blah\",\"RuleSet\",\"Foo\"" + PMD.EOL; + + "\"2\",\"\",\"" + getSourceCodeFilename() + "\",\"1\",\"1\",\"blah\",\"RuleSet\",\"Foo\"" + PMD.EOL; } @Override diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java index 089c8d12a2..ca24c5a909 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java @@ -76,7 +76,7 @@ public class CodeClimateRendererTest extends AbstractRendererTest { + "violationSuppressXPath | | Suppress violations on nodes which match a given relative XPath expression.\\n" + "\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"" + getSourceCodeFilename() + "\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\",\"remediation_points\":50000}" + "\u0000" + PMD.EOL + "{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\"," - + "\"content\":{\"body\":\"## Foo\\n\\nSince: PMD null\\n\\nPriority: Low\\n\\n" + + "\"content\":{\"body\":\"## Foo\\n\\nSince: PMD null\\n\\nPriority: High\\n\\n" + "[Categories](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#categories): Style\\n\\n" + "[Remediation Points](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#remediation-points): 50000\\n\\n" + "desc\\n\\n" @@ -84,7 +84,7 @@ public class CodeClimateRendererTest extends AbstractRendererTest { + "Name | Value | Description\\n" + "--- | --- | ---\\n" + "violationSuppressRegex | | Suppress violations with messages matching a regular expression\\n" + "violationSuppressXPath | | Suppress violations on nodes which match a given relative XPath expression.\\n" - + "\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"" + getSourceCodeFilename() + "\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\",\"remediation_points\":50000}" + + "\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"" + getSourceCodeFilename() + "\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"blocker\",\"remediation_points\":50000}" + "\u0000" + PMD.EOL; } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/JsonRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/JsonRendererTest.java index 2a1f08fd3c..524277fe21 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/JsonRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/JsonRendererTest.java @@ -79,6 +79,7 @@ public class JsonRendererTest extends AbstractRendererTest { public String filter(String expected) { String result = expected .replaceAll("\"timestamp\":\\s*\"[^\"]+\"", "\"timestamp\": \"--replaced--\"") + .replaceAll("\"pmdVersion\":\\s*\"[^\"]+\"", "\"pmdVersion\": \"unknown\"") .replaceAll("\r\n", "\n"); // make the test run on Windows, too return result; } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java index 62e628c106..6e11717f08 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java @@ -63,7 +63,7 @@ public class XMLRendererTest extends AbstractRendererTest { return getHeader() + "" + PMD.EOL + "" + PMD.EOL + "blah" + PMD.EOL + "" + PMD.EOL - + "" + + "" + PMD.EOL + "blah" + PMD.EOL + "" + PMD.EOL + "" + PMD.EOL + "" + PMD.EOL; } diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/json/expected-multiple.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/json/expected-multiple.json index 15717aed32..9ab3704f1e 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/json/expected-multiple.json +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/json/expected-multiple.json @@ -24,7 +24,7 @@ "description": "blah", "rule": "Foo", "ruleset": "RuleSet", - "priority": 5 + "priority": 1 } ] } From a5a51f0d5e35c7014d373dd5f7fbb7ec2f7bfeaa Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 7 Jan 2021 20:16:40 +0100 Subject: [PATCH 028/154] [doc] Update release notes, refs #2994 --- docs/pages/release_notes.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index b8f8783555..efd1ca7f67 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -16,9 +16,14 @@ This is a {{ site.pmd.release_type }} release. ### Fixed Issues +* core + * [#2994](https://github.com/pmd/pmd/pull/2994): \[core] Fix code climate severity strings + ### API Changes ### External Contributions +* [#2994](https://github.com/pmd/pmd/pull/2994): \[core] Fix code climate severity strings - [Vincent Maurin](https://github.com/vmaurin) + {% endtocmaker %} From 49ff9a5c5ffe57d2884a089ddec1d939fa530537 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 8 Jan 2021 15:33:48 +0100 Subject: [PATCH 029/154] [ci] Increase build timeout for pull requests to 60 minutes --- .github/workflows/pull-requests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index 169f000a80..9fd0c2fcbe 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -6,7 +6,7 @@ jobs: build: runs-on: ${{ matrix.os }} continue-on-error: false - timeout-minutes: 30 + timeout-minutes: 60 strategy: matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] From fde4d08213cac5e4fefa063fec0e5987d7bc58e6 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 8 Jan 2021 15:38:47 +0100 Subject: [PATCH 030/154] [ci] Fail if baseline couldn't be created/uploaded --- .ci/inc/regression-tester.inc | 63 +++++++++++------------------------ 1 file changed, 20 insertions(+), 43 deletions(-) diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index e959e9a780..7f5298abb4 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -33,54 +33,31 @@ function regression_tester_setup_ci() { } # -# Generate a new baseline and upload it to sourceforge -# -# Note: this function always succeeds, even if the upload fails. -# In that case, just a error logging is provided. +# Generate a new baseline and upload it to pmd-code.org # function regression_tester_uploadBaseline() { log_debug "$FUNCNAME branch=${PMD_CI_BRANCH}" - local targetUrl="https://sourceforge.net/projects/pmd/files/pmd-regression-tester/" local pmdcodeUrl="https://pmd-code.org/pmd-regression-tester/" - local errexitstate="$(shopt -po errexit)" - set +e # disable errexit - ( - # This handler is called if any command fails - function upload_failed() { - log_error "Error while uploading ${BRANCH_FILENAME}-baseline.zip to pmd-code.org!" - log_error "Please upload manually: ${pmdcodeUrl}" - #log_error "Error while uploading ${BRANCH_FILENAME}-baseline.zip to sourceforge!" - #log_error "Please upload manually: ${targetUrl}" - } - - # exit subshell after trap - set -e - trap upload_failed ERR - - log_info "Generating and uploading baseline for pmdtester..." - cd .. - bundle config --local gemfile pmd/Gemfile - bundle config set --local path pmd/vendor/bundle - bundle exec pmdtester \ - --mode single \ - --local-git-repo ./pmd \ - --patch-branch ${PMD_CI_BRANCH:-$PMD_CI_TAG} \ - --patch-config ./pmd/.ci/files/all-java.xml \ - --list-of-project ./pmd/.ci/files/project-list.xml --html-flag \ - --error-recovery - cd target/reports - BRANCH_FILENAME="${PMD_CI_BRANCH:-$PMD_CI_TAG}" - BRANCH_FILENAME="${BRANCH_FILENAME/\//_}" - zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/ - # ssh-key for pmd-code.org is setup already by pmd_ci_setup_ssh - scp ${BRANCH_FILENAME}-baseline.zip pmd@pmd-code.org:/httpdocs/pmd-regression-tester/ - log_success "Successfully uploaded ${BRANCH_FILENAME}-baseline.zip to ${pmdcodeUrl}" - #../../pmd/.ci/travis_wait "rsync -avh ${BRANCH_FILENAME}-baseline.zip ${PMD_SF_USER}@web.sourceforge.net:/home/frs/project/pmd/pmd-regression-tester/" - #log_success "Successfully uploaded ${BRANCH_FILENAME}-baseline.zip to ${targetUrl}" - ) - # restore errexit state - eval "$errexitstate" + log_info "Generating and uploading baseline for pmdtester..." + pushd .. + bundle config --local gemfile pmd/Gemfile + bundle config set --local path pmd/vendor/bundle + bundle exec pmdtester \ + --mode single \ + --local-git-repo ./pmd \ + --patch-branch ${PMD_CI_BRANCH:-$PMD_CI_TAG} \ + --patch-config ./pmd/.ci/files/all-java.xml \ + --list-of-project ./pmd/.ci/files/project-list.xml --html-flag \ + --error-recovery + cd target/reports + BRANCH_FILENAME="${PMD_CI_BRANCH:-$PMD_CI_TAG}" + BRANCH_FILENAME="${BRANCH_FILENAME/\//_}" + zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/ + # ssh-key for pmd-code.org is setup already by pmd_ci_setup_ssh + scp ${BRANCH_FILENAME}-baseline.zip pmd@pmd-code.org:/httpdocs/pmd-regression-tester/ + log_success "Successfully uploaded ${BRANCH_FILENAME}-baseline.zip to ${pmdcodeUrl}" + popd } # From 1ae70c2fa069a7a4875d92988a35df98b677d7c2 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 8 Jan 2021 15:50:42 +0100 Subject: [PATCH 031/154] [ci] Fix config for bundler for executing pmdtester --- .ci/inc/regression-tester.inc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index 7f5298abb4..29ec49adea 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -36,28 +36,29 @@ function regression_tester_setup_ci() { # Generate a new baseline and upload it to pmd-code.org # function regression_tester_uploadBaseline() { - log_debug "$FUNCNAME branch=${PMD_CI_BRANCH}" local pmdcodeUrl="https://pmd-code.org/pmd-regression-tester/" + local baseline_branch="${PMD_CI_BRANCH:-$PMD_CI_TAG}" + log_debug "$FUNCNAME branch=${baseline_branch}" - log_info "Generating and uploading baseline for pmdtester..." + log_info "Generating and uploading baseline for pmdtester (${baseline_branch})..." pushd .. - bundle config --local gemfile pmd/Gemfile - bundle config set --local path pmd/vendor/bundle + rm -f .bundle/config + bundle config set --local gemfile pmd/Gemfile bundle exec pmdtester \ --mode single \ --local-git-repo ./pmd \ - --patch-branch ${PMD_CI_BRANCH:-$PMD_CI_TAG} \ + --patch-branch ${baseline_branch} \ --patch-config ./pmd/.ci/files/all-java.xml \ --list-of-project ./pmd/.ci/files/project-list.xml --html-flag \ --error-recovery - cd target/reports - BRANCH_FILENAME="${PMD_CI_BRANCH:-$PMD_CI_TAG}" - BRANCH_FILENAME="${BRANCH_FILENAME/\//_}" + pushd target/reports + BRANCH_FILENAME="${baseline_branch/\//_}" zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/ # ssh-key for pmd-code.org is setup already by pmd_ci_setup_ssh scp ${BRANCH_FILENAME}-baseline.zip pmd@pmd-code.org:/httpdocs/pmd-regression-tester/ log_success "Successfully uploaded ${BRANCH_FILENAME}-baseline.zip to ${pmdcodeUrl}" popd + popd } # From ddcc2055166276c8682ad48f751b0eefb4131a70 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 8 Jan 2021 15:59:33 +0100 Subject: [PATCH 032/154] [ci] Fail if danger couldn't execute pmdtester or upload result --- .ci/inc/regression-tester.inc | 50 +++++++++++------------------------ Dangerfile | 2 +- 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index 29ec49adea..3bc72ccd81 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -64,42 +64,24 @@ function regression_tester_uploadBaseline() { # # Execute danger, which executes pmd-regression-tester (via Dangerfile). # -# Note: this function always succeeds, even if the danger fails. -# In that case, just a error logging is provided. -# function regression_tester_executeDanger() { log_debug "$FUNCNAME" - local errexitstate="$(shopt -po errexit)" - set +e # disable errexit - ( - # This handler is called if any command fails - function danger_failed() { - log_error "Error while executing danger/pmd-regression-tester" - } + # Create a corresponding remote branch locally + if ! git show-ref --verify --quiet refs/heads/${PMD_CI_BRANCH}; then + git fetch --no-tags --depth=1 origin +refs/heads/${PMD_CI_BRANCH}:refs/remotes/origin/${PMD_CI_BRANCH} + git branch ${PMD_CI_BRANCH} origin/${PMD_CI_BRANCH} + log_debug "Created local branch ${PMD_CI_BRANCH}" + fi + # Fetch more commits of the PR for danger and regression tester + git fetch --no-tags --depth=50 origin +$(git rev-parse HEAD^2): + # Fetch more commits from master branch for regression tester + if [[ "${PMD_CI_BRANCH}" != "master" ]]; then + git fetch --no-tags --depth=50 origin +master: + git branch master origin/master + fi - # exit subshell after trap - set -e - trap danger_failed ERR - - # Create a corresponding remote branch locally - if ! git show-ref --verify --quiet refs/heads/${PMD_CI_BRANCH}; then - git fetch --no-tags --depth=1 origin +refs/heads/${PMD_CI_BRANCH}:refs/remotes/origin/${PMD_CI_BRANCH} - git branch ${PMD_CI_BRANCH} origin/${PMD_CI_BRANCH} - log_debug "Created local branch ${PMD_CI_BRANCH}" - fi - # Fetch more commits of the PR for danger and regression tester - git fetch --no-tags --depth=50 origin +$(git rev-parse HEAD^2): - # Fetch more commits from master branch for regression tester - if [[ "${PMD_CI_BRANCH}" != "master" ]]; then - git fetch --no-tags --depth=50 origin +master: - git branch master origin/master - fi - - log_info "Running danger on branch ${PMD_CI_BRANCH}" - bundle exec danger --verbose - log_success "Executing danger successfully" - ) - # restore errexit state - eval "$errexitstate" + log_info "Running danger on branch ${PMD_CI_BRANCH}" + bundle exec danger --verbose + log_success "Executed danger successfully" } diff --git a/Dangerfile b/Dangerfile index 07cc07a715..d43916e4c7 100644 --- a/Dangerfile +++ b/Dangerfile @@ -39,7 +39,7 @@ def upload_report `tar -cf #{tar_filename} diff/` report_url = `curl -u #{ENV['PMD_CI_CHUNK_TOKEN']} -T #{tar_filename} https://chunk.io` if $?.success? - @logger.info "Successfully uploaded #{tar_filename} to chunk.io" + @logger.info "Successfully uploaded #{tar_filename} to #{report_url}" # set value of sticky to true and the message is kept after new commits are submitted to the PR message("This changeset " \ From 83bc024fd367591c40b60f68e1d0344f31e35962 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 8 Jan 2021 16:09:22 +0100 Subject: [PATCH 033/154] [ci] Enable debug mode for pmdtester --- .ci/inc/regression-tester.inc | 3 ++- Dangerfile | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index 3bc72ccd81..28575083d9 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -50,7 +50,8 @@ function regression_tester_uploadBaseline() { --patch-branch ${baseline_branch} \ --patch-config ./pmd/.ci/files/all-java.xml \ --list-of-project ./pmd/.ci/files/project-list.xml --html-flag \ - --error-recovery + --error-recovery \ + --debug pushd target/reports BRANCH_FILENAME="${baseline_branch/\//_}" zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/ diff --git a/Dangerfile b/Dangerfile index d43916e4c7..ae23da1367 100644 --- a/Dangerfile +++ b/Dangerfile @@ -16,7 +16,7 @@ def run_pmdtester '--auto-gen-config', '--error-recovery', '--baseline-download-url', 'https://pmd-code.org/pmd-regression-tester/', - # '--debug', + '--debug', ] begin @summary = PmdTester::Runner.new(argv).run From 93e567b8ea7f504aa0b5499bc2b157d5757b1dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 8 Jan 2021 21:39:33 +0100 Subject: [PATCH 034/154] Update release notes, refs #2964 --- docs/pages/release_notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index b8f8783555..f6e1ff6b89 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -20,5 +20,7 @@ This is a {{ site.pmd.release_type }} release. ### External Contributions +- [#2964](https://github.com/pmd/pmd/pull/2964) \[cs] Update C# grammar for additional C# 7 and C# 8 features -- [Maikel Steneker](https://github.com/maikelsteneker) + {% endtocmaker %} From 407aa690d1185a187763aa83fccb6f8310e8fe8b Mon Sep 17 00:00:00 2001 From: rmohan Date: Thu, 17 Dec 2020 13:34:57 -0800 Subject: [PATCH 035/154] parent 7fc3dee95acbb40692f2c115ebe7b0264f377385 author rmohan 1608240897 -0800 committer rmohan 1610153787 -0800 gpgsig -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEPykD30Ug+JG+ZQ7x5XWwXqT2VuwFAl/4/zsACgkQ5XWwXqT2 VuwJoBAAsdl/TU3nculnOPwrCHtaxgtM/4zB+wLdw7f+KtIaGMv5A8v8gMbqh0V+ OTPV0Q/CTEk4PxA2qD5aCPLOaO9+LHOKjPdkWalttVjINtTVPR2pKo0PKR+f2q77 79eFkhiB1uNx741ThGIZoWPv8eMKHYP+1BEwndFdByxSA70+gdYkbmZqKLLOOrTE 2uxuGwnNM02ZYskupvgPW2pZvWFviR2sZt/GbFPkqAwgjf40MxM5+3cujOOV850E hLHCcch5G5sh8sHTb9nCyNmc6i1kSw9+OOpOHmJGV4u3DRBBtTE9FdwyEUJ2VliZ 77VrfRPxwzkAE3jTH7EGCY7kpf84jQMqrGn/gav1LfH4DscPZmF617r9iYZ5fOr6 Z2Fun4vaCXk0h1CojCOpnh4WAthhiVtvBJmdiyO489/hmbtwWuz7KBkCsEVBS+8E ViEaQylIOWjcFXkAq5PQ8fCxIqqMgbI7p5kQL6zatpykteMT5zoJj41t8oyjr5Rz RUGXNPxpW3zKq3jw4VsjZ8qNYuoSerTzx0qGuUQIALO97HZ8ZBsxmcDTFJrzAkj7 yfslzLRIoPlL4ZKdpOgCGzVLBX1D/YCBn9KXQiYEYRh57umIt0wpM/Y/Xkl+/Jr4 97DYj7GQr1gYavSqe7z5n3aUixyibpWy/wx2K3VN6PmtZ3FEB5I= =dqIs -----END PGP SIGNATURE----- Creating new VfHtmlStyleTagXss rule to handle XSS in style tags. Also, refactoring common code out of VfUnescapeElRule to avoid duplication. --- .../rule/security/VfHtmlStyleTagXssRule.java | 73 +++++++++++-------- .../vf/rule/security/VfUnescapeElRule.java | 2 +- .../rule/security/lib/ElEscapeDetector.java | 70 ++++++++++++++---- .../rule/security/xml/VfHtmlStyleTagXss.xml | 48 ------------ 4 files changed, 100 insertions(+), 93 deletions(-) diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java index 3239a2167e..921e749d1f 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java @@ -22,13 +22,29 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { private final ElEscapeDetector escapeDetector = new ElEscapeDetector(); + public VfHtmlStyleTagXssRule() { + addRuleChainVisit(ASTElExpression.class); + } + + /** + * We are looking for an ASTElExpression node that is + * placed inside an ASTContent, which in turn is placed inside + * an ASTElement, where the element is not an inbuilt vf tag. + * + * + * + * + * + * + * + */ @Override public Object visit(ASTElExpression node, Object data) { final VfNode nodeParent = node.getParent(); if (!(nodeParent instanceof ASTContent)) { // nothing to do here. // We care only if parent is available and is an ASTContent - return super.visit(node, data); + return data; } final ASTContent contentNode = (ASTContent) nodeParent; @@ -36,7 +52,7 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { if (!(nodeGrandParent instanceof ASTElement)) { // nothing to do here. // We care only if grandparent is available and is an ASTElement - return super.visit(node, data); + return data; } final ASTElement elementNode = (ASTElement) nodeGrandParent; @@ -44,14 +60,18 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { if (isApexPrefixed(elementNode)) { // nothing to do here. // This rule does not deal with inbuilt-visualforce tags - return super.visit(node, data); + return data; } verifyEncoding(node, contentNode, elementNode, data); - return super.visit(node, data); + return data; } + /** + * Examining encoding of ElExpression - we apply different rules + * for plain HTML tags and content. + */ private void verifyEncoding( ASTElExpression node, ASTContent contentNode, @@ -60,10 +80,13 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { final String previousText = getPreviousText(contentNode, node); if (isStyleTag(elementNode)) { - verifyStyleEncoding(node, previousText, data); - } else { - // El expression in any other plain HTML tag should be encoded - verifyGeneralHtmlEncoding(node, data); + final boolean isWithinSafeResource = escapeDetector.startsWithSafeResource(node); + // check if we are within a URL expression + if (isWithinUrlMethod(previousText)) { + verifyEncodingWithinUrl(node, isWithinSafeResource, data); + } else { + verifyEncodingWithoutUrl(node, isWithinSafeResource, data); + } } } @@ -72,16 +95,6 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { return STYLE_TAG.equalsIgnoreCase(elementNode.getLocalName()); } - private void verifyStyleEncoding(ASTElExpression elExpressionNode, String previousText, Object data) { - final boolean isWithinSafeResource = escapeDetector.startsWithSafeResource(elExpressionNode); - // check if we are within a URL expression - if (isWithinUrlMethod(previousText)) { - verifyEncodingWithinUrl(elExpressionNode, isWithinSafeResource, data); - } else { - verifyEncodingWithoutUrl(elExpressionNode, isWithinSafeResource, data); - } - } - private void verifyEncodingWithinUrl(ASTElExpression elExpressionNode, boolean isWithinSafeResource, Object data) { // only allow URLENCODING or JSINHTMLENCODING @@ -109,23 +122,23 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { } } - private void verifyGeneralHtmlEncoding(ASTElExpression elExpressionNode, Object data) { - if (escapeDetector.doesElContainAnyUnescapedIdentifiers( - elExpressionNode, - ElEscapeDetector.Escaping.HTMLENCODE) - && !escapeDetector.startsWithSafeResource(elExpressionNode)) { - addViolationWithMessage( - data, - elExpressionNode, - "Dynamic content in plain HTML tags should be HTMLENCODED"); - } - } - private boolean isApexPrefixed(ASTElement node) { return node.isHasNamespacePrefix() && APEX_PREFIX.equalsIgnoreCase(node.getNamespacePrefix()); } + /** + * Get text content within style tag that leads upto the ElExpression. + * For example, in this snippet: + * + * + * getPreviousText(...) would return "\n div {\n background: url(" + * + */ private String getPreviousText(ASTContent content, ASTElExpression elExpressionNode) { final int indexInParent = elExpressionNode.getIndexInParent(); final VfNode previous = indexInParent > 0 ? content.getChild(indexInParent - 1) : null; diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java index 0a537f92bf..8d419c538e 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java @@ -47,7 +47,7 @@ public class VfUnescapeElRule extends AbstractVfRule { private static final Pattern ON_EVENT = Pattern.compile("^on(\\w)+$"); private static final Pattern PLACEHOLDERS = Pattern.compile("\\{(\\w|,|\\.|'|:|\\s)*\\}"); - private ElEscapeDetector escapeDetector = new ElEscapeDetector(); + private final ElEscapeDetector escapeDetector = new ElEscapeDetector(); @Override public Object visit(ASTHtmlScript node, Object data) { diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/lib/ElEscapeDetector.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/lib/ElEscapeDetector.java index 4821afbf84..1ebc8654d9 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/lib/ElEscapeDetector.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/lib/ElEscapeDetector.java @@ -4,7 +4,6 @@ package net.sourceforge.pmd.lang.vf.rule.security.lib; -import java.util.Arrays; import java.util.EnumSet; import java.util.HashSet; import java.util.List; @@ -27,17 +26,7 @@ import net.sourceforge.pmd.lang.vf.ast.VfTypedNode; * (porting over code previously living in VfUnescapeElRule for reusability) */ -public class ElEscapeDetector { - - private static final List FUNCTIONS_WITH_ARGS = Arrays.asList( - "urlfor", "casesafeid", "begins", "contains", "len", "getrecordids", "linkto", - "sqrt", "round", "mod", "log", "ln", "exp", "abs", "floor", "ceiling", - "nullvalue", "isnumber", "isnull", "isnew", "isblank", "isclone", - "year", "month", "day", "datetimevalue", "datevalue", "date", "now", "today"); - - private static final List FUNCTIONS_NO_ARGS = Arrays.asList( - "$action", "$page", "$site", "$resource", "$label", "$objecttype", - "$component", "$remoteaction", "$messagechannel"); +public final class ElEscapeDetector { public boolean innerContainsSafeFields(final AbstractVFNode expression) { for (int i = 0; i < expression.getNumChildren(); i++) { @@ -90,7 +79,59 @@ public class ElEscapeDetector { String lowerCaseId = id.getImage().toLowerCase(Locale.ROOT); List args = expression.findChildrenOfType(ASTArguments.class); - return args.isEmpty() ? FUNCTIONS_NO_ARGS.contains(lowerCaseId) : FUNCTIONS_WITH_ARGS.contains(lowerCaseId); + if (!args.isEmpty()) { + switch (lowerCaseId) { + case "urlfor": + case "casesafeid": + case "begins": + case "contains": + case "len": + case "getrecordids": + case "linkto": + case "sqrt": + case "round": + case "mod": + case "log": + case "ln": + case "exp": + case "abs": + case "floor": + case "ceiling": + case "nullvalue": + case "isnumber": + case "isnull": + case "isnew": + case "isblank": + case "isclone": + case "year": + case "month": + case "day": + case "datetimevalue": + case "datevalue": + case "date": + case "now": + case "today": + return true; + + default: + } + } else { + // has no arguments + switch (lowerCaseId) { + case "$action": + case "$page": + case "$site": + case "$resource": + case "$label": + case "$objecttype": + case "$component": + case "$remoteaction": + case "$messagechannel": + return true; + + default: + } + } } } return false; @@ -156,7 +197,7 @@ public class ElEscapeDetector { * Return true if the type of all data nodes can be determined and none of them require escaping * @param expression */ - private boolean expressionContainsSafeDataNodes(ASTExpression expression) { + public boolean expressionContainsSafeDataNodes(ASTExpression expression) { try { for (VfTypedNode node : expression.getDataNodes().keySet()) { DataType dataType = node.getDataType(); @@ -189,4 +230,5 @@ public class ElEscapeDetector { return text; } } + } diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/xml/VfHtmlStyleTagXss.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/xml/VfHtmlStyleTagXss.xml index 5ac5b57e5a..288a385cc8 100644 --- a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/xml/VfHtmlStyleTagXss.xml +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/xml/VfHtmlStyleTagXss.xml @@ -4,42 +4,6 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd"> - - sObject field assigned to non-style html - 1 - -
- ${!XSSHere} -
- - ]]>
-
- - - sObject field assigned to apex tag value - 0 - - - ${!XSSHere} - - - ]]> - - - - sObject field assigned to apex tag attribute - 0 - - - some output content - - - ]]> - - sObject field (from controller) assigned to url variable in style tag (html encoded) 1 @@ -148,16 +112,4 @@ a { {!JSENCODE(XSSHere)} } ]]>
- - EL assigned to plain html within safe resource - 0 - -
- {!URLFOR($Resource.valid)} -
- - ]]>
-
- \ No newline at end of file From e8a0ba9815f3e6a1928ed671388847778506603c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Jan 2021 18:22:33 +0100 Subject: [PATCH 036/154] Make map not static in MethodNamingConventionsRule Fix #2960 --- .../java/rule/codestyle/MethodNamingConventionsRule.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java index 46c74a8568..0183e2889c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java @@ -22,7 +22,7 @@ import net.sourceforge.pmd.properties.PropertyDescriptor; public class MethodNamingConventionsRule extends AbstractNamingConventionRule { - private static final Map DESCRIPTOR_TO_DISPLAY_NAME = new HashMap<>(); + private final Map descriptorToDisplayName = new HashMap<>(); @Deprecated private static final BooleanProperty CHECK_NATIVE_METHODS_DESCRIPTOR = new BooleanProperty("checkNativeMethods", @@ -118,7 +118,7 @@ public class MethodNamingConventionsRule extends AbstractNamingConventionRule descriptor) { - return DESCRIPTOR_TO_DISPLAY_NAME.get(descriptor.name()); + return descriptorToDisplayName.get(descriptor.name()); } } From ec4dcdd262eae34722c2068856376cae2390e612 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Jan 2021 18:24:27 +0100 Subject: [PATCH 037/154] [doc] Update release notes, refs #2970 --- docs/pages/release_notes.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index b8f8783555..468a8a0ecc 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -16,6 +16,9 @@ This is a {{ site.pmd.release_type }} release. ### Fixed Issues +* core + * [#2970](https://github.com/pmd/pmd/issues/2970): \[core] PMD 6.30.0 release is not reproducible + ### API Changes ### External Contributions From 8f021502de2febd9281aa7acbc905d5708acdfdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Jan 2021 18:25:28 +0100 Subject: [PATCH 038/154] Update release notes Refs #3060 --- docs/pages/release_notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 9d2add0d98..9847ce0625 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -20,6 +20,8 @@ This is a {{ site.pmd.release_type }} release. * [#2994](https://github.com/pmd/pmd/pull/2994): \[core] Fix code climate severity strings * java-bestpractices * [#575](https://github.com/pmd/pmd/issues/575): \[java] LiteralsFirstInComparisons should consider constant fields +* java-codestyle + * [#2960](https://github.com/pmd/pmd/issues/2960): \[java] Thread issue in MethodNamingConventionsRule ### API Changes From 6a179980bea3bc719aa8961484131839574c662b Mon Sep 17 00:00:00 2001 From: rmohan Date: Fri, 15 Jan 2021 13:40:07 -0800 Subject: [PATCH 039/154] Applying changes from review feedback --- .../rule/security/VfHtmlStyleTagXssRule.java | 31 ++++--- .../vf/rule/security/VfUnescapeElRule.java | 10 ++- .../{lib => internal}/ElEscapeDetector.java | 87 +++++-------------- ...HtmlXssStyleTagUrlPatternMatchingTest.java | 14 +-- 4 files changed, 52 insertions(+), 90 deletions(-) rename pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/{lib => internal}/ElEscapeDetector.java (69%) diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java index 921e749d1f..72c95edd9c 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java @@ -13,12 +13,15 @@ import net.sourceforge.pmd.lang.vf.ast.ASTElement; import net.sourceforge.pmd.lang.vf.ast.ASTText; import net.sourceforge.pmd.lang.vf.ast.VfNode; import net.sourceforge.pmd.lang.vf.rule.AbstractVfRule; -import net.sourceforge.pmd.lang.vf.rule.security.lib.ElEscapeDetector; +import net.sourceforge.pmd.lang.vf.rule.security.internal.ElEscapeDetector; public class VfHtmlStyleTagXssRule extends AbstractVfRule { private static final String STYLE_TAG = "style"; private static final String APEX_PREFIX = "apex"; + private static final EnumSet URLENCODE_JSINHTMLENCODE = EnumSet.of(ElEscapeDetector.Escaping.URLENCODE, ElEscapeDetector.Escaping.JSINHTMLENCODE); + private static final EnumSet ANY_ENCODE = EnumSet.of(ElEscapeDetector.Escaping.ANY); + private static final String URL_METHOD_PATTERN = "url\\s*\\([^)]*$"; private final ElEscapeDetector escapeDetector = new ElEscapeDetector(); @@ -78,14 +81,17 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { ASTElement elementNode, Object data) { final String previousText = getPreviousText(contentNode, node); + final boolean isWithinSafeResource = escapeDetector.startsWithSafeResource(node); + + // if El is inside a tag + // and is not surrounded by a safe resource, check for violations + if (isStyleTag(elementNode) && !isWithinSafeResource) { - if (isStyleTag(elementNode)) { - final boolean isWithinSafeResource = escapeDetector.startsWithSafeResource(node); // check if we are within a URL expression if (isWithinUrlMethod(previousText)) { - verifyEncodingWithinUrl(node, isWithinSafeResource, data); + verifyEncodingWithinUrl(node, data); } else { - verifyEncodingWithoutUrl(node, isWithinSafeResource, data); + verifyEncodingWithoutUrl(node, data); } } } @@ -95,13 +101,12 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { return STYLE_TAG.equalsIgnoreCase(elementNode.getLocalName()); } - private void verifyEncodingWithinUrl(ASTElExpression elExpressionNode, boolean isWithinSafeResource, Object data) { + private void verifyEncodingWithinUrl(ASTElExpression elExpressionNode, Object data) { // only allow URLENCODING or JSINHTMLENCODING if (escapeDetector.doesElContainAnyUnescapedIdentifiers( elExpressionNode, - EnumSet.of(ElEscapeDetector.Escaping.URLENCODE, ElEscapeDetector.Escaping.JSINHTMLENCODE)) - && !isWithinSafeResource) { + URLENCODE_JSINHTMLENCODE)) { addViolationWithMessage( data, elExpressionNode, @@ -110,11 +115,10 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { } - private void verifyEncodingWithoutUrl(ASTElExpression elExpressionNode, boolean isWithinSafeResource, Object data) { + private void verifyEncodingWithoutUrl(ASTElExpression elExpressionNode, Object data) { if (escapeDetector.doesElContainAnyUnescapedIdentifiers( elExpressionNode, - EnumSet.of(ElEscapeDetector.Escaping.ANY)) - && !isWithinSafeResource) { + ANY_ENCODE)) { addViolationWithMessage( data, elExpressionNode, @@ -146,7 +150,7 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { } // visible for unit testing - boolean isWithinUrlMethod(String previousText) { + static boolean isWithinUrlMethod(String previousText) { // match for a pattern that // 1. contains "url" (case insensitive), // 2. followed by any number of whitespaces, @@ -156,8 +160,7 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { // Matches: "div { background: url('", "div { background: Url ( blah" // Does not match: "div { background: url('myUrl')", "div { background: myStyle('" - final String urlMethodPattern = "url\\s*\\([^)]*$"; - return Pattern.compile(urlMethodPattern, Pattern.CASE_INSENSITIVE) + return Pattern.compile(URL_METHOD_PATTERN, Pattern.CASE_INSENSITIVE) .matcher(previousText) .find(); } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java index 8d419c538e..7a321abd1e 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java @@ -21,7 +21,7 @@ import net.sourceforge.pmd.lang.vf.ast.ASTHtmlScript; import net.sourceforge.pmd.lang.vf.ast.ASTLiteral; import net.sourceforge.pmd.lang.vf.ast.ASTText; import net.sourceforge.pmd.lang.vf.rule.AbstractVfRule; -import net.sourceforge.pmd.lang.vf.rule.security.lib.ElEscapeDetector; +import net.sourceforge.pmd.lang.vf.rule.security.internal.ElEscapeDetector; /** @@ -46,6 +46,8 @@ public class VfUnescapeElRule extends AbstractVfRule { private static final String FALSE = "false"; private static final Pattern ON_EVENT = Pattern.compile("^on(\\w)+$"); private static final Pattern PLACEHOLDERS = Pattern.compile("\\{(\\w|,|\\.|'|:|\\s)*\\}"); + private static final EnumSet JSENCODE_JSINHTMLENCODE = EnumSet.of(ElEscapeDetector.Escaping.JSENCODE, ElEscapeDetector.Escaping.JSINHTMLENCODE); + private static final EnumSet ANY_ENCODE = EnumSet.of(ElEscapeDetector.Escaping.ANY); private final ElEscapeDetector escapeDetector = new ElEscapeDetector(); @@ -88,14 +90,14 @@ public class VfUnescapeElRule extends AbstractVfRule { // check escaping too if (!(jsonParse || escapeDetector.startsWithSafeResource(elExpression) || escapeDetector.containsSafeFields(elExpression))) { if (escapeDetector.doesElContainAnyUnescapedIdentifiers(elExpression, - EnumSet.of(ElEscapeDetector.Escaping.JSENCODE, ElEscapeDetector.Escaping.JSINHTMLENCODE))) { + JSENCODE_JSINHTMLENCODE)) { addViolation(data, elExpression); } } } else { if (!(escapeDetector.startsWithSafeResource(elExpression) || escapeDetector.containsSafeFields(elExpression))) { final boolean hasUnscaped = escapeDetector.doesElContainAnyUnescapedIdentifiers(elExpression, - EnumSet.of(ElEscapeDetector.Escaping.JSENCODE, ElEscapeDetector.Escaping.JSINHTMLENCODE)); + JSENCODE_JSINHTMLENCODE); if (!(jsonParse && !hasUnscaped)) { addViolation(data, elExpression); } @@ -219,7 +221,7 @@ public class VfUnescapeElRule extends AbstractVfRule { } if (escapeDetector.doesElContainAnyUnescapedIdentifiers(el, - EnumSet.of(ElEscapeDetector.Escaping.ANY))) { + ANY_ENCODE)) { isEL = true; toReport.add(el); } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/lib/ElEscapeDetector.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/internal/ElEscapeDetector.java similarity index 69% rename from pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/lib/ElEscapeDetector.java rename to pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/internal/ElEscapeDetector.java index 1ebc8654d9..b92a88f5f9 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/lib/ElEscapeDetector.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/internal/ElEscapeDetector.java @@ -2,15 +2,15 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.lang.vf.rule.security.lib; +package net.sourceforge.pmd.lang.vf.rule.security.internal; +import java.util.Arrays; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; -import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.vf.DataType; import net.sourceforge.pmd.lang.vf.ast.ASTArguments; import net.sourceforge.pmd.lang.vf.ast.ASTDotExpression; @@ -19,6 +19,7 @@ import net.sourceforge.pmd.lang.vf.ast.ASTExpression; import net.sourceforge.pmd.lang.vf.ast.ASTIdentifier; import net.sourceforge.pmd.lang.vf.ast.ASTNegationExpression; import net.sourceforge.pmd.lang.vf.ast.AbstractVFNode; +import net.sourceforge.pmd.lang.vf.ast.VfNode; import net.sourceforge.pmd.lang.vf.ast.VfTypedNode; /** @@ -28,18 +29,19 @@ import net.sourceforge.pmd.lang.vf.ast.VfTypedNode; public final class ElEscapeDetector { - public boolean innerContainsSafeFields(final AbstractVFNode expression) { - for (int i = 0; i < expression.getNumChildren(); i++) { - Node child = expression.getChild(i); + private static final Set SAFE_EXPRESSIONS = new HashSet<>(Arrays.asList("id", "size", "caseNumber")); + private static final Set NON_EMPTY_ARG_SAFE_RESOURCE = new HashSet<>(Arrays.asList("urlfor", "casesafeid", "begins", "contains", + "len", "getrecordids", "linkto", "sqrt", "round", "mod", "log", "ln", "exp", "abs", "floor", "ceiling", + "nullvalue", "isnumber", "isnull", "isnew", "isblank", "isclone", "year", "month", "day", "datetimevalue", + "datevalue", "date", "now", "today")); + private static final Set EMPTY_ARG_SAFE_RESOURCE = new HashSet<>(Arrays.asList("$action", "$page", "$site", + "$resource", "$label", "$objecttype", "$component", "$remoteaction", "$messagechannel")); - if (child instanceof ASTIdentifier) { - switch (child.getImage().toLowerCase(Locale.ROOT)) { - case "id": - case "size": - case "caseNumber": - return true; - default: - } + public boolean innerContainsSafeFields(final VfNode expression) { + for (VfNode child : expression.children()) { + + if (child instanceof ASTIdentifier && SAFE_EXPRESSIONS.contains(child.getImage().toLowerCase(Locale.ROOT))) { + return true; } if (child instanceof ASTArguments) { @@ -79,59 +81,14 @@ public final class ElEscapeDetector { String lowerCaseId = id.getImage().toLowerCase(Locale.ROOT); List args = expression.findChildrenOfType(ASTArguments.class); - if (!args.isEmpty()) { - switch (lowerCaseId) { - case "urlfor": - case "casesafeid": - case "begins": - case "contains": - case "len": - case "getrecordids": - case "linkto": - case "sqrt": - case "round": - case "mod": - case "log": - case "ln": - case "exp": - case "abs": - case "floor": - case "ceiling": - case "nullvalue": - case "isnumber": - case "isnull": - case "isnew": - case "isblank": - case "isclone": - case "year": - case "month": - case "day": - case "datetimevalue": - case "datevalue": - case "date": - case "now": - case "today": - return true; - - default: - } - } else { - // has no arguments - switch (lowerCaseId) { - case "$action": - case "$page": - case "$site": - case "$resource": - case "$label": - case "$objecttype": - case "$component": - case "$remoteaction": - case "$messagechannel": - return true; - - default: - } + if (!args.isEmpty() && NON_EMPTY_ARG_SAFE_RESOURCE.contains(lowerCaseId)) { + return true; } + + if (args.isEmpty() && EMPTY_ARG_SAFE_RESOURCE.contains(lowerCaseId)) { + return true; + } + } } return false; diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlXssStyleTagUrlPatternMatchingTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlXssStyleTagUrlPatternMatchingTest.java index f7b80edf07..9f5ac72b51 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlXssStyleTagUrlPatternMatchingTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlXssStyleTagUrlPatternMatchingTest.java @@ -19,42 +19,42 @@ public class VfHtmlXssStyleTagUrlPatternMatchingTest { @Test public void testUrlMethodPatternMatchForPositive() { final String sampleString = "div { background: url(blah"; - assertTrue("Sample should be considered as starting a URL method: " + sampleString, rule.isWithinUrlMethod(sampleString)); + assertTrue("Sample should be considered as starting a URL method: " + sampleString, VfHtmlStyleTagXssRule.isWithinUrlMethod(sampleString)); } @Test public void testUrlMethodPatternMatchForCaseInsensitive() { final String sampleString = "div { background: uRl("; - assertTrue("Sample should be considered as starting a URL method: " + sampleString, rule.isWithinUrlMethod(sampleString)); + assertTrue("Sample should be considered as starting a URL method: " + sampleString, VfHtmlStyleTagXssRule.isWithinUrlMethod(sampleString)); } @Test public void testUrlMethodPatternMatchForWhitespaceAfterUrl() { final String sampleString = "div { background: url ("; - assertTrue("Sample should be considered as starting a URL method: " + sampleString, rule.isWithinUrlMethod(sampleString)); + assertTrue("Sample should be considered as starting a URL method: " + sampleString, VfHtmlStyleTagXssRule.isWithinUrlMethod(sampleString)); } @Test public void testUrlMethodPatternMatchForClosedUrl() { final String sampleString = "div { background: url('myUrl')"; - assertFalse("Sample should not be considered as starting a URL method: " + sampleString, rule.isWithinUrlMethod(sampleString)); + assertFalse("Sample should not be considered as starting a URL method: " + sampleString, VfHtmlStyleTagXssRule.isWithinUrlMethod(sampleString)); } @Test public void testUrlMethodPatternMatchForClosedUrlWithNoContent() { final String sampleString = "div { background: url() "; - assertFalse("Sample should not be considered as starting a URL method: " + sampleString, rule.isWithinUrlMethod(sampleString)); + assertFalse("Sample should not be considered as starting a URL method: " + sampleString, VfHtmlStyleTagXssRule.isWithinUrlMethod(sampleString)); } @Test public void testUrlMethodPatternMatchForUrlNoBracket() { final String sampleString = "div { background: url"; - assertFalse("Sample should not be considered as starting a URL method: " + sampleString, rule.isWithinUrlMethod(sampleString)); + assertFalse("Sample should not be considered as starting a URL method: " + sampleString, VfHtmlStyleTagXssRule.isWithinUrlMethod(sampleString)); } @Test public void testUrlMethodPatternMatchForNoUrl() { final String sampleString = "div { background: myStyle('"; - assertFalse("Sample should not be considered as starting a URL method: " + sampleString, rule.isWithinUrlMethod(sampleString)); + assertFalse("Sample should not be considered as starting a URL method: " + sampleString, VfHtmlStyleTagXssRule.isWithinUrlMethod(sampleString)); } } From 1fa9d08d7dc82e99bac7b24ca7944fd0113adb49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Jan 2021 23:27:51 +0100 Subject: [PATCH 040/154] Test for #3004 --- .../rule/errorprone/xml/UseEqualsToCompareStrings.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml index 476b7ae864..5e4239fce9 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml @@ -126,6 +126,17 @@ public class ClassWithStringFields { public void bar(String param) { if (param != null) { } // ok } +} + ]]>
+
+ + #3004 UseEqualsToCompareStrings false positive with PMD 6.30.0 + 0 + From 3233ca01275e35d1de818216256571f9dd8ee6ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Jan 2021 23:45:31 +0100 Subject: [PATCH 041/154] Tests for #2976 --- .../xml/CompareObjectsWithEquals.xml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml index 893433e2e4..3d578ebb67 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml @@ -317,4 +317,36 @@ public class ClassWithFields { } ]]>
+ + + #2976 FP with array length + 0 + + + + #2976 FP with method call (unresolved class) + 0 + + + From 7d87e89f7216a6969983799e1febb6de52140f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Jan 2021 23:47:04 +0100 Subject: [PATCH 042/154] Tests for #2979 --- .../xml/UseEqualsToCompareStrings.xml | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml index 5e4239fce9..934090bf33 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml @@ -137,6 +137,46 @@ public class O { boolean f(String s) { return s.charAt(0) == s.charAt(1); } +} + ]]> +
+ + #2979 UseEqualsToCompareStrings: FP with "var" variables + 0 + + + + #2979 UseEqualsToCompareStrings: FP with "var" variables (control, types are explicit) + 0 + From 4e96ee6bb7843fef3a1e3266803dd9bf98c9d3f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Jan 2021 23:41:45 +0100 Subject: [PATCH 043/154] Fix type res of primary prefix for methods Consider things accessible if we can't resolve this class. In particular, local variables were inaccessible, which is absurd. --- .../pmd/lang/java/typeresolution/ClassTypeResolver.java | 3 --- .../pmd/lang/java/typeresolution/MethodTypeResolution.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java index 66ba85fda5..764a81822a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java @@ -587,9 +587,6 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla */ private JavaTypeDefinition getTypeDefinitionOfVariableFromScope(Scope scope, String image, Class accessingClass) { - if (accessingClass == null) { - return null; - } for (/* empty */; scope != null; scope = scope.getParent()) { // search each enclosing scope one by one diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodTypeResolution.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodTypeResolution.java index e00ed03859..0846737b3b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodTypeResolution.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodTypeResolution.java @@ -565,7 +565,7 @@ public final class MethodTypeResolution { */ public static boolean isMemberVisibleFromClass(Class classWithMember, int modifiers, Class accessingClass) { if (accessingClass == null) { - return false; + return true; } // public members From cbceca9861ee70857c7c94df594f6687983ef823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 16 Jan 2021 00:41:41 +0100 Subject: [PATCH 044/154] Fix DoNotUseThreads tests --- .../lang/java/typeresolution/ClassTypeResolver.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java index 764a81822a..9886d0506a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java @@ -275,6 +275,13 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla // FIXME, we should discard the array depth on this node, it should only be known to ASTReferenceType (#910) populateType(node, typeName, node.getArrayDepth()); + if (node.isAnonymousClass() && node.getTypeDefinition() == null) { + // eg for `new Runnable() { }`, retry with just "Runnable" + // instead of just "Enclosing$1" + populateType(node, node.getImage(), node.getArrayDepth()); + } + + ASTTypeArguments typeArguments = node.getFirstChildOfType(ASTTypeArguments.class); if (typeArguments != null) { @@ -435,7 +442,9 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla Collections.emptyList(), methodArgsArity, accessingClass); - previousType = getBestMethodReturnType(previousType, methods, astArgumentList); + JavaTypeDefinition resultType = getBestMethodReturnType(previousType, methods, astArgumentList); + ((ASTPrimarySuffix) astArguments.getParent()).setTypeDefinition(resultType); + break; // last iteration anyway } else { // field previousType = getFieldType(previousType, dotSplitImage[i], accessingClass); } From 9158c9f439a6c6a1dfad4020fff78c73a060caa7 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 16 Jan 2021 10:11:22 +0100 Subject: [PATCH 045/154] [ci] Update pmdtester to 1.1.1, disable debug --- .ci/inc/regression-tester.inc | 3 +-- Dangerfile | 2 +- Gemfile | 2 +- Gemfile.lock | 8 ++++---- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index 28575083d9..3bc72ccd81 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -50,8 +50,7 @@ function regression_tester_uploadBaseline() { --patch-branch ${baseline_branch} \ --patch-config ./pmd/.ci/files/all-java.xml \ --list-of-project ./pmd/.ci/files/project-list.xml --html-flag \ - --error-recovery \ - --debug + --error-recovery pushd target/reports BRANCH_FILENAME="${baseline_branch/\//_}" zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/ diff --git a/Dangerfile b/Dangerfile index ae23da1367..1d51856dab 100644 --- a/Dangerfile +++ b/Dangerfile @@ -16,7 +16,7 @@ def run_pmdtester '--auto-gen-config', '--error-recovery', '--baseline-download-url', 'https://pmd-code.org/pmd-regression-tester/', - '--debug', + #'--debug', ] begin @summary = PmdTester::Runner.new(argv).run diff --git a/Gemfile b/Gemfile index b8861eb9c3..67b22b628d 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,7 @@ source 'https://rubygems.org/' # bleeding edge from git #gem 'pmdtester', :git => 'https://github.com/pmd/pmd-regression-tester.git' -gem 'pmdtester', '~> 1.1' +gem 'pmdtester', '~> 1' gem 'danger', '~> 5.6', '>= 5.6' # This group is only needed for rendering release notes diff --git a/Gemfile.lock b/Gemfile.lock index 70b6a0223e..e742be01b1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -31,7 +31,7 @@ GEM multipart-post (>= 1.2, < 3) faraday-http-cache (1.3.1) faraday (~> 0.8) - fugit (1.4.1) + fugit (1.4.2) et-orbi (~> 1.1, >= 1.1.8) raabro (~> 1.4) git (1.8.1) @@ -50,11 +50,11 @@ GEM faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) open4 (1.3.4) - pmdtester (1.1.0) + pmdtester (1.1.1) differ (~> 0.1) liquid (>= 4.0) logger-colors (~> 1.0) - nokogiri (~> 1.8) + nokogiri (>= 1.11.0.rc4) rufus-scheduler (~> 3.5) slop (~> 4.6) public_suffix (4.0.6) @@ -81,7 +81,7 @@ PLATFORMS DEPENDENCIES danger (~> 5.6, >= 5.6) liquid (>= 4.0.0) - pmdtester (~> 1.1) + pmdtester (~> 1) rouge (>= 1.7, < 4) safe_yaml (>= 1.0) From 28ebe30127865e3cc9e42c1652a8390b877813b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 16 Jan 2021 15:56:54 +0100 Subject: [PATCH 046/154] Fix AvoidInstantiatingObjectsInLoopsRule --- .../AvoidInstantiatingObjectsInLoopsRule.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidInstantiatingObjectsInLoopsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidInstantiatingObjectsInLoopsRule.java index 035159061d..bb195accb7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidInstantiatingObjectsInLoopsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidInstantiatingObjectsInLoopsRule.java @@ -13,6 +13,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; import net.sourceforge.pmd.lang.java.ast.ASTBreakStatement; import net.sourceforge.pmd.lang.java.ast.ASTDoStatement; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTForInit; import net.sourceforge.pmd.lang.java.ast.ASTForStatement; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; @@ -21,6 +22,8 @@ import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement; import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; import net.sourceforge.pmd.lang.java.ast.ASTThrowStatement; import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; @@ -65,11 +68,27 @@ public class AvoidInstantiatingObjectsInLoopsRule extends AbstractJavaRule { private boolean notCollectionAccess(ASTAllocationExpression node) { if (node.getNthParent(4) instanceof ASTArgumentList && node.getNthParent(8) instanceof ASTStatementExpression) { ASTStatementExpression statement = (ASTStatementExpression) node.getNthParent(8); - return !TypeTestUtil.isA(Collection.class, statement); + return !isCallOnReceiverOfType(Collection.class, statement); } return true; } + private static boolean isCallOnReceiverOfType(Class receiverType, JavaNode expression) { + if ((expression instanceof ASTExpression || expression instanceof ASTStatementExpression) + && expression.getNumChildren() == 1) { + expression = expression.getChild(0); + } + int numChildren = expression.getNumChildren(); + if (expression instanceof ASTPrimaryExpression && numChildren >= 2) { + JavaNode lastChild = expression.getChild(numChildren - 1); + if (lastChild instanceof ASTPrimarySuffix && ((ASTPrimarySuffix) lastChild).isArguments()) { + JavaNode receiverExpr = expression.getChild(numChildren - 2); + return receiverExpr instanceof TypeNode && TypeTestUtil.isA(receiverType, (TypeNode) receiverExpr); + } + } + return false; + } + private boolean notBreakFollowing(ASTAllocationExpression node) { ASTBlockStatement blockStatement = node.getFirstParentOfType(ASTBlockStatement.class); if (blockStatement != null) { From 292d526a73f1a453d7613951721740c1ae2b74b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 16 Jan 2021 15:57:24 +0100 Subject: [PATCH 047/154] Fix type resolver tests The primary prefix now has the type of the receiver, when it contains the name of a method call --- .../sourceforge/pmd/typeresolution/ClassTypeResolverTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java index f5a7add61a..54cf3e777e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java @@ -1402,7 +1402,7 @@ public class ClassTypeResolverTest { // Number e = field.noArguments(); assertEquals(Number.class, expressions.get(index).getType()); - assertEquals(Number.class, getChildType(expressions.get(index), 0)); + assertEquals(MethodPotentialApplicability.class, getChildType(expressions.get(index), 0)); assertEquals(Number.class, getChildType(expressions.get(index++), 1)); // int f = this.vararg(""); From 20a2ab7416d2997ce25911178b59b3e87ddc31d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 16 Jan 2021 16:13:18 +0100 Subject: [PATCH 048/154] Remove useless code This made tests fail --- .../java/typeresolution/ClassTypeResolver.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java index 9886d0506a..2fd174559b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java @@ -1014,22 +1014,6 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla primaryNodeType = currentChild.getTypeDefinition(); } - // if this expression is a method call, then make sure, PrimaryPrefix has the type - // on which the method is executed (type of the target reference) - if (currentChild.getFirstChildOfType(ASTArguments.class) != null && previousChild.getFirstChildOfType(ASTName.class) != null) { - // restore type of the name and search again - ASTName name = previousChild.getFirstChildOfType(ASTName.class); - name.setTypeDefinition(null); - searchNodeNameForClass(name, name.getImage().split("\\.")); - if (name.getTypeDefinition() != null) { - // rollup from Name -> PrimaryPrefix - previousChild.setTypeDefinition(name.getTypeDefinition()); - } else if (name.getTypeDefinition() == null) { - // if there is no better type, use the type of the expression - name.setTypeDefinition(primaryNodeType); - } - } - // maybe array access? if (primaryNodeType != null && primaryNodeType.isArrayType()) { if (currentChild instanceof ASTPrimarySuffix && ((ASTPrimarySuffix) currentChild).isArrayDereference()) { From f9295c2bcb5cd4ef7dcda41cf55254f1e01e5de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 16 Jan 2021 16:25:22 +0100 Subject: [PATCH 049/154] Fix bug with extra array dims --- .../pmd/lang/java/typeresolution/ClassTypeResolver.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java index 2fd174559b..560978550a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java @@ -609,11 +609,7 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla return null; } - if (typeNode.getChild(0) instanceof ASTReferenceType) { - return ((TypeNode) typeNode.getChild(0)).getTypeDefinition(); - } else { // primitive type - return JavaTypeDefinition.forClass(typeNode.getType()); - } + return entry.getKey().getDeclaratorId().getTypeDefinition(); } } From f84ca189b4e40ba3839d00a33522f400300eff62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 16 Jan 2021 16:36:26 +0100 Subject: [PATCH 050/154] Update test of CloseResource --- .../pmd/lang/java/rule/errorprone/xml/CloseResource.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml index eb28592406..6254132a2a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml @@ -1135,7 +1135,8 @@ public class CloseResourcePrintWriter { 7,8,10 Ensure that resources like this FileInputStream object are closed after use - Ensure that resources like this Scanner object are closed after use + + Ensure that resources like this InputStream object are closed after use Ensure that resources like this FileInputStream object are closed after use Date: Sat, 16 Jan 2021 20:38:57 +0100 Subject: [PATCH 051/154] Test for #2977 --- .../java/rule/errorprone/xml/CloseResource.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml index 6254132a2a..71a724a999 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml @@ -1474,6 +1474,24 @@ public class Foo { c.close(); } } +} + ]]> + + + #2977 6.30.0 introduces new false positive in CloseResource rule + 0 + From b7ff1aa295904ab7dee5377fd744d94bf774f90f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 16 Jan 2021 20:54:12 +0100 Subject: [PATCH 052/154] Fix CloseResource reassigned stream Refs #2977 comment --- .../rule/errorprone/CloseResourceRule.java | 2 +- .../java/rule/errorprone/xml/CloseResource.xml | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java index 5af54e056e..0e50bc7927 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java @@ -169,7 +169,7 @@ public class CloseResourceRule extends AbstractJavaRule { } else if (shouldVarOfTypeBeClosedInMethod(resVar, resVarType, methodOrConstructor)) { reportedVarNames.add(resVar.getVariableId().getName()); addCloseResourceViolation(resVar.getVariableId(), resVarType, data); - } else { + } else if (isNotAllowedResourceType(resVarType)) { ASTStatementExpression reassigningStatement = getFirstReassigningStatementBeforeBeingClosed(resVar, methodOrConstructor); if (reassigningStatement != null) { reportedVarNames.add(resVar.getVariableId().getName()); diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml index eb28592406..c451a81a6f 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml @@ -1429,7 +1429,7 @@ public class CloseResourceWithVar { } ]]> - + #2764 false-negative when re-assigning connection java.sql.Connection,java.sql.Statement,java.sql.ResultSet @@ -1473,6 +1473,22 @@ public class Foo { c.close(); } } +} + ]]> + + + #3062 CloseResource FP with reassigned stream + 0 + stream = Stream.of(2); + if (condition) { + stream = stream.skip(2); + } + } } ]]> From d90d1645db7644a842937abcca669409c85d3f54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 20 Dec 2020 18:31:24 +0100 Subject: [PATCH 053/154] Verify that TypeHelper input is a binary name Change contract in Annotatable interface The goal is to allow us to drop support for canonical name loading in PMD 7, for performance. --- .../pmd/internal/util/AssertionUtil.java | 58 +++++++++++++++++++ .../java/ast/AbstractJavaAnnotatableNode.java | 15 +++-- .../pmd/lang/java/ast/Annotatable.java | 24 ++++---- .../lang/java/typeresolution/TypeHelper.java | 6 +- .../pmd/lang/java/types/TypeTestUtil.java | 19 +++--- 5 files changed, 91 insertions(+), 31 deletions(-) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java new file mode 100644 index 0000000000..83b76a192e --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java @@ -0,0 +1,58 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.internal.util; + +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; + +/** + * + */ +public final class AssertionUtil { + + private static final Pattern PACKAGE_PATTERN = Pattern.compile("[\\w$]+(\\.[\\w$]+)*|"); + private static final Pattern BINARY_NAME_PATTERN = Pattern.compile("[\\w$]+(?:\\.[\\w$]+)*(?:\\[])*"); + + private AssertionUtil() { + // utility class + } + + public static boolean isValidJavaPackageName(CharSequence name) { + requireParamNotNull("name", name); + return PACKAGE_PATTERN.matcher(name).matches(); + } + + /** + * @throws IllegalArgumentException if the name is not a binary name + */ + public static void assertValidJavaBinaryName(CharSequence name) { + if (!isJavaBinaryName(name)) { + throw new IllegalArgumentException("Not a Java binary name '" + name + "'"); + } + } + + public static boolean isJavaBinaryName(CharSequence name) { + return BINARY_NAME_PATTERN.matcher(name).matches(); + } + + + public static T requireParamNotNull(String paramName, T obj) { + if (obj == null) { + throw new NullPointerException("Parameter " + paramName + " is null"); + } + + return obj; + } + + public static AssertionError shouldNotReachHere(String message) { + String prefix = "This should be unreachable"; + message = StringUtils.isBlank(message) ? prefix + : prefix + ": " + message; + return new AssertionError(message); + } + + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaAnnotatableNode.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaAnnotatableNode.java index 277d945b80..e06b80b5c5 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaAnnotatableNode.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaAnnotatableNode.java @@ -7,6 +7,8 @@ package net.sourceforge.pmd.lang.java.ast; import java.util.Collection; import java.util.List; +import org.apache.commons.lang3.StringUtils; + import net.sourceforge.pmd.lang.java.types.TypeTestUtil; // package private @@ -26,11 +28,12 @@ abstract class AbstractJavaAnnotatableNode extends AbstractJavaNode implements A } @Override - public ASTAnnotation getAnnotation(String annotQualifiedName) { + public ASTAnnotation getAnnotation(String binaryName) { + binaryName = StringUtils.deleteWhitespace(binaryName); List annotations = getDeclaredAnnotations(); for (ASTAnnotation annotation : annotations) { ASTName name = annotation.getFirstDescendantOfType(ASTName.class); - if (TypeTestUtil.isA(annotQualifiedName, name)) { + if (TypeTestUtil.isA(binaryName, name)) { return annotation; } } @@ -38,13 +41,13 @@ abstract class AbstractJavaAnnotatableNode extends AbstractJavaNode implements A } @Override - public boolean isAnnotationPresent(String annotQualifiedName) { - return getAnnotation(annotQualifiedName) != null; + public boolean isAnnotationPresent(String binaryName) { + return getAnnotation(binaryName) != null; } @Override - public boolean isAnyAnnotationPresent(Collection annotQualifiedNames) { - for (String annotQualifiedName : annotQualifiedNames) { + public boolean isAnyAnnotationPresent(Collection binaryNames) { + for (String annotQualifiedName : binaryNames) { if (isAnnotationPresent(annotQualifiedName)) { return true; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Annotatable.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Annotatable.java index e85027e5fd..5fc3c58dac 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Annotatable.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Annotatable.java @@ -20,29 +20,31 @@ public interface Annotatable extends JavaNode { List getDeclaredAnnotations(); /** - * Get specific annotaion on this node. + * Returns a specific annotation on this node, or null if absent. * - * @param annotQualifiedName - * qulified name of the annotation. - * @return ASTAnnotaion node if the annotation is present on this node, else null + * @param binaryName + * Binary name of the annotation type. + * Note: for now, canonical names are tolerated, this may be changed in PMD 7. */ - ASTAnnotation getAnnotation(String annotQualifiedName); + ASTAnnotation getAnnotation(String binaryName); /** * Checks whether any annotation is present on this node. * - * @param annotQualifiedNames - * collection that cotains qulified name of annotations. + * @param binaryNames + * Collection that contains binary names of annotations. + * Note: for now, canonical names are tolerated, this may be changed in PMD 7. * @return true if any annotation is present on this node, else false */ - boolean isAnyAnnotationPresent(Collection annotQualifiedNames); + boolean isAnyAnnotationPresent(Collection binaryNames); /** * Checks whether the annotation is present on this node. * - * @param annotQualifiedName - * qulified name of the annotation. + * @param binaryName + * Binary name of the annotation type. + * Note: for now, canonical names are tolerated, this may be changed in PMD 7. * @return true if the annotation is present on this node, else false */ - boolean isAnnotationPresent(String annotQualifiedName); + boolean isAnnotationPresent(String binaryName); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/TypeHelper.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/TypeHelper.java index 6bf9b0bb71..7ecc10d0e8 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/TypeHelper.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/TypeHelper.java @@ -6,6 +6,8 @@ package net.sourceforge.pmd.lang.java.typeresolution; import java.util.Objects; +import org.apache.commons.lang3.StringUtils; + import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.java.symboltable.TypedNameDeclaration; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; @@ -37,7 +39,7 @@ public final class TypeHelper { @Deprecated public static boolean isA(final TypeNode n, final String clazzName) { Objects.requireNonNull(n); - return clazzName != null && TypeTestUtil.isA(clazzName, n); + return clazzName != null && TypeTestUtil.isA(StringUtils.deleteWhitespace(clazzName), n); } /** @@ -52,7 +54,7 @@ public final class TypeHelper { @Deprecated public static boolean isExactlyA(final TypeNode n, final String clazzName) { Objects.requireNonNull(n); - return clazzName != null && TypeTestUtil.isExactlyA(clazzName, n); + return clazzName != null && TypeTestUtil.isExactlyA(StringUtils.deleteWhitespace(clazzName), n); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index b5a8e559bf..7926ecaeda 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang.java.types; import java.lang.reflect.Modifier; import java.util.List; +import net.sourceforge.pmd.internal.util.AssertionUtil; import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; @@ -26,6 +27,8 @@ import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; *
  • Take the node as the second parameter *
  • Systematically return false if the node argument is null *
  • Systematically throw if the other argument is null + *
  • Do not sanitize string arguments, they must be exactly canonical names, + * with no whitespace characters. * */ public final class TypeTestUtil { @@ -57,7 +60,7 @@ public final class TypeTestUtil { * @throws NullPointerException if the class parameter is null */ public static boolean isA(/*@NonNull*/ Class clazz, /*@Nullable*/ TypeNode node) { - requireParamNotNull("class", clazz); + AssertionUtil.requireParamNotNull("class", clazz); if (node == null) { return false; } else if (node.getType() == clazz) { @@ -94,7 +97,7 @@ public final class TypeTestUtil { * @throws NullPointerException if the class name parameter is null */ public static boolean isA(/*@NonNull*/ String canonicalName, /*@Nullable*/ TypeNode node) { - requireParamNotNull("canonicalName", canonicalName); + AssertionUtil.assertValidJavaBinaryName(canonicalName); if (node == null) { return false; } @@ -142,7 +145,7 @@ public final class TypeTestUtil { * @throws NullPointerException if the class parameter is null */ public static boolean isExactlyA(/*@NonNull*/ Class clazz, /*@Nullable*/ TypeNode node) { - requireParamNotNull("class", clazz); + AssertionUtil.requireParamNotNull("class", clazz); if (node == null) { return false; } @@ -174,7 +177,7 @@ public final class TypeTestUtil { * @throws NullPointerException if the class name parameter is null */ public static boolean isExactlyA(/*@Nullable*/ String canonicalName, TypeNode node /*@NonNull*/) { - requireParamNotNull("canonicalName", canonicalName); + AssertionUtil.assertValidJavaBinaryName(canonicalName); if (node == null) { return false; } @@ -193,14 +196,6 @@ public final class TypeTestUtil { } - // this is in AssertionUtil in 7.0 - private static void requireParamNotNull(String name, Object o) { - if (o == null) { - throw new NullPointerException("Parameter '" + name + "' was null"); - } - } - - private static boolean hasNoSubtypes(Class clazz) { // Neither final nor an annotation. Enums & records have ACC_FINAL // Note: arrays have ACC_FINAL, but have subtypes by covariance From d025641b34f734198de3f20d819a5407a4c4a970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 16 Jan 2021 21:25:48 +0100 Subject: [PATCH 054/154] Test for #2454 --- .../bestpractices/xml/UnusedPrivateMethod.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml index 2c3e325c11..6615481676 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml @@ -1640,6 +1640,21 @@ public class UnusedPrivateMethodFP { private void privateBooleanMethod(String s, boolean isTrue) { System.out.println(s); } +} + ]]> + + + #2454 [java] UnusedPrivateMethod violation for disabled annotation in 6.23.0 + + java + .lang.Deprecated + 0 + From 8a191167cf93a6a2e62ac7fdc3b46966799e16c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 16 Jan 2021 21:45:35 +0100 Subject: [PATCH 055/154] Fix xpath functions --- .../pmd/lang/java/xpath/TypeIsExactlyFunction.java | 4 +++- .../net/sourceforge/pmd/lang/java/xpath/TypeIsFunction.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsExactlyFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsExactlyFunction.java index fcfffcb12f..b2a60b8f3b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsExactlyFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsExactlyFunction.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.java.xpath; import java.util.List; +import org.apache.commons.lang3.StringUtils; import org.jaxen.Context; import org.jaxen.Function; import org.jaxen.FunctionCallException; @@ -48,8 +49,9 @@ public class TypeIsExactlyFunction implements Function { * @param fullTypeName The fully qualified name of the class or any supertype * @return True if the type of the node matches, false otherwise. */ - public static boolean typeIsExactly(final Node n, final String fullTypeName) { + public static boolean typeIsExactly(Node n, String fullTypeName) { if (n instanceof TypeNode) { + fullTypeName = StringUtils.deleteWhitespace(fullTypeName); return TypeTestUtil.isExactlyA(fullTypeName, (TypeNode) n); } else { throw new IllegalArgumentException("typeIsExactly function may only be called on a TypeNode."); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsFunction.java index 50ca03e0b0..906cc1ee63 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/xpath/TypeIsFunction.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.java.xpath; import java.util.List; +import org.apache.commons.lang3.StringUtils; import org.jaxen.Context; import org.jaxen.Function; import org.jaxen.FunctionCallException; @@ -48,8 +49,9 @@ public class TypeIsFunction implements Function { * @param fullTypeName The fully qualified name of the class or any supertype * @return True if the type of the node matches, false otherwise. */ - public static boolean typeIs(final Node n, final String fullTypeName) { + public static boolean typeIs(Node n, String fullTypeName) { if (n instanceof TypeNode) { + fullTypeName = StringUtils.deleteWhitespace(fullTypeName); return TypeTestUtil.isA(fullTypeName, (TypeNode) n); } else { throw new IllegalArgumentException("typeIs function may only be called on a TypeNode."); From dbecb80c9e61b44679f148746f10e15cd631eff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 16 Jan 2021 21:50:16 +0100 Subject: [PATCH 056/154] Only use ignored annotation, not lombok one, fix #2876 --- .../rule/bestpractices/UnusedPrivateFieldRule.java | 12 +++++++----- .../rule/bestpractices/xml/UnusedPrivateField.xml | 12 ++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateFieldRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateFieldRule.java index 6cc74ef41f..c60e6ca6a9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateFieldRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateFieldRule.java @@ -42,16 +42,18 @@ public class UnusedPrivateFieldRule extends AbstractLombokAwareRule { @Override public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - boolean classHasLombok = hasLombokAnnotation(node); + if (hasIgnoredAnnotation(node)) { + return super.visit(node, data); + } Map> vars = node.getScope() - .getDeclarations(VariableNameDeclaration.class); + .getDeclarations(VariableNameDeclaration.class); for (Map.Entry> entry : vars.entrySet()) { VariableNameDeclaration decl = entry.getKey(); AccessNode accessNodeParent = decl.getAccessNodeParent(); - if (!accessNodeParent.isPrivate() || isOK(decl.getImage()) || classHasLombok - || hasIgnoredAnnotation((Annotatable) accessNodeParent) - || hasIgnoredAnnotation(node)) { + if (!accessNodeParent.isPrivate() + || isOK(decl.getImage()) + || hasIgnoredAnnotation((Annotatable) accessNodeParent)) { continue; } if (!actuallyUsed(entry.getValue())) { diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml index efd39eb122..5d7bda8a23 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml @@ -636,6 +636,18 @@ public class Foo { + + + #2673 UnusedPrivateField false positive with lombok annotation EqualsAndHashCode + lombok.Getter|lombok.Data + 1 + Date: Sat, 16 Jan 2021 21:56:45 +0100 Subject: [PATCH 057/154] Make unused rules ignore some names, fix #2957 --- docs/pages/release_notes.md | 1 + .../bestpractices/UnusedAssignmentRule.java | 8 ++---- .../UnusedFormalParameterRule.java | 4 ++- .../UnusedLocalVariableRule.java | 5 +++- .../lang/java/rule/internal/JavaRuleUtil.java | 25 +++++++++++++++++++ .../resources/category/java/bestpractices.xml | 8 ++++-- .../bestpractices/xml/UnusedAssignment.xml | 6 ++--- .../xml/UnusedFormalParameter.xml | 15 +++++++++++ .../bestpractices/xml/UnusedLocalVariable.xml | 13 ++++++++++ 9 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 9d2add0d98..da4fb0a7e9 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -20,6 +20,7 @@ This is a {{ site.pmd.release_type }} release. * [#2994](https://github.com/pmd/pmd/pull/2994): \[core] Fix code climate severity strings * java-bestpractices * [#575](https://github.com/pmd/pmd/issues/575): \[java] LiteralsFirstInComparisons should consider constant fields + * [#2957](https://github.com/pmd/pmd/issues/2957): \[java] Ignore unused declarations that have special name ### API Changes diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedAssignmentRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedAssignmentRule.java index 36713aa632..5593a07700 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedAssignmentRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedAssignmentRule.java @@ -78,6 +78,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTYieldStatement; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorAdapter; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; import net.sourceforge.pmd.lang.java.symboltable.ClassScope; import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; import net.sourceforge.pmd.lang.symboltable.Scope; @@ -206,7 +207,7 @@ public class UnusedAssignmentRule extends AbstractJavaRule { } else { reason = joinLines("overwritten on lines ", killers); } - if (reason == null && hasExplicitIgnorableName(entry.var.getName())) { + if (reason == null && JavaRuleUtil.isExplicitUnusedVarName(entry.var.getName())) { // Then the variable is never used (cf UnusedVariable) // We ignore those that start with "ignored", as that is standard // practice for exceptions, and may be useful for resources/foreach vars @@ -217,11 +218,6 @@ public class UnusedAssignmentRule extends AbstractJavaRule { } } - private boolean hasExplicitIgnorableName(String name) { - return name.startsWith("ignored") - || "_".equals(name); // before java 9 it's ok - } - private boolean suppressUnusedVariableRuleOverlap(AssignmentEntry entry) { return !getProperty(REPORT_UNUSED_VARS) && (entry.rhs instanceof ASTVariableInitializer || entry.rhs instanceof ASTVariableDeclaratorId); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java index d2bfba8044..04bdb44bf2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java @@ -24,6 +24,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTType; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; @@ -96,7 +97,8 @@ public class UnusedFormalParameterRule extends AbstractJavaRule { continue; } - if (actuallyUsed(nameDecl, entry.getValue())) { + if (actuallyUsed(nameDecl, entry.getValue()) + || JavaRuleUtil.isExplicitUnusedVarName(nameDecl.getName())) { continue; } addViolation(data, nameDecl.getNode(), new Object[] { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedLocalVariableRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedLocalVariableRule.java index fea5d5696f..3dbe508338 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedLocalVariableRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedLocalVariableRule.java @@ -10,6 +10,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; @@ -29,7 +30,9 @@ public class UnusedLocalVariableRule extends AbstractJavaRule { // TODO this isArray() check misses some cases // need to add DFAish code to determine if an array // is initialized locally or gotten from somewhere else - if (!node.getNameDeclaration().isArray() && !actuallyUsed(node.getUsages())) { + if (!node.getNameDeclaration().isArray() + && !actuallyUsed(node.getUsages()) + && !JavaRuleUtil.isExplicitUnusedVarName(node.getName())) { addViolation(data, node, node.getNameDeclaration().getImage()); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java new file mode 100644 index 0000000000..0082105a3f --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java @@ -0,0 +1,25 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.internal; + +/** + * Utilities shared between rules (note: in pmd 7 this is used much more extensively). + */ +public final class JavaRuleUtil { + + private JavaRuleUtil() { + // utility class + } + + + /** + * Whether the name may be ignored by unused rules like UnusedAssignment. + */ + public static boolean isExplicitUnusedVarName(String name) { + return name.startsWith("ignored") + || name.startsWith("unused") + || "_".equals(name); // before java 9 it's ok + } +} diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 59e5e56c72..98af2c679b 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -1365,7 +1365,7 @@ class Foo{ The rule subsumes {% rule "UnusedLocalVariable" %}, and {% rule "UnusedFormalParameter" %}. Those violations are filtered out by default, in case you already have enabled those rules, but may be enabled with the property - `reportUnusedVariables`. Variables whose name starts with `ignored` are filtered out, as + `reportUnusedVariables`. Variables whose name starts with `ignored` or `unused` are filtered out, as is standard practice for exceptions. Limitations: @@ -1476,10 +1476,13 @@ class C { class="net.sourceforge.pmd.lang.java.rule.bestpractices.UnusedFormalParameterRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#unusedformalparameter"> -Avoid passing parameters to methods or constructors without actually referencing them in the method body. +Reports parameters of methods and constructors that are not referenced them in the method body. +Parameters whose name starts with `ignored` or `unused` are filtered out. + Removing unused formal parameters from public methods could cause a ripple effect through the code base. Hence, by default, this rule only considers private methods. To include non-private methods, set the `checkAll` property to `true`. + 3 @@ -1523,6 +1526,7 @@ public class Foo {} externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#unusedlocalvariable"> Detects when a local variable is declared and/or assigned, but not used. +Variables whose name starts with `ignored` or `unused` are filtered out. 3 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml index 8b100336f0..e3019ed865 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml @@ -2797,10 +2797,10 @@ class Foo { Test ignored name 0 true - 1 + 2 0 + + + #2957 Unused rules should ignore some names + 2 + 2,3 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedLocalVariable.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedLocalVariable.xml index 4c7aadbb11..3ff1770ef1 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedLocalVariable.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedLocalVariable.xml @@ -378,6 +378,19 @@ public class UnusedLocalVariable { System.out.println("foo!"); } } +} + ]]> + + + #2957 Unused rules should ignore some names + 1 + 4 + From 2f94ee603a9d84db9a714b8ecaf5a32482c14089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 16 Jan 2021 22:10:00 +0100 Subject: [PATCH 058/154] Checkstyle --- .../java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index 7926ecaeda..3f8f437e9b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -60,7 +60,7 @@ public final class TypeTestUtil { * @throws NullPointerException if the class parameter is null */ public static boolean isA(/*@NonNull*/ Class clazz, /*@Nullable*/ TypeNode node) { - AssertionUtil.requireParamNotNull("class", clazz); + AssertionUtil.requireParamNotNull("class", clazz); if (node == null) { return false; } else if (node.getType() == clazz) { From 5dbff1ab8c188211269b762d622764d924273ba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 16 Jan 2021 23:12:58 +0100 Subject: [PATCH 059/154] Fix PMDTaskTest --- .../src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java | 4 ++-- pmd-java/src/test/resources/ant/java/EncodingTestClass.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java index 82802e409c..3cc5d614ce 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java @@ -136,7 +136,7 @@ public class PMDTaskTest extends AbstractAntTestHelper { executeTarget("testFormatterEncodingWithXML"); String report = FileUtils.readFileToString(currentTempFile(), "UTF-8"); - assertTrue(report.contains("unusedVariableWithÜmlaut")); + assertTrue(report.contains("someVariableWithÜmlaut")); } private static String convert(String report) { @@ -160,7 +160,7 @@ public class PMDTaskTest extends AbstractAntTestHelper { executeTarget("testFormatterEncodingWithXMLConsole"); String report = convert(buildRule.getOutput()); assertTrue(report.startsWith("")); - assertTrue(report.contains("unusedVariableWithÜmlaut")); + assertTrue(report.contains("someVariableWithÜmlaut")); } @Test diff --git a/pmd-java/src/test/resources/ant/java/EncodingTestClass.java b/pmd-java/src/test/resources/ant/java/EncodingTestClass.java index 617c682f1d..e731dbae24 100644 --- a/pmd-java/src/test/resources/ant/java/EncodingTestClass.java +++ b/pmd-java/src/test/resources/ant/java/EncodingTestClass.java @@ -4,6 +4,6 @@ public class EncodingTestClass { // so, the encoding matters // NOTE: This file is stored with encoding windows-1252 or cp1252 // The Umlaut Ü has codepoint U+00DC, which is the same in cp1252 and iso-8859-1 - String unusedVariableWithÜmlaut = "unused"; + String someVariableWithÜmlaut = "unused"; } } From 87f8ca8e9dcd4625e46f1967587b07396e7e1441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 17 Jan 2021 17:36:29 +0100 Subject: [PATCH 060/154] Deprecate xml base rule classes Refs #2971 --- docs/pages/release_notes.md | 5 +++++ .../net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRule.java | 3 +++ .../sourceforge/pmd/lang/xml/rule/AbstractDomXmlRule.java | 3 +++ 3 files changed, 11 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 9d2add0d98..477d87f0a9 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -23,6 +23,11 @@ This is a {{ site.pmd.release_type }} release. ### API Changes +#### Deprecated API + +* {% jdoc xml::lang.xml.rule.AbstractDomXmlRule %} +* {% jdoc xml::lang.wsdl.rule.AbstractWsdlRule %} + ### External Contributions * [#2964](https://github.com/pmd/pmd/pull/2964): \[cs] Update C# grammar for additional C# 7 and C# 8 features - [Maikel Steneker](https://github.com/maikelsteneker) diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRule.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRule.java index e348d84583..9731209938 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRule.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRule.java @@ -10,7 +10,10 @@ import net.sourceforge.pmd.lang.xml.rule.AbstractXmlRule; /** * Created by bernardo-macedo on 24.06.15. + * + * @deprecated Extend {@link AbstractXmlRule} */ +@Deprecated public class AbstractWsdlRule extends AbstractXmlRule { public AbstractWsdlRule() { diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRule.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRule.java index 13f3ebb65a..e4e6225c0c 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRule.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRule.java @@ -25,7 +25,10 @@ import net.sourceforge.pmd.lang.xml.ast.XmlNode; * This is a base class for XML Java bases rules that which to visit the nodes * using the DOM. Subclasses should override the DOM appropriate method and can * call super to visit children. + * + * @deprecated Not very useful, use XPath rules. */ +@Deprecated public class AbstractDomXmlRule extends AbstractXmlRule { @Override From 0c5bd89033bda8b70d790daa20cf7b167ce131aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 17 Jan 2021 17:39:16 +0100 Subject: [PATCH 061/154] Also deprecate useless visit methods --- docs/pages/release_notes.md | 1 + .../net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 477d87f0a9..6118b68cc3 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -27,6 +27,7 @@ This is a {{ site.pmd.release_type }} release. * {% jdoc xml::lang.xml.rule.AbstractDomXmlRule %} * {% jdoc xml::lang.wsdl.rule.AbstractWsdlRule %} +* A few methods of {% jdoc xml::lang.xml.rule.AbstractXmlRule %} ### External Contributions diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java index ec76a99611..e7c0b4a27f 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java @@ -70,6 +70,8 @@ public class AbstractXmlRule extends AbstractRule implements ImmutableLanguage { visitAll(nodes, ctx); } + /** @deprecated Not useful, will be removed in PMD 7. */ + @Deprecated protected void visitAll(List nodes, RuleContext ctx) { for (Object element : nodes) { if (element instanceof XmlNode) { @@ -78,6 +80,8 @@ public class AbstractXmlRule extends AbstractRule implements ImmutableLanguage { } } + /** @deprecated Not useful, will be removed in PMD 7. */ + @Deprecated protected void visit(XmlNode node, RuleContext ctx) { final int numChildren = node.getNumChildren(); for (int i = 0; i < numChildren; i++) { From b638bbb549e77a2cb920f005e4fa55234b6e0868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 17 Jan 2021 17:55:14 +0100 Subject: [PATCH 062/154] Fix #3006: [java] NPE in SingularFieldRule with concise resource syntax --- .../java/rule/design/SingularFieldRule.java | 11 ++++-- .../java/rule/design/xml/SingularField.xml | 34 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SingularFieldRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SingularFieldRule.java index ce1777ce48..e82d04763f 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SingularFieldRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SingularFieldRule.java @@ -95,8 +95,15 @@ public class SingularFieldRule extends AbstractLombokAwareRule { NameOccurrence no = usages.get(ix); Node location = no.getLocation(); - ASTPrimaryExpression primaryExpressionParent = location - .getFirstParentOfType(ASTPrimaryExpression.class); + ASTPrimaryExpression primaryExpressionParent = location.getFirstParentOfType(ASTPrimaryExpression.class); + + if (primaryExpressionParent == null) { + // concise resource `try(field) {...}`, in pmd 7 + // there will be an expression there + violation = false; + break; + } + if (ix == 0 && !disallowNotAssignment) { if (primaryExpressionParent.getFirstParentOfType(ASTIfStatement.class) != null) { // the first usage is in an if, so it may be skipped diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SingularField.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SingularField.xml index ef7f6bb6f1..b019b5c206 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SingularField.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SingularField.xml @@ -691,4 +691,38 @@ public class Foo { } ]]> + + #3006 [java] NPE in SingularFieldRule with concise resource syntax + false + 0 + + + + #3006 [java] NPE in SingularFieldRule with concise resource syntax (disallowNotAssignment) + true + 0 + + From ddec921e38c6d67be2c3fbdf03ab29e7e9d0e381 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sun, 17 Jan 2021 18:31:33 +0100 Subject: [PATCH 063/154] [apex] Fixes from PR #2959 - Update description of new rule OverrideBothEqualsAndHashcode - add to quickstart (commented out) - use case insensitive comparison --- .../OverrideBothEqualsAndHashcodeRule.java | 6 +- .../resources/category/apex/errorprone.xml | 99 ++++++++++--------- .../resources/rulesets/apex/quickstart.xml | 1 + .../xml/OverrideBothEqualsAndHashcode.xml | 6 +- 4 files changed, 59 insertions(+), 53 deletions(-) diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java index c63367e7dc..29d90ad523 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ @@ -51,7 +51,7 @@ public class OverrideBothEqualsAndHashcodeRule extends AbstractApexRule { paramType = ((ASTParameter) sn).getType(); } } - return numParams == 1 && node.hasImageEqualTo("equals") && "Object".equalsIgnoreCase(paramType); + return numParams == 1 && "equals".equalsIgnoreCase(node.getImage()) && "Object".equalsIgnoreCase(paramType); } private boolean isHashCode(ASTMethod node) { @@ -63,6 +63,6 @@ public class OverrideBothEqualsAndHashcodeRule extends AbstractApexRule { } } - return numParams == 0 && node.hasImageEqualTo("hashCode"); + return numParams == 0 && "hashCode".equalsIgnoreCase(node.getImage()); } } diff --git a/pmd-apex/src/main/resources/category/apex/errorprone.xml b/pmd-apex/src/main/resources/category/apex/errorprone.xml index a9d56f5d04..66779d65b6 100644 --- a/pmd-apex/src/main/resources/category/apex/errorprone.xml +++ b/pmd-apex/src/main/resources/category/apex/errorprone.xml @@ -111,6 +111,30 @@ public without sharing class Foo { + + + Apex supported non existent annotations for legacy reasons. + In the future, use of such non-existent annotations could result in broken apex code that will not compile. + This will prevent users of garbage annotations from being able to use legitimate annotations added to Apex in the future. + A full list of supported annotations can be found at https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation.htm + + 3 + + + + + - - - - - -Override both public boolean Object.equals(Object other), and public int Object.hashCode(), or override neither. Even if you are inheriting a hashCode() from a parent class, consider implementing hashCode and explicitly delegating to your superclass. - - 3 - - @@ -351,24 +341,39 @@ public class MyClass { - + since="6.31.0" + message="Ensure you override both equals() and hashCode()" + class="net.sourceforge.pmd.lang.apex.rule.errorprone.OverrideBothEqualsAndHashcodeRule" + externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#overridebothequalsandhashcode"> - Apex supported non existent annotations for legacy reasons. - In the future, use of such non-existent annotations could result in broken apex code that will not compile. - This will prevent users of garbage annotations from being able to use legitimate annotations added to Apex in the future. - A full list of supported annotations can be found at https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation.htm +Override both `public Boolean equals(Object obj)`, and `public Integer hashCode()`, or override neither. +Even if you are inheriting a hashCode() from a parent class, consider implementing hashCode and explicitly +delegating to your superclass. + +This is especially important when [Using Custom Types in Map Keys and Sets](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_collections_maps_keys_userdefined.htm). 3 - diff --git a/pmd-apex/src/main/resources/rulesets/apex/quickstart.xml b/pmd-apex/src/main/resources/rulesets/apex/quickstart.xml index 10b0ddcfcc..c0842d8641 100644 --- a/pmd-apex/src/main/resources/rulesets/apex/quickstart.xml +++ b/pmd-apex/src/main/resources/rulesets/apex/quickstart.xml @@ -472,5 +472,6 @@ + diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml index c5305e28e9..d321c92777 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml @@ -37,12 +37,12 @@ public class Foo { - nested equals only + nested equals only, checking case insensitiveness 1 @@ -106,7 +106,7 @@ public interface Foo { - implements equals but with 2 args + implements equals but with 2 args, hashCode overloaded as well 0 Date: Sun, 17 Jan 2021 18:32:16 +0100 Subject: [PATCH 064/154] [doc] Update release notes, refs #2959 --- docs/pages/release_notes.md | 7 +++++++ .../src/main/resources/rulesets/releases/6310.xml | 13 +++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 pmd-core/src/main/resources/rulesets/releases/6310.xml diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 9d7eb1bf77..1e338c77d0 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -14,6 +14,12 @@ This is a {{ site.pmd.release_type }} release. ### New and noteworthy +#### New Rules + +* The new Apex rule {% rule "apex/errorprone/OverrideBothEqualsAndHashcode" %} brings the well known Java rule + to Apex. In Apex the same principle applies: `equals` and `hashCode` should always be overridden + together to ensure collection classes such as Maps and Sets work as expected. + ### Fixed Issues * core @@ -28,6 +34,7 @@ This is a {{ site.pmd.release_type }} release. ### External Contributions +* [#2959](https://github.com/pmd/pmd/pull/2959): \[apex] New Rule: override equals and hashcode rule - [recdevs](https://github.com/recdevs) * [#2964](https://github.com/pmd/pmd/pull/2964): \[cs] Update C# grammar for additional C# 7 and C# 8 features - [Maikel Steneker](https://github.com/maikelsteneker) * [#2983](https://github.com/pmd/pmd/pull/2983): \[java] LiteralsFirstInComparisons should consider constant fields - [Ozan Gulle](https://github.com/ozangulle) * [#2994](https://github.com/pmd/pmd/pull/2994): \[core] Fix code climate severity strings - [Vincent Maurin](https://github.com/vmaurin) diff --git a/pmd-core/src/main/resources/rulesets/releases/6310.xml b/pmd-core/src/main/resources/rulesets/releases/6310.xml new file mode 100644 index 0000000000..eaf61d2469 --- /dev/null +++ b/pmd-core/src/main/resources/rulesets/releases/6310.xml @@ -0,0 +1,13 @@ + + + + +This ruleset contains links to rules that are new in PMD v6.31.0 + + + + + From 7e9f17658c8b9fbd564bcb57cf37c27436188657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 18 Jan 2021 14:54:33 +0100 Subject: [PATCH 065/154] Fixups for #2666 Support full swift 5 string grammar - arbitrary balanced delimiters - raw strings ignore escapes - multiline raw strings Also rename lexer rules, 'dashed' is not adapted --- .../sourceforge/pmd/lang/swift/antlr4/Swift.g4 | 18 ++++++++---------- .../pmd/lang/swift/cpd/testdata/Swift5.0.swift | 7 ++++++- .../pmd/lang/swift/cpd/testdata/Swift5.0.txt | 15 ++++++++++----- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 b/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 index c71b5fbda7..2c44bc62f2 100644 --- a/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 +++ b/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 @@ -1018,7 +1018,7 @@ ImplicitParameterName : '$' DecimalLiteral ; // TODO: don't allow '_' here // GRAMMAR OF A LITERAL booleanLiteral: BooleanLiteral ; -literal : numericLiteral | MultiStringLiteral | DashedSingleStringLiteral | SingleStringLiteral | BooleanLiteral | NilLiteral ; +literal : numericLiteral | MultiStringLiteral | SingleStringLiteral | BooleanLiteral | NilLiteral | RawMultiStringLiteral | RawSingleStringLiteral ; // GRAMMAR OF AN INTEGER LITERAL @@ -1073,17 +1073,15 @@ TRIPLEDQUOTES : '"""' ; MultiStringLiteral : TRIPLEDQUOTES '\n' .*? '\n' TRIPLEDQUOTES ; fragment MultiQuotedText : MultiQuotedTextItem+ ; -fragment MultiQuotedTextItem : MultiInterpolatedString - | ~[\\\u000A\u000D] - ; +fragment MultiQuotedTextItem : MultiInterpolatedString | ~[\\\u000A\u000D] ; fragment MultiInterpolatedString: '\\(' (MultiQuotedTextItem | SingleStringLiteral)* ')'; -DashedSingleStringLiteral : '#"' DashedMultiQuotedText? '"#' ; -fragment DashedMultiQuotedText : DashedMultiQuotedTextItem+ ; -fragment DashedMultiQuotedTextItem : EscapedCharacter | DashedMultiInterpolatedString - | ~[\\\u000A\u000D] - ; -fragment DashedMultiInterpolatedString: '\\#(' (DashedMultiQuotedTextItem | DashedSingleStringLiteral)* ')'; +// swift 5 extended delimiter, eg ##"abc"## +RawSingleStringLiteral : '#"' RawSingleQuotedTextItem* '"#' | '#' RawSingleStringLiteral '#'; +fragment RawSingleQuotedTextItem : ~[\u000A\u000D] ; + +RawMultiStringLiteral : '#"""' RawMultiQuotedTextItem* '"""#' | '#' RawMultiStringLiteral '#'; +fragment RawMultiQuotedTextItem : . ; // StringLiteral : '"' QuotedText? '"' ; SingleStringLiteral : '"' QuotedText? '"' ; diff --git a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift index cf326c21c7..85bd015309 100644 --- a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift +++ b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift @@ -43,6 +43,11 @@ print(x[keyPath: id]) // prints 3 let rawString = #"Press "Continue" to close this dialog."# extension URL { func html(withTitle title: String) -> String { - return #"\#(title)"# + return ##"\#(title)"## } } + +let rawMultiString = ###"a\###"### +let rawMultiString2 = ###"""a\### +""hey"" +"""### diff --git a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt index 0a89406a83..cb66d0c607 100644 --- a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt +++ b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt @@ -220,14 +220,19 @@ L45 [{] 50 50 L46 [return] 9 14 - [#"\\[ 16 61 + [##" Date: Mon, 18 Jan 2021 15:18:05 +0100 Subject: [PATCH 066/154] Update release notes, refs #2666 --- docs/pages/release_notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 1e338c77d0..2ab681546c 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -34,6 +34,7 @@ This is a {{ site.pmd.release_type }} release. ### External Contributions +* [#2666](https://github.com/pmd/pmd/pull/2666): \[swift] Manage swift5 string literals - [kenji21](https://github.com/kenji21) * [#2959](https://github.com/pmd/pmd/pull/2959): \[apex] New Rule: override equals and hashcode rule - [recdevs](https://github.com/recdevs) * [#2964](https://github.com/pmd/pmd/pull/2964): \[cs] Update C# grammar for additional C# 7 and C# 8 features - [Maikel Steneker](https://github.com/maikelsteneker) * [#2983](https://github.com/pmd/pmd/pull/2983): \[java] LiteralsFirstInComparisons should consider constant fields - [Ozan Gulle](https://github.com/ozangulle) From 8759ccdce47b9bec3c2761cda3413c3609a75186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 18 Jan 2021 15:32:51 +0100 Subject: [PATCH 067/154] Update release notes, refs #2965 --- docs/pages/release_notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 2ab681546c..cfa32a64ae 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -37,6 +37,7 @@ This is a {{ site.pmd.release_type }} release. * [#2666](https://github.com/pmd/pmd/pull/2666): \[swift] Manage swift5 string literals - [kenji21](https://github.com/kenji21) * [#2959](https://github.com/pmd/pmd/pull/2959): \[apex] New Rule: override equals and hashcode rule - [recdevs](https://github.com/recdevs) * [#2964](https://github.com/pmd/pmd/pull/2964): \[cs] Update C# grammar for additional C# 7 and C# 8 features - [Maikel Steneker](https://github.com/maikelsteneker) +* [#2965](https://github.com/pmd/pmd/pull/2965): \[cs] Improvements for ignore sequences of literals functionality - [Maikel Steneker](https://github.com/maikelsteneker) * [#2983](https://github.com/pmd/pmd/pull/2983): \[java] LiteralsFirstInComparisons should consider constant fields - [Ozan Gulle](https://github.com/ozangulle) * [#2994](https://github.com/pmd/pmd/pull/2994): \[core] Fix code climate severity strings - [Vincent Maurin](https://github.com/vmaurin) From 615b33b8b1d259adcc051f692c900515e5616cb7 Mon Sep 17 00:00:00 2001 From: Manuel Moya Date: Mon, 18 Jan 2021 16:32:27 +0100 Subject: [PATCH 068/154] Basic sarif renderer with unit tests --- .DS_Store | Bin 0 -> 10244 bytes pmd-core/pom.xml | 11 + .../pmd/renderers/RendererFactory.java | 1 + .../sourceforge/pmd/renderers/SarifLog.java | 328 ++++++++++++++++++ .../pmd/renderers/SarifRenderer.java | 170 +++++++++ .../pmd/renderers/SarifRendererTest.java | 76 ++++ .../pmd/renderers/sarif/empty.sarif.json | 17 + .../sarif/expected-multiple.sarif.json | 85 +++++ .../pmd/renderers/sarif/expected.sarif.json | 52 +++ pmd-dist/.DS_Store | Bin 0 -> 6148 bytes pom.xml | 6 + 11 files changed, 746 insertions(+) create mode 100644 .DS_Store create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java create mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java create mode 100644 pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/empty.sarif.json create mode 100644 pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json create mode 100644 pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected.sarif.json create mode 100644 pmd-dist/.DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a2e856bf7d9877725528b9bd357de4a5228d83ae GIT binary patch literal 10244 zcmeHM-EQMV6h3Z4>!d9$tCe=S^9|Bov{fsXYAdu-$hJb2fJLPINPxPI<2LR(ad4cZ z-EL8Ccn1V80B?eL0bYT79ss^Gc0E6FuR(NNd1m5#XXbq0IWx{oiO6Y=|Ca20H|wtEH1RM4-ht9Q8$Zvme;}*edfCdr!aMj zEryeu<9mjo{Z7T#2J9u$FD{o>-3DC zYKEwFz#j$dNU|2g34C@B`^)ccZd`RIzL@|ZB*Uk@y%^LhmXbbp|(Xbz(+(y|m zEdMerYr-<1k&Y9({HW-%jPs8W=O2~+5G$afS9LTziQ!*BFZEFbchzng(K>>9hfuFg zhX$HitLPg1nLw$jfn$y;&b`6Gj{t2W0?gHM6|PGd{ke!4Jkk4RrW)3zA@>OFeY8Tl z(DBSr!@p_bVR;U1Qj`@@)@NwSm{X@ zyi>~bz6I;s@DZ}o#3Qn|_Al_A>1bHY+TT0FI2vU;JAXI}i?uhFmR+~uHr5*7wtLC6 zo%z{#)bi7>`O~L<*6S4Q<0Og8w%_?83a2L<_n#$$%#Q|PDvYCU2*@{IMuToLZ6)JD zcO?30=U=#P-L0Q&ynS`Gx4XaTy}SSTdeghw-Fa_s)7#tMzrL=!58l~*di0`y`ReNR z_4hv^4{^DTB?$Q>7<mu&Kz-7=an+zry@f##0G1L zZY3iBXJzEiC^4`cc@^6zM>s>@3H~N@soRL?t2UD3IlP&WZ{RulD!yKhQQ+x`VZppr z3puN9EJm2=Db~)RnQP2`;VXr`RQMd3Y0eVx*U*yxOK{Jyu0^K4%wMxvEBVA64Xo^2 zuneo`fo3Uvi}C(3b%P2TTKd+E$YB-K-evX-!DU3>nfTYc`TYipX@T=75HmUU6pn9GiL8n#EgXWJ(L@z-))gBd#k|zTmh$c&^~B^2.8.5 + + org.projectlombok + lombok + provided + net.sourceforge.saxon @@ -190,6 +195,12 @@ system-rules test + + com.fasterxml.jackson.core + jackson-annotations + 2.6.0 + compile + diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java index 18abb8a29d..4159f6c37f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java @@ -44,6 +44,7 @@ public final class RendererFactory { map.put(VBHTMLRenderer.NAME, VBHTMLRenderer.class); map.put(EmptyRenderer.NAME, EmptyRenderer.class); map.put(JsonRenderer.NAME, JsonRenderer.class); + map.put(SarifRenderer.NAME, SarifRenderer.class); REPORT_FORMAT_TO_RENDERER = Collections.unmodifiableMap(map); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java new file mode 100644 index 0000000000..9117411889 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java @@ -0,0 +1,328 @@ +package net.sourceforge.pmd.renderers; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.List; + +@Data +@Accessors(chain = true) +public class SarifLog { + + /** + * The URI of the JSON schema corresponding to the version. + */ + private String schema = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"; + + /** + * The SARIF format version of this log file. + */ + private String version = "2.1.0"; + + /** + * The set of runs contained in this log file. + */ + private List runs; + + /** + * A location within a programming artifact. + */ + @Data + @Accessors(chain = true) + public static class Location { + + /** + * Value that distinguishes this location from all other locations within a single result object. + */ + private Integer id; + + /** + * Identifies the artifact and region. + */ + private PhysicalLocation physicalLocation; + } + + /** + * Specifies the location of an artifact. + */ + @Data + public static class ArtifactLocation { + + /** + * A string containing a valid relative or absolute URI. + */ + private String uri; + + /** + * A string which indirectly specifies the absolute URI with respect to which a relative URI in the "uri" property + * is interpreted. + */ + private String uriBaseId; + + /** + * The index within the run artifacts array of the artifact object associated with the artifact location. + */ + private Integer index; + } + + + /** + * A physical location relevant to a result. Specifies a reference to a programming artifact together with a range + * of bytes or characters within that artifact. + */ + @Data + @Accessors(chain = true) + public static class PhysicalLocation { + + /** + * The location of the artifact. + */ + private ArtifactLocation artifactLocation; + + /** + * Specifies a portion of the artifact. + */ + private Region region; + } + + /** + * Key/value pairs that provide additional information about the object. + */ + @Data + public static class PropertyBag { + + /** + * The name of the rule set. + */ + private String ruleset; + + /** + * The pmd priority of the rule. + */ + private Integer priority; + } + + /** + * A region within an artifact where a result was detected. + */ + @Data + @Accessors(chain = true) + public static class Region { + + /** + * The line number of the first character in the region. + */ + private Integer startLine; + + /** + * The column number of the first character in the region. + */ + private Integer startColumn; + + /** + * The line number of the last character in the region. + */ + private Integer endLine; + + /** + * The column number of the character following the end of the region. + */ + private Integer endColumn; + } + + /** + * A result produced by an analysis tool. + */ + @Data + @Accessors(chain = true) + public static class Result { + + /** + * The stable, unique identifier of the rule, if any, to which this result is relevant. + */ + private String ruleId; + + /** + * The index link the rule, if any, to which this result is relevant. + */ + private Integer ruleIndex; + + /** + * A message that describes the result. The first sentence of the message only will be displayed when visible + * space is limited. + */ + private Message message; + + /** + * The set of locations where the result was detected. Specify only one location unless the problem indicated by + * the result can only be corrected by making a change at every specified location. + */ + private List locations; + + /** + * Key/value pairs that provide additional information about the address. + */ + private PropertyBag properties; + } + + /** + * Encapsulates a message intended to be read by the end user. + */ + @Data + @Accessors(chain = true) + public static class Message { + + /** + * A plain text message string. + */ + private String text; + + /** + * A Markdown message string. + */ + private String markdown; + + /** + * The identifier for this message. + */ + private String id; + } + + /** + * Describes a single run of an analysis tool, and contains the reported output of that run. + */ + @Data + @Accessors(chain = true) + public static class Run { + + /** + * Information about the tool or tool pipeline that generated the results in this run. A run can only contain + * results produced by a single tool or tool pipeline. A run can aggregate results from multiple log files, as long + * as context around the tool run (tool command-line arguments and the like) is identical for all aggregated files. + */ + private Tool tool; + + /** + * The set of results contained in an SARIF log. The results array can be omitted when a run is solely exporting + * rules metadata. It must be present (but may be empty) if a log file represents an actual scan. + */ + private List results; + } + + /** + * The analysis tool that was run. + */ + @Data + @Accessors(chain = true) + public static class Tool { + + /** + * The analysis tool that was run. + */ + private Component driver; + } + + /** + * A component, such as a plug-in or the driver, of the analysis tool that was run. + */ + @Data + public static class Component { + /** + * The name of the tool component. + */ + private String name; + + /** + * The tool component version, in whatever format the component natively provides. + */ + private String version; + + /** + * The absolute URI at which information about this version of the tool component can be found. + */ + private String informationUri; + + /** + * An array of reportingDescriptor objects relevant to the analysis performed by the tool component. + */ + private List rules; + } + + /** + * Metadata that describes a specific report produced by the tool, as part of the analysis it provides or its runtime + * reporting. + */ + @Data + @Accessors(chain = true) + public static class ReportingDescriptor { + + /** + * A stable, opaque identifier for the report. + */ + private String id; + + /** + * A report identifier that is understandable to an end user. + */ + private String name; + + /** + * A concise description of the report. Should be a single sentence that is understandable when visible space is + * limited to a single line of text. + */ + private MultiformatMessage shortDescription; + + /** + * A description of the report. Should, as far as possible, provide details sufficient to enable resolution of any + * problem indicated by the result. + */ + private MultiformatMessage fullDescription; + + /** + * A set of name/value pairs with arbitrary names. Each value is a multiformatMessageString object, which holds + * message strings in plain text and (optionally) Markdown format. The strings can include placeholders, which can + * be used to construct a message in combination with an arbitrary number of additional string arguments. + */ + private MultiformatMessage messageStrings; + + /** + * A URI where the primary documentation for the report can be found. + */ + private String helpUri; + + /** + * Provides the primary documentation for the report, useful when there is no online documentation. + */ + private MultiformatMessage help; + + /** + * Key/value pairs that provide additional information about the report. + */ + private PropertyBag properties; + } + + /** + * A message string or message format string rendered in multiple formats. + */ + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class MultiformatMessage { + + /** + * A plain text message string or format string. + */ + private String text; + + /** + * A Markdown message string or format string. + */ + private String markdown; + + public MultiformatMessage(String text) { + this.text = text; + } + } +} \ No newline at end of file diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java new file mode 100644 index 0000000000..52bcd12954 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java @@ -0,0 +1,170 @@ +package net.sourceforge.pmd.renderers; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import net.sourceforge.pmd.PMDVersion; +import net.sourceforge.pmd.RuleViolation; + +import java.io.IOException; +import java.util.*; + +public class SarifRenderer extends AbstractIncrementingRenderer { + public static final String NAME = "sarif"; + + private Gson gson; + private SarifLog sarifLog; + SarifLog.Component driver; + private List violatedRules; + private SarifLog.Run run; + private List results; + + public SarifRenderer() { + super(NAME, "Sarif integration."); + } + + @Override + public String defaultFileExtension() { + return "sarif.json"; + } + + @Override + public void start() throws IOException { + gson = new GsonBuilder() + .disableHtmlEscaping() + .setPrettyPrinting() + .create(); + + run = new SarifLog.Run(); + defineRunTool(); + + sarifLog = new SarifLog(); + violatedRules = new LinkedList<>(); + results = new LinkedList<>(); + } + + @Override + public void renderFileViolations(Iterator violations) throws IOException { + + while (violations.hasNext()) { + RuleViolation rv = violations.next(); + + Integer violatedRuleIndex = getRuleViolationIndex(rv); + if (violatedRuleIndex != -1) { + SarifLog.Result existingResult = getResultByRuleIndex(violatedRuleIndex); + + List locations = existingResult.getLocations(); + locations.add(getRuleViolationLocation(rv)); + existingResult.setLocations(locations); + } else { + SarifLog.ReportingDescriptor rule = createReportingDescriptor(rv); + violatedRules.add(rule); + violatedRuleIndex = violatedRules.indexOf(rule); + + SarifLog.Result result = new SarifLog.Result(); + result.setRuleId(rv.getRule().getName()); + result.setRuleIndex(violatedRuleIndex); + + SarifLog.Message message = new SarifLog.Message(); + message.setText(rv.getDescription()); + result.setMessage(message); + + List locations = new LinkedList<>(); + locations.add(getRuleViolationLocation(rv)); + result.setLocations(locations); + + results.add(result); + } + } + } + + @Override + public void end() throws IOException { + if (errors.isEmpty() && configErrors.isEmpty()) { + driver.setRules(violatedRules); + run.setResults(results); + + List runs = new LinkedList<>(); + runs.add(run); + sarifLog.setRuns(runs); + + String json = gson.toJson(sarifLog); + + writer.write(json); + } + } + + private void defineRunTool() { + driver = new SarifLog.Component(); + driver.setName("PMD"); // to improve + driver.setVersion(PMDVersion.VERSION); + driver.setInformationUri("https://github.com/pmd"); // to improve + + SarifLog.Tool tool = new SarifLog.Tool(); + tool.setDriver(driver); + + run.setTool(tool); + } + + private Integer getRuleViolationIndex(RuleViolation rv) { + Integer result = -1; + for (SarifLog.ReportingDescriptor rule : violatedRules) { + if (rule.getId() == rv.getRule().getName()) { + result = violatedRules.indexOf(rule); + } + } + return result; + } + + private SarifLog.ReportingDescriptor createReportingDescriptor(RuleViolation rv) { + SarifLog.ReportingDescriptor result = new SarifLog.ReportingDescriptor(); + result.setId(rv.getRule().getName()); + result.setName(rv.getRule().getName()); + result.setShortDescription(new SarifLog.MultiformatMessage(rv.getDescription())); + result.setHelpUri(rv.getRule().getExternalInfoUrl()); + result.setProperties(getRuleProperties(rv)); + + return result; + } + + private SarifLog.Result getResultByRuleIndex(Integer ruleIndex) { + SarifLog.Result result = null; + + for (SarifLog.Result res : results) { + if (res.getRuleIndex() == ruleIndex) { + result = res; + } + } + return result; + } + + private SarifLog.Location getRuleViolationLocation(RuleViolation rv) { + SarifLog.ArtifactLocation artifactLocation = new SarifLog.ArtifactLocation(); + artifactLocation.setUri(rv.getFilename()); + + SarifLog.Region region = new SarifLog.Region(); + region.setStartLine(rv.getBeginLine()); + region.setEndLine(rv.getEndLine()); + region.setStartColumn(rv.getBeginColumn()); + region.setEndColumn(rv.getEndColumn()); + + SarifLog.PhysicalLocation physicalLocation = new SarifLog.PhysicalLocation(); + physicalLocation.setArtifactLocation(artifactLocation); + physicalLocation.setRegion(region); + + SarifLog.Location location = new SarifLog.Location(); + location.setPhysicalLocation(physicalLocation); + + return location; + } + + private SarifLog.PropertyBag getRuleProperties(RuleViolation rv) { + SarifLog.PropertyBag propertyBag = new SarifLog.PropertyBag(); + + propertyBag.setRuleset(rv.getRule().getRuleSetName()); + propertyBag.setPriority(rv.getRule().getPriority().getPriority()); + + return propertyBag; + } + + +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java new file mode 100644 index 0000000000..071ab6e1ab --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java @@ -0,0 +1,76 @@ +package net.sourceforge.pmd.renderers; + +import static org.junit.Assert.assertEquals; + +import net.sourceforge.pmd.*; +import net.sourceforge.pmd.lang.ast.DummyNode; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.rule.AbstractRule; +import net.sourceforge.pmd.lang.rule.ParametricRuleViolation; + +import org.apache.commons.io.IOUtils; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +public class SarifRendererTest extends AbstractRendererTest{ + @Override + public Renderer getRenderer() { + return new SarifRenderer(); + } + + @Override + public String getExpected() { + return readFile("expected.sarif.json"); + } + + @Override + public String getExpectedEmpty() { + return readFile("empty.sarif.json"); + } + + @Override + public String getExpectedMultiple() { + return readFile("expected-multiple.sarif.json"); + } + + @Override + @Test + public void testRendererMultiple() throws Exception { + Report rep = reportTwoViolations(); + String actual = ReportTest.render(getRenderer(), rep); + assertEquals(filter(getExpectedMultiple()), filter(actual)); + } + + private Report reportTwoViolations() { + Report report = new Report(); + RuleViolation informationalRuleViolation = newRuleViolation(1, "Foo"); + informationalRuleViolation.getRule().setPriority(RulePriority.LOW); + report.addRuleViolation(informationalRuleViolation); + RuleViolation severeRuleViolation = newRuleViolation(2, "Boo"); + severeRuleViolation.getRule().setPriority(RulePriority.HIGH); + report.addRuleViolation(severeRuleViolation); + return report; + } + + private RuleViolation newRuleViolation(int endColumn, String ruleName) { + DummyNode node = createNode(endColumn); + RuleContext ctx = new RuleContext(); + ctx.setSourceCodeFile(new File(getSourceCodeFilename())); + AbstractRule fooRule = new FooRule(); + fooRule.setName(ruleName); + return new ParametricRuleViolation(fooRule, ctx, node, "blah"); + } + + private String readFile(String name) { + try (InputStream in = SarifRendererTest.class.getResourceAsStream("sarif/" + name)) { + String asd = IOUtils.toString(in, StandardCharsets.UTF_8); + return asd; + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/empty.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/empty.sarif.json new file mode 100644 index 0000000000..171c68bc90 --- /dev/null +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/empty.sarif.json @@ -0,0 +1,17 @@ +{ + "schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "PMD", + "version": "unknown", + "informationUri": "https://github.com/pmd", + "rules": [] + } + }, + "results": [] + } + ] +} \ No newline at end of file diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json new file mode 100644 index 0000000000..e43dc5c436 --- /dev/null +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json @@ -0,0 +1,85 @@ +{ + "schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "PMD", + "version": "unknown", + "informationUri": "https://github.com/pmd", + "rules": [ + { + "id": "Foo", + "name": "Foo", + "shortDescription": { + "text": "blah" + }, + "properties": { + "ruleset": "RuleSet", + "priority": 5 + } + }, + { + "id": "Boo", + "name": "Boo", + "shortDescription": { + "text": "blah" + }, + "properties": { + "ruleset": "RuleSet", + "priority": 1 + } + } + ] + } + }, + "results": [ + { + "ruleId": "Foo", + "ruleIndex": 0, + "message": { + "text": "blah" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "notAvailable.ext" + }, + "region": { + "startLine": 1, + "startColumn": 1, + "endLine": 1, + "endColumn": 1 + } + } + } + ] + }, + { + "ruleId": "Boo", + "ruleIndex": 1, + "message": { + "text": "blah" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "notAvailable.ext" + }, + "region": { + "startLine": 1, + "startColumn": 1, + "endLine": 1, + "endColumn": 2 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected.sarif.json new file mode 100644 index 0000000000..6bee531e3c --- /dev/null +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected.sarif.json @@ -0,0 +1,52 @@ +{ + "schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "PMD", + "version": "unknown", + "informationUri": "https://github.com/pmd", + "rules": [ + { + "id": "Foo", + "name": "Foo", + "shortDescription": { + "text": "blah" + }, + "properties": { + "ruleset": "RuleSet", + "priority": 5 + } + } + ] + } + }, + "results": [ + { + "ruleId": "Foo", + "ruleIndex": 0, + "message": { + "text": "blah" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "notAvailable.ext" + }, + "region": { + "startLine": 1, + "startColumn": 1, + "endLine": 1, + "endColumn": 1 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/pmd-dist/.DS_Store b/pmd-dist/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..6470af5bf85720c73adc1b259ba22de9590bdd00 GIT binary patch literal 6148 zcmeHK!A{#i5S>i|Y*Ha|s6@H#rBV-3m8cLYLY1sE9CAZ4q6dn=CJ|UN-Y9mOwrMJV z2Ydm)!VmB{?ab~bB2DRys-PKZ_Kj!X?8+HQ~^Mu6Bf$Y{K2T7e8~zf zvw|qh7+>HL;%=CeEMeLh#`M!jHOisqFt><3Xu_5v+EU?;7{ZogUOArUFt=#SLAb+*a5D>c zLJ?+m9A6na2+tz7tN<&ps6f$PR%!p=d-(jnSj1PX04wmU6cDA3-)ZA5xxIDi=4h`~ t=%?sp#LF$tOE557F>+}ueuZuf^NIpQ&tYy6GidxHU}WHi75JwL`~s{5b>sj5 literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml index d7c11a0bda..64e3a2d120 100644 --- a/pom.xml +++ b/pom.xml @@ -726,6 +726,12 @@ groovy 2.4.21 + + org.projectlombok + lombok + 1.18.12 + provided + From 9fe90d4ae40d4e47b21f6f5a500c1720b6222811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 18 Jan 2021 16:47:33 +0100 Subject: [PATCH 069/154] Deprecate performance.xml/SimplifyStartsWith Fixes #2740 --- docs/pages/release_notes.md | 6 ++++++ pmd-java/src/main/resources/category/java/performance.xml | 3 +++ 2 files changed, 9 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index cfa32a64ae..7c1710590c 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -32,6 +32,12 @@ This is a {{ site.pmd.release_type }} release. ### API Changes +### Deprecated rules + +* java-performance + * [SimplifyStartsWith](https://pmd.github.io/latest/pmd_rules_java_performance.html#simplifystartswith): the suggested code transformation has an insignificant performance impact, and decreases readability. + + ### External Contributions * [#2666](https://github.com/pmd/pmd/pull/2666): \[swift] Manage swift5 string literals - [kenji21](https://github.com/kenji21) diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index b9c61b80b5..656aab8849 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -696,8 +696,11 @@ public class C { since="3.1" message="This call to String.startsWith can be rewritten using String.charAt(0)" class="net.sourceforge.pmd.lang.rule.XPathRule" + deprecated="true" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_performance.html#simplifystartswith"> +Note: this rule is deprecated for removal, as the optimization is insignificant. + Calls to `string.startsWith("x")` with a string literal of length 1 can be rewritten using `string.charAt(0)`, at the expense of some readability. To prevent `IndexOutOfBoundsException` being thrown by the `charAt` method, ensure that the string is not empty by making an additional check first. From 7f1b6f582f882f03a9c25b3bd6b0bf07f15e37cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 18 Jan 2021 16:54:59 +0100 Subject: [PATCH 070/154] Deprecate performance.xml/AvoidUsingShortType Fixes #2296 --- docs/pages/release_notes.md | 1 + pmd-java/src/main/resources/category/java/performance.xml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 7c1710590c..a97c187285 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -35,6 +35,7 @@ This is a {{ site.pmd.release_type }} release. ### Deprecated rules * java-performance + * [AvoidUsingShortType](https://pmd.github.io/latest/pmd_rules_java_performance.html#avoidusingshorttype): arithmetic on shorts is not significantly slower than on ints, whereas using shorts may provide significant memory savings in arrays. * [SimplifyStartsWith](https://pmd.github.io/latest/pmd_rules_java_performance.html#simplifystartswith): the suggested code transformation has an insignificant performance impact, and decreases readability. diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index 656aab8849..f200437198 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -275,8 +275,11 @@ public class Something { message="Do not use the short type" class="net.sourceforge.pmd.lang.rule.XPathRule" typeResolution="true" + deprecated="true" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_performance.html#avoidusingshorttype"> +Note: this rule is deprecated, as its rationale does not hold. + Java uses the 'short' type to reduce memory usage, not to optimize calculation. In fact, the JVM does not have any arithmetic capabilities for the short type: the JVM must convert the short into an int, do the proper calculation and convert the int back to a short. Thus any storage gains found through use of the 'short' type may be offset by From 51b1c1b429659402f6b41cf2e45d7dce6634207f Mon Sep 17 00:00:00 2001 From: Manuel Moya Date: Mon, 18 Jan 2021 17:45:52 +0100 Subject: [PATCH 071/154] cleaning code --- .../pmd/renderers/SarifRenderer.java | 82 +++++++++---------- .../pmd/renderers/SarifRendererTest.java | 5 ++ 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java index 52bcd12954..87580b8ca0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java @@ -11,12 +11,12 @@ import java.util.*; public class SarifRenderer extends AbstractIncrementingRenderer { public static final String NAME = "sarif"; - private Gson gson; - private SarifLog sarifLog; - SarifLog.Component driver; - private List violatedRules; + private SarifLog sarifLog = new SarifLog(); private SarifLog.Run run; - private List results; + private SarifLog.Tool tool; + private SarifLog.Component driver; + private List ruleDescriptors = new LinkedList<>(); + private List results = new LinkedList<>(); public SarifRenderer() { super(NAME, "Sarif integration."); @@ -29,17 +29,9 @@ public class SarifRenderer extends AbstractIncrementingRenderer { @Override public void start() throws IOException { - gson = new GsonBuilder() - .disableHtmlEscaping() - .setPrettyPrinting() - .create(); - - run = new SarifLog.Run(); - defineRunTool(); - - sarifLog = new SarifLog(); - violatedRules = new LinkedList<>(); - results = new LinkedList<>(); + driver = getDriverComponent(); + tool = new SarifLog.Tool().setDriver(driver); + run = new SarifLog.Run().setTool(tool); } @Override @@ -48,21 +40,22 @@ public class SarifRenderer extends AbstractIncrementingRenderer { while (violations.hasNext()) { RuleViolation rv = violations.next(); - Integer violatedRuleIndex = getRuleViolationIndex(rv); - if (violatedRuleIndex != -1) { - SarifLog.Result existingResult = getResultByRuleIndex(violatedRuleIndex); + Integer ruleIndex = getRuleViolationIndex(rv); + + if (ruleIndex != -1) { + SarifLog.Result existingResult = getResultByRuleIndex(ruleIndex); List locations = existingResult.getLocations(); locations.add(getRuleViolationLocation(rv)); existingResult.setLocations(locations); } else { - SarifLog.ReportingDescriptor rule = createReportingDescriptor(rv); - violatedRules.add(rule); - violatedRuleIndex = violatedRules.indexOf(rule); + SarifLog.ReportingDescriptor ruleDescriptor = getReportingDescriptor(rv); + ruleDescriptors.add(ruleDescriptor); + ruleIndex = ruleDescriptors.indexOf(ruleDescriptor); SarifLog.Result result = new SarifLog.Result(); result.setRuleId(rv.getRule().getName()); - result.setRuleIndex(violatedRuleIndex); + result.setRuleIndex(ruleIndex); SarifLog.Message message = new SarifLog.Message(); message.setText(rv.getDescription()); @@ -80,43 +73,46 @@ public class SarifRenderer extends AbstractIncrementingRenderer { @Override public void end() throws IOException { if (errors.isEmpty() && configErrors.isEmpty()) { - driver.setRules(violatedRules); + driver.setRules(ruleDescriptors); run.setResults(results); List runs = new LinkedList<>(); runs.add(run); sarifLog.setRuns(runs); - String json = gson.toJson(sarifLog); + Gson gson = new GsonBuilder() + .disableHtmlEscaping() + .setPrettyPrinting() + .create(); + String json = gson.toJson(sarifLog); writer.write(json); } } - private void defineRunTool() { - driver = new SarifLog.Component(); - driver.setName("PMD"); // to improve - driver.setVersion(PMDVersion.VERSION); - driver.setInformationUri("https://github.com/pmd"); // to improve + private SarifLog.Component getDriverComponent() { + SarifLog.Component result = new SarifLog.Component(); - SarifLog.Tool tool = new SarifLog.Tool(); - tool.setDriver(driver); + result.setName("PMD"); // to improve + result.setVersion(PMDVersion.VERSION); + result.setInformationUri("https://github.com/pmd"); // to improve - run.setTool(tool); + return result; } private Integer getRuleViolationIndex(RuleViolation rv) { Integer result = -1; - for (SarifLog.ReportingDescriptor rule : violatedRules) { + for (SarifLog.ReportingDescriptor rule : ruleDescriptors) { if (rule.getId() == rv.getRule().getName()) { - result = violatedRules.indexOf(rule); + result = ruleDescriptors.indexOf(rule); } } return result; } - private SarifLog.ReportingDescriptor createReportingDescriptor(RuleViolation rv) { + private SarifLog.ReportingDescriptor getReportingDescriptor(RuleViolation rv) { SarifLog.ReportingDescriptor result = new SarifLog.ReportingDescriptor(); + result.setId(rv.getRule().getName()); result.setName(rv.getRule().getName()); result.setShortDescription(new SarifLog.MultiformatMessage(rv.getDescription())); @@ -151,19 +147,19 @@ public class SarifRenderer extends AbstractIncrementingRenderer { physicalLocation.setArtifactLocation(artifactLocation); physicalLocation.setRegion(region); - SarifLog.Location location = new SarifLog.Location(); - location.setPhysicalLocation(physicalLocation); + SarifLog.Location result = new SarifLog.Location(); + result.setPhysicalLocation(physicalLocation); - return location; + return result; } private SarifLog.PropertyBag getRuleProperties(RuleViolation rv) { - SarifLog.PropertyBag propertyBag = new SarifLog.PropertyBag(); + SarifLog.PropertyBag result = new SarifLog.PropertyBag(); - propertyBag.setRuleset(rv.getRule().getRuleSetName()); - propertyBag.setPriority(rv.getRule().getPriority().getPriority()); + result.setRuleset(rv.getRule().getRuleSetName()); + result.setPriority(rv.getRule().getPriority().getPriority()); - return propertyBag; + return result; } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java index 071ab6e1ab..ee963e565b 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java @@ -40,8 +40,13 @@ public class SarifRendererTest extends AbstractRendererTest{ @Override @Test public void testRendererMultiple() throws Exception { + // Setup Report rep = reportTwoViolations(); + + // Exercise String actual = ReportTest.render(getRenderer(), rep); + + // Verify assertEquals(filter(getExpectedMultiple()), filter(actual)); } From e008f2ba715cfb86d732e7822280f60cd85e7795 Mon Sep 17 00:00:00 2001 From: Manuel Moya Date: Tue, 19 Jan 2021 10:07:59 +0100 Subject: [PATCH 072/154] Cleaning code --- .../pmd/renderers/SarifRenderer.java | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java index 87580b8ca0..2940b434ab 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java @@ -38,35 +38,22 @@ public class SarifRenderer extends AbstractIncrementingRenderer { public void renderFileViolations(Iterator violations) throws IOException { while (violations.hasNext()) { + SarifLog.Result result; + List locations = new LinkedList<>(); + RuleViolation rv = violations.next(); Integer ruleIndex = getRuleViolationIndex(rv); - if (ruleIndex != -1) { - SarifLog.Result existingResult = getResultByRuleIndex(ruleIndex); - - List locations = existingResult.getLocations(); - locations.add(getRuleViolationLocation(rv)); - existingResult.setLocations(locations); + result = getResultByRuleIndex(ruleIndex); + locations = result.getLocations(); } else { - SarifLog.ReportingDescriptor ruleDescriptor = getReportingDescriptor(rv); - ruleDescriptors.add(ruleDescriptor); - ruleIndex = ruleDescriptors.indexOf(ruleDescriptor); - - SarifLog.Result result = new SarifLog.Result(); - result.setRuleId(rv.getRule().getName()); - result.setRuleIndex(ruleIndex); - - SarifLog.Message message = new SarifLog.Message(); - message.setText(rv.getDescription()); - result.setMessage(message); - - List locations = new LinkedList<>(); - locations.add(getRuleViolationLocation(rv)); - result.setLocations(locations); - + result = resultFrom(rv); results.add(result); } + + locations.add(getRuleViolationLocation(rv)); + result.setLocations(locations); } } @@ -90,6 +77,22 @@ public class SarifRenderer extends AbstractIncrementingRenderer { } } + private SarifLog.Result resultFrom(RuleViolation violation) { + SarifLog.ReportingDescriptor ruleDescriptor = getReportingDescriptor(violation); + ruleDescriptors.add(ruleDescriptor); + Integer ruleIndex = ruleDescriptors.indexOf(ruleDescriptor); + + SarifLog.Result result = new SarifLog.Result(); + result.setRuleId(violation.getRule().getName()); + result.setRuleIndex(ruleIndex); + + SarifLog.Message message = new SarifLog.Message(); + message.setText(violation.getDescription()); + result.setMessage(message); + + return result; + } + private SarifLog.Component getDriverComponent() { SarifLog.Component result = new SarifLog.Component(); From 52a6ab2e08fa856e7a76196b2b8ce4d9d2ae357e Mon Sep 17 00:00:00 2001 From: Manuel Moya Date: Tue, 19 Jan 2021 10:45:17 +0100 Subject: [PATCH 073/154] Fix checkstyle issues --- .../sourceforge/pmd/renderers/SarifLog.java | 10 +++++-- .../pmd/renderers/SarifRenderer.java | 20 ++++++++----- .../pmd/renderers/SarifRendererTest.java | 29 ++++++++++++------- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java index 9117411889..533c7e7636 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java @@ -1,12 +1,16 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + package net.sourceforge.pmd.renderers; +import java.util.List; + import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; -import java.util.List; - @Data @Accessors(chain = true) public class SarifLog { @@ -325,4 +329,4 @@ public class SarifLog { this.text = text; } } -} \ No newline at end of file +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java index 2940b434ab..8d03427823 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java @@ -1,19 +1,25 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + package net.sourceforge.pmd.renderers; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + import net.sourceforge.pmd.PMDVersion; import net.sourceforge.pmd.RuleViolation; -import java.io.IOException; -import java.util.*; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; public class SarifRenderer extends AbstractIncrementingRenderer { public static final String NAME = "sarif"; private SarifLog sarifLog = new SarifLog(); private SarifLog.Run run; - private SarifLog.Tool tool; private SarifLog.Component driver; private List ruleDescriptors = new LinkedList<>(); private List results = new LinkedList<>(); @@ -30,7 +36,7 @@ public class SarifRenderer extends AbstractIncrementingRenderer { @Override public void start() throws IOException { driver = getDriverComponent(); - tool = new SarifLog.Tool().setDriver(driver); + SarifLog.Tool tool = new SarifLog.Tool().setDriver(driver); run = new SarifLog.Run().setTool(tool); } @@ -164,6 +170,4 @@ public class SarifRenderer extends AbstractIncrementingRenderer { return result; } - - } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java index ee963e565b..93d2ac2f4f 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java @@ -1,22 +1,31 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + package net.sourceforge.pmd.renderers; import static org.junit.Assert.assertEquals; -import net.sourceforge.pmd.*; -import net.sourceforge.pmd.lang.ast.DummyNode; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.rule.AbstractRule; -import net.sourceforge.pmd.lang.rule.ParametricRuleViolation; - -import org.apache.commons.io.IOUtils; -import org.junit.Test; - import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -public class SarifRendererTest extends AbstractRendererTest{ +import org.apache.commons.io.IOUtils; +import org.junit.Test; + +import net.sourceforge.pmd.FooRule; +import net.sourceforge.pmd.Report; +import net.sourceforge.pmd.ReportTest; +import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.RulePriority; +import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.lang.ast.DummyNode; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.rule.AbstractRule; +import net.sourceforge.pmd.lang.rule.ParametricRuleViolation; + +public class SarifRendererTest extends AbstractRendererTest { @Override public Renderer getRenderer() { return new SarifRenderer(); From 2e2b763b5a4d3ce5fdadee004927a68d55a38c86 Mon Sep 17 00:00:00 2001 From: Manuel Moya Date: Tue, 19 Jan 2021 10:52:16 +0100 Subject: [PATCH 074/154] Update SarifRenderer.java Cleaning code --- .../java/net/sourceforge/pmd/renderers/SarifRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java index 8d03427823..d33a887bba 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java @@ -102,9 +102,9 @@ public class SarifRenderer extends AbstractIncrementingRenderer { private SarifLog.Component getDriverComponent() { SarifLog.Component result = new SarifLog.Component(); - result.setName("PMD"); // to improve + result.setName("PMD"); result.setVersion(PMDVersion.VERSION); - result.setInformationUri("https://github.com/pmd"); // to improve + result.setInformationUri("https://pmd.github.io/pmd/"); return result; } From fbb73373ea480dd2955573033d58ef60dc4eed19 Mon Sep 17 00:00:00 2001 From: Manuel Moya Date: Tue, 19 Jan 2021 11:11:42 +0100 Subject: [PATCH 075/154] Sarif format refinement --- pmd-core/pmd-core-checkstyle-suppressions.xml | 3 +++ .../main/java/net/sourceforge/pmd/renderers/SarifLog.java | 2 +- .../java/net/sourceforge/pmd/renderers/SarifRenderer.java | 1 - .../net/sourceforge/pmd/renderers/sarif/empty.sarif.json | 4 ++-- .../pmd/renderers/sarif/expected-multiple.sarif.json | 6 ++---- .../net/sourceforge/pmd/renderers/sarif/expected.sarif.json | 5 ++--- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/pmd-core/pmd-core-checkstyle-suppressions.xml b/pmd-core/pmd-core-checkstyle-suppressions.xml index c1647b6530..09f4e19709 100644 --- a/pmd-core/pmd-core-checkstyle-suppressions.xml +++ b/pmd-core/pmd-core-checkstyle-suppressions.xml @@ -8,4 +8,7 @@ + + + \ No newline at end of file diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java index 533c7e7636..e18ec2586b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java @@ -18,7 +18,7 @@ public class SarifLog { /** * The URI of the JSON schema corresponding to the version. */ - private String schema = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"; + private String $schema = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"; /** * The SARIF format version of this log file. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java index d33a887bba..ddc8bb7633 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java @@ -123,7 +123,6 @@ public class SarifRenderer extends AbstractIncrementingRenderer { SarifLog.ReportingDescriptor result = new SarifLog.ReportingDescriptor(); result.setId(rv.getRule().getName()); - result.setName(rv.getRule().getName()); result.setShortDescription(new SarifLog.MultiformatMessage(rv.getDescription())); result.setHelpUri(rv.getRule().getExternalInfoUrl()); result.setProperties(getRuleProperties(rv)); diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/empty.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/empty.sarif.json index 171c68bc90..fd4c900be2 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/empty.sarif.json +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/empty.sarif.json @@ -1,5 +1,5 @@ { - "schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", "version": "2.1.0", "runs": [ { @@ -7,7 +7,7 @@ "driver": { "name": "PMD", "version": "unknown", - "informationUri": "https://github.com/pmd", + "informationUri": "https://pmd.github.io/pmd/", "rules": [] } }, diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json index e43dc5c436..3b1236529c 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json @@ -1,5 +1,5 @@ { - "schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", "version": "2.1.0", "runs": [ { @@ -7,11 +7,10 @@ "driver": { "name": "PMD", "version": "unknown", - "informationUri": "https://github.com/pmd", + "informationUri": "https://pmd.github.io/pmd/", "rules": [ { "id": "Foo", - "name": "Foo", "shortDescription": { "text": "blah" }, @@ -22,7 +21,6 @@ }, { "id": "Boo", - "name": "Boo", "shortDescription": { "text": "blah" }, diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected.sarif.json index 6bee531e3c..539493852f 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected.sarif.json +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected.sarif.json @@ -1,5 +1,5 @@ { - "schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", "version": "2.1.0", "runs": [ { @@ -7,11 +7,10 @@ "driver": { "name": "PMD", "version": "unknown", - "informationUri": "https://github.com/pmd", + "informationUri": "https://pmd.github.io/pmd/", "rules": [ { "id": "Foo", - "name": "Foo", "shortDescription": { "text": "blah" }, From a5ac240fd0031235667c52027fbd9fc92165a2d4 Mon Sep 17 00:00:00 2001 From: Manuel Moya Date: Tue, 19 Jan 2021 11:59:14 +0100 Subject: [PATCH 076/154] Adding doc --- .DS_Store | Bin 10244 -> 10244 bytes docs/pages/pmd/userdocs/pmd_report_formats.md | 12 ++ docs/report-examples/pmd-report.sarif.json | 137 ++++++++++++++++++ sarif-output-example.sarif.json | 137 ++++++++++++++++++ 4 files changed, 286 insertions(+) create mode 100644 docs/report-examples/pmd-report.sarif.json create mode 100644 sarif-output-example.sarif.json diff --git a/.DS_Store b/.DS_Store index a2e856bf7d9877725528b9bd357de4a5228d83ae..0d550049c764a190ecc85e026c6eedaab5464801 100644 GIT binary patch delta 337 zcmZn(XbG6$&uFnRU^hRb#bzFXOs0BnhJ1!nh7yJXFsa9o#ZU|s$@9#~Pfp6oPhwzT z5MW?nN(9mx|G@yrVqg$M(d7)}r7)y2I5XrhWHRJ3Bm&J#Wx!?Dd1SLDX9)9dt`~aC zC}L@(qhM@mTC1Z_ZD?d+siR zBA}`?23-`#=>nZn0hG%HdNBv=EQCs2P6T;(#^fHU7(NCD=G6%IZY*SG+03r+i-i&7 HG$v*MX=PTu delta 92 zcmZn(XbG6$&uFqSU^hRb$z~pbOs2`wVqBXUh2JuY7+UHm7+adu>L^qj8km~tD41DV qZe|kQ%{uwDRLtb1(t%w6;b8MWX-<}n4LppS*%f}VY;G21W(EL_jv`6` diff --git a/docs/pages/pmd/userdocs/pmd_report_formats.md b/docs/pages/pmd/userdocs/pmd_report_formats.md index 5ca846ac69..1fd3c5c34b 100644 --- a/docs/pages/pmd/userdocs/pmd_report_formats.md +++ b/docs/pages/pmd/userdocs/pmd_report_formats.md @@ -19,6 +19,18 @@ those can be specified with the `-property` / `-P` option on the command-line. {% include note.html content="Suppressed violations are only reported, if the CLI parameter `-showsuppressed` is set." %} +## SARIF + +"SARIF, the Static Analysis Results Interchange Format, is a standard, JSON-based format for the output of static +analysis tools. It has been approved as an OASIS standard" - . + +SARIF schema can be found here: . + +PMD holds an initial implementation version of SARIF rendering. This means SARIF allows for more complexity and the +current implementation can be extended. + +[Example](report-examples/pmd-report.sarif.json) + ## codeclimate Renderer for Code Climate JSON format. diff --git a/docs/report-examples/pmd-report.sarif.json b/docs/report-examples/pmd-report.sarif.json new file mode 100644 index 0000000000..af3c6a7fea --- /dev/null +++ b/docs/report-examples/pmd-report.sarif.json @@ -0,0 +1,137 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "PMD", + "version": "", + "informationUri": "https://pmd.github.io/pmd/", + "rules": [ + { + "id": "ApexSharingViolations", + "shortDescription": { + "text": "Apex classes should declare a sharing model if DML or SOQL/SOSL is used" + }, + "helpUri": "https://pmd.github.io/pmd/pmd_rules_apex_security.html#apexsharingviolations", + "properties": { + "ruleset": "Security", + "priority": 3 + } + }, + { + "id": "ApexDoc", + "shortDescription": { + "text": "Missing ApexDoc comment" + }, + "helpUri": "https://pmd.github.io/pmd/pmd_rules_apex_documentation.html#apexdoc", + "properties": { + "ruleset": "Documentation", + "priority": 3 + } + } + ] + } + }, + "results": [ + { + "ruleId": "ApexSharingViolations", + "ruleIndex": 0, + "message": { + "text": "Apex classes should declare a sharing model if DML or SOQL/SOSL is used" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/Users/user/pmd/unhappy-soup/force-app/main/default/classes/ApexSOQLInjection.cls" + }, + "region": { + "startLine": 1, + "startColumn": 14, + "endLine": 1, + "endColumn": 30 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "/Users/user/pmd/unhappy-soup/force-app/main/default/classes/ApexSharingViolations.cls" + }, + "region": { + "startLine": 1, + "startColumn": 14, + "endLine": 1, + "endColumn": 34 + } + } + } + ] + }, + { + "ruleId": "ApexDoc", + "ruleIndex": 1, + "message": { + "text": "Missing ApexDoc comment" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/Users/user/pmd/unhappy-soup/force-app/main/default/classes/ApexSOQLInjection.cls" + }, + "region": { + "startLine": 1, + "startColumn": 14, + "endLine": 6, + "endColumn": 1 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "/Users/user/pmd/unhappy-soup/force-app/main/default/classes/ApexSOQLInjection.cls" + }, + "region": { + "startLine": 3, + "startColumn": 17, + "endLine": 5, + "endColumn": 2 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "/Users/user/pmd/unhappy-soup/force-app/main/default/classes/ApexSharingViolations.cls" + }, + "region": { + "startLine": 1, + "startColumn": 14, + "endLine": 6, + "endColumn": 1 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "/Users/user/pmd/unhappy-soup/force-app/main/default/classes/ApexSharingViolations.cls" + }, + "region": { + "startLine": 3, + "startColumn": 14, + "endLine": 5, + "endColumn": 2 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/sarif-output-example.sarif.json b/sarif-output-example.sarif.json new file mode 100644 index 0000000000..e48d7e928d --- /dev/null +++ b/sarif-output-example.sarif.json @@ -0,0 +1,137 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "PMD", + "version": "6.31.0-SNAPSHOT", + "informationUri": "https://pmd.github.io/pmd/", + "rules": [ + { + "id": "ApexSharingViolations", + "shortDescription": { + "text": "Apex classes should declare a sharing model if DML or SOQL/SOSL is used" + }, + "helpUri": "https://pmd.github.io/pmd/pmd_rules_apex_security.html#apexsharingviolations", + "properties": { + "ruleset": "Security", + "priority": 3 + } + }, + { + "id": "ApexDoc", + "shortDescription": { + "text": "Missing ApexDoc comment" + }, + "helpUri": "https://pmd.github.io/pmd/pmd_rules_apex_documentation.html#apexdoc", + "properties": { + "ruleset": "Documentation", + "priority": 3 + } + } + ] + } + }, + "results": [ + { + "ruleId": "ApexSharingViolations", + "ruleIndex": 0, + "message": { + "text": "Apex classes should declare a sharing model if DML or SOQL/SOSL is used" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/Users/manuelmoyaferrer/devel/pmd-copado/unhappy-soup/force-app/main/default/classes/ApexSOQLInjection.cls" + }, + "region": { + "startLine": 1, + "startColumn": 14, + "endLine": 1, + "endColumn": 30 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "/Users/manuelmoyaferrer/devel/pmd-copado/unhappy-soup/force-app/main/default/classes/ApexSharingViolations.cls" + }, + "region": { + "startLine": 1, + "startColumn": 14, + "endLine": 1, + "endColumn": 34 + } + } + } + ] + }, + { + "ruleId": "ApexDoc", + "ruleIndex": 1, + "message": { + "text": "Missing ApexDoc comment" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/Users/manuelmoyaferrer/devel/pmd-copado/unhappy-soup/force-app/main/default/classes/ApexSOQLInjection.cls" + }, + "region": { + "startLine": 1, + "startColumn": 14, + "endLine": 6, + "endColumn": 1 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "/Users/manuelmoyaferrer/devel/pmd-copado/unhappy-soup/force-app/main/default/classes/ApexSOQLInjection.cls" + }, + "region": { + "startLine": 3, + "startColumn": 17, + "endLine": 5, + "endColumn": 2 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "/Users/manuelmoyaferrer/devel/pmd-copado/unhappy-soup/force-app/main/default/classes/ApexSharingViolations.cls" + }, + "region": { + "startLine": 1, + "startColumn": 14, + "endLine": 6, + "endColumn": 1 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "uri": "/Users/manuelmoyaferrer/devel/pmd-copado/unhappy-soup/force-app/main/default/classes/ApexSharingViolations.cls" + }, + "region": { + "startLine": 3, + "startColumn": 14, + "endLine": 5, + "endColumn": 2 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file From 2aae644dd5c77ea89d7b29c89e482bc542901dcb Mon Sep 17 00:00:00 2001 From: Manuel Moya Date: Tue, 19 Jan 2021 12:19:15 +0100 Subject: [PATCH 077/154] Update pmd_report_formats.md --- docs/pages/pmd/userdocs/pmd_report_formats.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/pmd/userdocs/pmd_report_formats.md b/docs/pages/pmd/userdocs/pmd_report_formats.md index 1fd3c5c34b..3dc845b497 100644 --- a/docs/pages/pmd/userdocs/pmd_report_formats.md +++ b/docs/pages/pmd/userdocs/pmd_report_formats.md @@ -19,7 +19,7 @@ those can be specified with the `-property` / `-P` option on the command-line. {% include note.html content="Suppressed violations are only reported, if the CLI parameter `-showsuppressed` is set." %} -## SARIF +## sarif "SARIF, the Static Analysis Results Interchange Format, is a standard, JSON-based format for the output of static analysis tools. It has been approved as an OASIS standard" - . From 68eb4294d4e5ef056bea78a2beb99ab24f973814 Mon Sep 17 00:00:00 2001 From: Manuel Moya Date: Tue, 19 Jan 2021 13:29:33 +0100 Subject: [PATCH 078/154] Initialising vars in start method --- .DS_Store | Bin 10244 -> 10244 bytes .../pmd/renderers/SarifRenderer.java | 10 +++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.DS_Store b/.DS_Store index 0d550049c764a190ecc85e026c6eedaab5464801..091f0b39ff8dc98003e93c817969c0b809b37add 100644 GIT binary patch delta 150 zcmZn(XbG6$&uFtTU^hRb&1N2fncUps48;tI3`Go?3~7^-q|H@&j4gk08Dn$SQ delta 45 ycmZn(XbG6$&uFnRU^hRb#bzFXncS1@WrBH_S2Hj$X#59*jfKoCo7olqvI78dmJwV4 diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java index ddc8bb7633..d969cb1752 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java @@ -18,11 +18,11 @@ import com.google.gson.GsonBuilder; public class SarifRenderer extends AbstractIncrementingRenderer { public static final String NAME = "sarif"; - private SarifLog sarifLog = new SarifLog(); + private SarifLog sarifLog; private SarifLog.Run run; private SarifLog.Component driver; - private List ruleDescriptors = new LinkedList<>(); - private List results = new LinkedList<>(); + private List ruleDescriptors; + private List results; public SarifRenderer() { super(NAME, "Sarif integration."); @@ -35,6 +35,10 @@ public class SarifRenderer extends AbstractIncrementingRenderer { @Override public void start() throws IOException { + sarifLog = new SarifLog(); + ruleDescriptors = new LinkedList<>(); + results = new LinkedList<>(); + driver = getDriverComponent(); SarifLog.Tool tool = new SarifLog.Tool().setDriver(driver); run = new SarifLog.Run().setTool(tool); From f16269a75024f1fd502a268467527cea8cca2807 Mon Sep 17 00:00:00 2001 From: Manuel Moya Date: Tue, 19 Jan 2021 18:30:39 +0100 Subject: [PATCH 079/154] Including a SarifLogBuilder and error display --- pmd-core/pmd-core-checkstyle-suppressions.xml | 3 - .../sourceforge/pmd/renderers/SarifLog.java | 13 +- .../pmd/renderers/SarifLogBuilder.java | 131 ++++++++++++++ .../pmd/renderers/SarifRenderer.java | 167 +++++------------- .../pmd/renderers/SarifRendererTest.java | 25 +++ .../sarif/expected-configerror.sarif.json | 8 + .../sarif/expected-error-nomessage.sarif.json | 8 + .../renderers/sarif/expected-error.sarif.json | 8 + .../sarif/expected-multiple.sarif.json | 66 +++---- 9 files changed, 268 insertions(+), 161 deletions(-) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java create mode 100644 pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-configerror.sarif.json create mode 100644 pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error-nomessage.sarif.json create mode 100644 pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error.sarif.json diff --git a/pmd-core/pmd-core-checkstyle-suppressions.xml b/pmd-core/pmd-core-checkstyle-suppressions.xml index 09f4e19709..c1647b6530 100644 --- a/pmd-core/pmd-core-checkstyle-suppressions.xml +++ b/pmd-core/pmd-core-checkstyle-suppressions.xml @@ -8,7 +8,4 @@ - - - \ No newline at end of file diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java index e18ec2586b..c470ff0e03 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java @@ -6,23 +6,30 @@ package net.sourceforge.pmd.renderers; import java.util.List; +import com.google.gson.annotations.SerializedName; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.Singular; import lombok.experimental.Accessors; @Data @Accessors(chain = true) +@Builder public class SarifLog { /** * The URI of the JSON schema corresponding to the version. */ - private String $schema = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"; + @Builder.Default + @SerializedName("$schema") + private String schema = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"; /** * The SARIF format version of this log file. */ + @Builder.Default private String version = "2.1.0"; /** @@ -199,6 +206,7 @@ public class SarifLog { */ @Data @Accessors(chain = true) + @Builder public static class Run { /** @@ -212,6 +220,7 @@ public class SarifLog { * The set of results contained in an SARIF log. The results array can be omitted when a run is solely exporting * rules metadata. It must be present (but may be empty) if a log file represents an actual scan. */ + @Singular private List results; } @@ -220,6 +229,7 @@ public class SarifLog { */ @Data @Accessors(chain = true) + @Builder public static class Tool { /** @@ -232,6 +242,7 @@ public class SarifLog { * A component, such as a plug-in or the driver, of the analysis tool that was run. */ @Data + @Builder(toBuilder = true) public static class Component { /** * The name of the tool component. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java new file mode 100644 index 0000000000..b7cf295b28 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java @@ -0,0 +1,131 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.renderers; + +import static lombok.AccessLevel.PRIVATE; +import static net.sourceforge.pmd.renderers.SarifLog.ArtifactLocation; +import static net.sourceforge.pmd.renderers.SarifLog.Component; +import static net.sourceforge.pmd.renderers.SarifLog.Location; +import static net.sourceforge.pmd.renderers.SarifLog.Message; +import static net.sourceforge.pmd.renderers.SarifLog.MultiformatMessage; +import static net.sourceforge.pmd.renderers.SarifLog.PhysicalLocation; +import static net.sourceforge.pmd.renderers.SarifLog.PropertyBag; +import static net.sourceforge.pmd.renderers.SarifLog.Region; +import static net.sourceforge.pmd.renderers.SarifLog.ReportingDescriptor; +import static net.sourceforge.pmd.renderers.SarifLog.Result; +import static net.sourceforge.pmd.renderers.SarifLog.Tool; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import lombok.NoArgsConstructor; + +import net.sourceforge.pmd.renderers.SarifLog.Run; +import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.PMDVersion; + +@NoArgsConstructor(access = PRIVATE) +class SarifLogBuilder { + private final Map> locationsByRule = new HashMap<>(); + + public static SarifLogBuilder sarifLogBuilder() { + return new SarifLogBuilder(); + } + + public SarifLog build() { + final List rules = new ArrayList<>(locationsByRule.keySet()); + + final List results = new ArrayList<>(); + for (int i = 0, size = rules.size(); i < size; i++) { + ReportingDescriptor rule = rules.get(i); + List locations = locationsByRule.get(rule); + results.add(resultFrom(rule, i, locations)); + } + + final Component driver = getDriverComponent().toBuilder().rules(rules).build(); + final Tool tool = Tool.builder().driver(driver).build(); + final Run run = Run.builder().tool(tool).results(results).build(); + + List runs = Collections.singletonList(run); + + return SarifLog.builder().runs(runs).build(); + } + + public SarifLogBuilder add(RuleViolation violation) { + final ReportingDescriptor ruleDescriptor = getReportingDescriptor(violation); + final Location location = getRuleViolationLocation(violation); + + final List ruleLocation = locationsByRule.containsKey(ruleDescriptor) ? locationsByRule.get(ruleDescriptor) : new ArrayList(); + ruleLocation.add(location); + locationsByRule.put(ruleDescriptor, ruleLocation); + + return this; + } + + private Result resultFrom(ReportingDescriptor rule, Integer ruleIndex, List locations) { + final Result result = new Result(); + result.setRuleId(rule.getId()); + result.setRuleIndex(ruleIndex); + + final Message message = new Message(); + message.setText(rule.getShortDescription().getText()); + result.setMessage(message); + + result.setLocations(locations); + + return result; + } + + private Location getRuleViolationLocation(RuleViolation rv) { + ArtifactLocation artifactLocation = new ArtifactLocation(); + artifactLocation.setUri(rv.getFilename()); + + Region region = new Region(); + region.setStartLine(rv.getBeginLine()); + region.setEndLine(rv.getEndLine()); + region.setStartColumn(rv.getBeginColumn()); + region.setEndColumn(rv.getEndColumn()); + + PhysicalLocation physicalLocation = new PhysicalLocation(); + physicalLocation.setArtifactLocation(artifactLocation); + physicalLocation.setRegion(region); + + Location result = new Location(); + result.setPhysicalLocation(physicalLocation); + + return result; + } + + private ReportingDescriptor getReportingDescriptor(RuleViolation rv) { + ReportingDescriptor result = new ReportingDescriptor(); + + result.setId(rv.getRule().getName()); + result.setShortDescription(new MultiformatMessage(rv.getDescription())); + result.setHelpUri(rv.getRule().getExternalInfoUrl()); + result.setProperties(getRuleProperties(rv)); + + return result; + } + + private PropertyBag getRuleProperties(RuleViolation rv) { + PropertyBag result = new PropertyBag(); + + result.setRuleset(rv.getRule().getRuleSetName()); + result.setPriority(rv.getRule().getPriority().getPriority()); + + return result; + } + + private Component getDriverComponent() { + return Component.builder() + .name("PMD") + .version(PMDVersion.VERSION) + .informationUri("https://pmd.github.io/pmd/") + .build(); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java index d969cb1752..bde884dca3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java @@ -5,11 +5,11 @@ package net.sourceforge.pmd.renderers; import java.io.IOException; +import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; +import java.util.Map; -import net.sourceforge.pmd.PMDVersion; +import net.sourceforge.pmd.Report; import net.sourceforge.pmd.RuleViolation; import com.google.gson.Gson; @@ -17,160 +17,79 @@ import com.google.gson.GsonBuilder; public class SarifRenderer extends AbstractIncrementingRenderer { public static final String NAME = "sarif"; + private static final String DEFAULT_DESCRIPTION = "Sarif integration."; + private static final String DEFAULT_FILE_EXTENSION = "sarif.json"; - private SarifLog sarifLog; - private SarifLog.Run run; - private SarifLog.Component driver; - private List ruleDescriptors; - private List results; + private final Gson gson = new GsonBuilder() + .disableHtmlEscaping() + .setPrettyPrinting() + .create(); + + private SarifLogBuilder sarifLogBuilder; public SarifRenderer() { - super(NAME, "Sarif integration."); + super(NAME, DEFAULT_DESCRIPTION); } @Override public String defaultFileExtension() { - return "sarif.json"; + return DEFAULT_FILE_EXTENSION; } @Override public void start() throws IOException { - sarifLog = new SarifLog(); - ruleDescriptors = new LinkedList<>(); - results = new LinkedList<>(); - - driver = getDriverComponent(); - SarifLog.Tool tool = new SarifLog.Tool().setDriver(driver); - run = new SarifLog.Run().setTool(tool); + sarifLogBuilder = SarifLogBuilder.sarifLogBuilder(); } @Override public void renderFileViolations(Iterator violations) throws IOException { - while (violations.hasNext()) { - SarifLog.Result result; - List locations = new LinkedList<>(); - - RuleViolation rv = violations.next(); - - Integer ruleIndex = getRuleViolationIndex(rv); - if (ruleIndex != -1) { - result = getResultByRuleIndex(ruleIndex); - locations = result.getLocations(); - } else { - result = resultFrom(rv); - results.add(result); - } - - locations.add(getRuleViolationLocation(rv)); - result.setLocations(locations); + final RuleViolation violation = violations.next(); + sarifLogBuilder.add(violation); } } @Override public void end() throws IOException { - if (errors.isEmpty() && configErrors.isEmpty()) { - driver.setRules(ruleDescriptors); - run.setResults(results); - - List runs = new LinkedList<>(); - runs.add(run); - sarifLog.setRuns(runs); - - Gson gson = new GsonBuilder() - .disableHtmlEscaping() - .setPrettyPrinting() - .create(); - - String json = gson.toJson(sarifLog); - writer.write(json); + if (!hasErrors()) { + writeLog(); + } else { + writeErrors(); } } - private SarifLog.Result resultFrom(RuleViolation violation) { - SarifLog.ReportingDescriptor ruleDescriptor = getReportingDescriptor(violation); - ruleDescriptors.add(ruleDescriptor); - Integer ruleIndex = ruleDescriptors.indexOf(ruleDescriptor); - - SarifLog.Result result = new SarifLog.Result(); - result.setRuleId(violation.getRule().getName()); - result.setRuleIndex(ruleIndex); - - SarifLog.Message message = new SarifLog.Message(); - message.setText(violation.getDescription()); - result.setMessage(message); - - return result; + private boolean hasErrors() { + return !errors.isEmpty() || !configErrors.isEmpty(); } - private SarifLog.Component getDriverComponent() { - SarifLog.Component result = new SarifLog.Component(); + private void writeLog() throws IOException { + final SarifLog sarifLog = sarifLogBuilder.build(); - result.setName("PMD"); - result.setVersion(PMDVersion.VERSION); - result.setInformationUri("https://pmd.github.io/pmd/"); - - return result; + final String json = gson.toJson(sarifLog); + writer.write(json); } - private Integer getRuleViolationIndex(RuleViolation rv) { - Integer result = -1; - for (SarifLog.ReportingDescriptor rule : ruleDescriptors) { - if (rule.getId() == rv.getRule().getName()) { - result = ruleDescriptors.indexOf(rule); - } + private void writeErrors() throws IOException { + final Map> errors = new HashMap<>(); + final Map processingErrors = new HashMap<>(); + final Map configErrors = new HashMap<>(); + + for (Report.ProcessingError error : this.errors) { + processingErrors.put("filename", error.getFile()); + processingErrors.put("message", error.getMsg()); + processingErrors.put("detail", error.getDetail()); } - return result; - } - private SarifLog.ReportingDescriptor getReportingDescriptor(RuleViolation rv) { - SarifLog.ReportingDescriptor result = new SarifLog.ReportingDescriptor(); - - result.setId(rv.getRule().getName()); - result.setShortDescription(new SarifLog.MultiformatMessage(rv.getDescription())); - result.setHelpUri(rv.getRule().getExternalInfoUrl()); - result.setProperties(getRuleProperties(rv)); - - return result; - } - - private SarifLog.Result getResultByRuleIndex(Integer ruleIndex) { - SarifLog.Result result = null; - - for (SarifLog.Result res : results) { - if (res.getRuleIndex() == ruleIndex) { - result = res; - } + for (Report.ConfigurationError error: this.configErrors) { + configErrors.put("rule", error.rule().getName()); + configErrors.put("ruleset", error.rule().getRuleSetName()); + configErrors.put("message", error.issue()); } - return result; - } - private SarifLog.Location getRuleViolationLocation(RuleViolation rv) { - SarifLog.ArtifactLocation artifactLocation = new SarifLog.ArtifactLocation(); - artifactLocation.setUri(rv.getFilename()); + errors.put("processing-errors", processingErrors); + errors.put("config-errors", configErrors); - SarifLog.Region region = new SarifLog.Region(); - region.setStartLine(rv.getBeginLine()); - region.setEndLine(rv.getEndLine()); - region.setStartColumn(rv.getBeginColumn()); - region.setEndColumn(rv.getEndColumn()); - - SarifLog.PhysicalLocation physicalLocation = new SarifLog.PhysicalLocation(); - physicalLocation.setArtifactLocation(artifactLocation); - physicalLocation.setRegion(region); - - SarifLog.Location result = new SarifLog.Location(); - result.setPhysicalLocation(physicalLocation); - - return result; - } - - private SarifLog.PropertyBag getRuleProperties(RuleViolation rv) { - SarifLog.PropertyBag result = new SarifLog.PropertyBag(); - - result.setRuleset(rv.getRule().getRuleSetName()); - result.setPriority(rv.getRule().getPriority().getPriority()); - - return result; + final String json = gson.toJson(errors); + writer.write(json); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java index 93d2ac2f4f..195294e543 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java @@ -46,6 +46,31 @@ public class SarifRendererTest extends AbstractRendererTest { return readFile("expected-multiple.sarif.json"); } + @Override + public String getExpectedError(Report.ProcessingError error) { + String expected = readFile("expected-error.sarif.json"); + expected = expected.replace("###REPLACE_ME###", error.getDetail() + .replaceAll("\r", "\\\\r") + .replaceAll("\n", "\\\\n") + .replaceAll("\t", "\\\\t")); + return expected; + } + + @Override + public String getExpectedError(Report.ConfigurationError error) { + return readFile("expected-configerror.sarif.json"); + } + + @Override + public String getExpectedErrorWithoutMessage(Report.ProcessingError error) { + String expected = readFile("expected-error-nomessage.sarif.json"); + expected = expected.replace("###REPLACE_ME###", error.getDetail() + .replaceAll("\r", "\\\\r") + .replaceAll("\n", "\\\\n") + .replaceAll("\t", "\\\\t")); + return expected; + } + @Override @Test public void testRendererMultiple() throws Exception { diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-configerror.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-configerror.sarif.json new file mode 100644 index 0000000000..6da4f10247 --- /dev/null +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-configerror.sarif.json @@ -0,0 +1,8 @@ +{ + "processing-errors": {}, + "config-errors": { + "ruleset": "RuleSet", + "rule": "Foo", + "message": "a configuration error" + } +} \ No newline at end of file diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error-nomessage.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error-nomessage.sarif.json new file mode 100644 index 0000000000..512273cb66 --- /dev/null +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error-nomessage.sarif.json @@ -0,0 +1,8 @@ +{ + "processing-errors": { + "filename": "file", + "detail": "###REPLACE_ME###", + "message": "NullPointerException: null" + }, + "config-errors": {} +} \ No newline at end of file diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error.sarif.json new file mode 100644 index 0000000000..432b411e08 --- /dev/null +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error.sarif.json @@ -0,0 +1,8 @@ +{ + "processing-errors": { + "filename": "file", + "detail": "###REPLACE_ME###", + "message": "RuntimeException: Error" + }, + "config-errors": {} +} \ No newline at end of file diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json index 3b1236529c..d3ed7b6508 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json @@ -9,16 +9,6 @@ "version": "unknown", "informationUri": "https://pmd.github.io/pmd/", "rules": [ - { - "id": "Foo", - "shortDescription": { - "text": "blah" - }, - "properties": { - "ruleset": "RuleSet", - "priority": 5 - } - }, { "id": "Boo", "shortDescription": { @@ -28,36 +18,24 @@ "ruleset": "RuleSet", "priority": 1 } + }, + { + "id": "Foo", + "shortDescription": { + "text": "blah" + }, + "properties": { + "ruleset": "RuleSet", + "priority": 5 + } } ] } }, "results": [ - { - "ruleId": "Foo", - "ruleIndex": 0, - "message": { - "text": "blah" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "notAvailable.ext" - }, - "region": { - "startLine": 1, - "startColumn": 1, - "endLine": 1, - "endColumn": 1 - } - } - } - ] - }, { "ruleId": "Boo", - "ruleIndex": 1, + "ruleIndex": 0, "message": { "text": "blah" }, @@ -76,6 +54,28 @@ } } ] + }, + { + "ruleId": "Foo", + "ruleIndex": 1, + "message": { + "text": "blah" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "notAvailable.ext" + }, + "region": { + "startLine": 1, + "startColumn": 1, + "endLine": 1, + "endColumn": 1 + } + } + } + ] } ] } From 69bd89884fb8fd849d6698f3ea27a0037ca9bcb4 Mon Sep 17 00:00:00 2001 From: Manuel Moya Date: Tue, 19 Jan 2021 18:36:58 +0100 Subject: [PATCH 080/154] Fix checkstyle --- .../net/sourceforge/pmd/renderers/SarifLogBuilder.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java index b7cf295b28..e972ca7f1d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java @@ -23,11 +23,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import lombok.NoArgsConstructor; - -import net.sourceforge.pmd.renderers.SarifLog.Run; -import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.PMDVersion; +import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.renderers.SarifLog.Run; + +import lombok.NoArgsConstructor; @NoArgsConstructor(access = PRIVATE) class SarifLogBuilder { From 98acf1de638e28efd3fce94e984a53962c431643 Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Tue, 19 Jan 2021 10:11:52 -0800 Subject: [PATCH 081/154] Update pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlXssStyleTagUrlPatternMatchingTest.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Clément Fournier --- .../rule/security/VfHtmlXssStyleTagUrlPatternMatchingTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlXssStyleTagUrlPatternMatchingTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlXssStyleTagUrlPatternMatchingTest.java index 9f5ac72b51..8b2792c46f 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlXssStyleTagUrlPatternMatchingTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlXssStyleTagUrlPatternMatchingTest.java @@ -14,7 +14,6 @@ import org.junit.Test; */ public class VfHtmlXssStyleTagUrlPatternMatchingTest { - final VfHtmlStyleTagXssRule rule = new VfHtmlStyleTagXssRule(); @Test public void testUrlMethodPatternMatchForPositive() { From 15dd8783c7e443f9d3978bd038fd8be9455032d4 Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Tue, 19 Jan 2021 10:28:08 -0800 Subject: [PATCH 082/154] Convert Pattern to a static final member --- .../pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java index 72c95edd9c..14dd489e79 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java @@ -21,7 +21,7 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { private static final String APEX_PREFIX = "apex"; private static final EnumSet URLENCODE_JSINHTMLENCODE = EnumSet.of(ElEscapeDetector.Escaping.URLENCODE, ElEscapeDetector.Escaping.JSINHTMLENCODE); private static final EnumSet ANY_ENCODE = EnumSet.of(ElEscapeDetector.Escaping.ANY); - private static final String URL_METHOD_PATTERN = "url\\s*\\([^)]*$"; + private static final Pattern URL_METHOD_PATTERN = Pattern.compile("url\\s*\\([^)]*$", Pattern.CASE_INSENSITIVE); private final ElEscapeDetector escapeDetector = new ElEscapeDetector(); @@ -160,9 +160,7 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { // Matches: "div { background: url('", "div { background: Url ( blah" // Does not match: "div { background: url('myUrl')", "div { background: myStyle('" - return Pattern.compile(URL_METHOD_PATTERN, Pattern.CASE_INSENSITIVE) - .matcher(previousText) - .find(); + return URL_METHOD_PATTERN.matcher(previousText).find(); } } From 3e9fd7b80a54900103e4cd8c1213c7387f3b7d71 Mon Sep 17 00:00:00 2001 From: Manuel Moya Date: Wed, 20 Jan 2021 09:16:00 +0100 Subject: [PATCH 083/154] Cleaning code --- .../pmd/renderers/SarifLogBuilder.java | 22 +++++++++---------- .../pmd/renderers/SarifRenderer.java | 1 - 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java index e972ca7f1d..82f82a8d87 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java @@ -37,6 +37,17 @@ class SarifLogBuilder { return new SarifLogBuilder(); } + public SarifLogBuilder add(RuleViolation violation) { + final ReportingDescriptor ruleDescriptor = getReportingDescriptor(violation); + final Location location = getRuleViolationLocation(violation); + + final List ruleLocation = locationsByRule.containsKey(ruleDescriptor) ? locationsByRule.get(ruleDescriptor) : new ArrayList(); + ruleLocation.add(location); + locationsByRule.put(ruleDescriptor, ruleLocation); + + return this; + } + public SarifLog build() { final List rules = new ArrayList<>(locationsByRule.keySet()); @@ -56,17 +67,6 @@ class SarifLogBuilder { return SarifLog.builder().runs(runs).build(); } - public SarifLogBuilder add(RuleViolation violation) { - final ReportingDescriptor ruleDescriptor = getReportingDescriptor(violation); - final Location location = getRuleViolationLocation(violation); - - final List ruleLocation = locationsByRule.containsKey(ruleDescriptor) ? locationsByRule.get(ruleDescriptor) : new ArrayList(); - ruleLocation.add(location); - locationsByRule.put(ruleDescriptor, ruleLocation); - - return this; - } - private Result resultFrom(ReportingDescriptor rule, Integer ruleIndex, List locations) { final Result result = new Result(); result.setRuleId(rule.getId()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java index bde884dca3..4995ba0bd1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java @@ -64,7 +64,6 @@ public class SarifRenderer extends AbstractIncrementingRenderer { private void writeLog() throws IOException { final SarifLog sarifLog = sarifLogBuilder.build(); - final String json = gson.toJson(sarifLog); writer.write(json); } From 944058ff29739920d673b3b6ce2d97879c13efcf Mon Sep 17 00:00:00 2001 From: Manuel Moya Date: Wed, 20 Jan 2021 17:14:08 +0100 Subject: [PATCH 084/154] Improved sarif with runtime and configuration errors Added those in toolConfigurationNotifications and toolExecutionNotifications components of invocations (part of sarif standard) --- .../sourceforge/pmd/renderers/SarifLog.java | 102 ++++++++++++ .../pmd/renderers/SarifLogBuilder.java | 145 +++++++++++++----- .../pmd/renderers/SarifRenderer.java | 43 ++---- .../pmd/renderers/sarif/empty.sarif.json | 9 +- .../sarif/expected-configerror.sarif.json | 37 ++++- .../sarif/expected-error-nomessage.sarif.json | 46 +++++- .../renderers/sarif/expected-error.sarif.json | 46 +++++- .../sarif/expected-multiple.sarif.json | 7 + .../pmd/renderers/sarif/expected.sarif.json | 7 + 9 files changed, 353 insertions(+), 89 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java index c470ff0e03..1d8db3e74d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLog.java @@ -42,6 +42,7 @@ public class SarifLog { */ @Data @Accessors(chain = true) + @Builder public static class Location { /** @@ -59,6 +60,7 @@ public class SarifLog { * Specifies the location of an artifact. */ @Data + @Builder public static class ArtifactLocation { /** @@ -85,6 +87,7 @@ public class SarifLog { */ @Data @Accessors(chain = true) + @Builder public static class PhysicalLocation { /** @@ -102,6 +105,7 @@ public class SarifLog { * Key/value pairs that provide additional information about the object. */ @Data + @Builder public static class PropertyBag { /** @@ -120,6 +124,7 @@ public class SarifLog { */ @Data @Accessors(chain = true) + @Builder public static class Region { /** @@ -148,6 +153,7 @@ public class SarifLog { */ @Data @Accessors(chain = true) + @Builder public static class Result { /** @@ -183,6 +189,7 @@ public class SarifLog { */ @Data @Accessors(chain = true) + @Builder public static class Message { /** @@ -222,6 +229,12 @@ public class SarifLog { */ @Singular private List results; + + /** + * The set of invocations providing information about the tool execution such as configuration errors or runtime + * exceptions + */ + private List invocations; } /** @@ -271,6 +284,7 @@ public class SarifLog { */ @Data @Accessors(chain = true) + @Builder public static class ReportingDescriptor { /** @@ -324,6 +338,7 @@ public class SarifLog { @Data @NoArgsConstructor @AllArgsConstructor + @Builder public static class MultiformatMessage { /** @@ -340,4 +355,91 @@ public class SarifLog { this.text = text; } } + + /** + * A exception information object, for the tool runtime errors. + */ + @Data + @Builder(toBuilder = true) + public static class Exception { + /** + * A plain text message string or format string. + */ + private String message; + } + + /** + * A associated rule to the toolConfigurationNotification. + */ + @Data + @Builder(toBuilder = true) + public static class AssociatedRule { + /** + * The stable, unique identifier of the rule, if any, to which this result is relevant. + */ + private String id; + } + + /** + * An invocation property to specify tool configuration errors. + */ + @Data + @Builder(toBuilder = true) + public static class ToolConfigurationNotification { + /** + * An associated rule + */ + private AssociatedRule associatedRule; + + /** + * A message component to detail the configuration error + */ + private Message message; + } + + /** + * An invocation property to specify tool runtime errors. + */ + @Data + @Builder(toBuilder = true) + public static class ToolExecutionNotification { + /** + * A list of related locations to the error + */ + private List locations; + + /** + * A message component to detail the runtime error + */ + private Message message; + + /** + * A exception component to detail the tool exception + */ + private Exception exception; + } + + /** + * An invocation component to specify tool invocation details/errors. + */ + @Data + @Builder(toBuilder = true) + public static class Invocation { + /** + * An indicator of execution status + */ + private Boolean executionSuccessful; + + /** + * A list of associated tool configuration errors + */ + private List toolConfigurationNotifications; + + /** + * A list of associated tool runtime errors + */ + private List toolExecutionNotifications; + } + + } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java index 82f82a8d87..352c7b47f1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifLogBuilder.java @@ -6,7 +6,10 @@ package net.sourceforge.pmd.renderers; import static lombok.AccessLevel.PRIVATE; import static net.sourceforge.pmd.renderers.SarifLog.ArtifactLocation; +import static net.sourceforge.pmd.renderers.SarifLog.AssociatedRule; import static net.sourceforge.pmd.renderers.SarifLog.Component; +import static net.sourceforge.pmd.renderers.SarifLog.Exception; +import static net.sourceforge.pmd.renderers.SarifLog.Invocation; import static net.sourceforge.pmd.renderers.SarifLog.Location; import static net.sourceforge.pmd.renderers.SarifLog.Message; import static net.sourceforge.pmd.renderers.SarifLog.MultiformatMessage; @@ -15,7 +18,10 @@ import static net.sourceforge.pmd.renderers.SarifLog.PropertyBag; import static net.sourceforge.pmd.renderers.SarifLog.Region; import static net.sourceforge.pmd.renderers.SarifLog.ReportingDescriptor; import static net.sourceforge.pmd.renderers.SarifLog.Result; +import static net.sourceforge.pmd.renderers.SarifLog.Run; import static net.sourceforge.pmd.renderers.SarifLog.Tool; +import static net.sourceforge.pmd.renderers.SarifLog.ToolConfigurationNotification; +import static net.sourceforge.pmd.renderers.SarifLog.ToolExecutionNotification; import java.util.ArrayList; import java.util.Collections; @@ -24,14 +30,16 @@ import java.util.List; import java.util.Map; import net.sourceforge.pmd.PMDVersion; +import net.sourceforge.pmd.Report; import net.sourceforge.pmd.RuleViolation; -import net.sourceforge.pmd.renderers.SarifLog.Run; import lombok.NoArgsConstructor; @NoArgsConstructor(access = PRIVATE) class SarifLogBuilder { private final Map> locationsByRule = new HashMap<>(); + private final List toolConfigurationNotifications = new ArrayList<>(); + private final List toolExecutionNotifications = new ArrayList<>(); public static SarifLogBuilder sarifLogBuilder() { return new SarifLogBuilder(); @@ -48,6 +56,56 @@ class SarifLogBuilder { return this; } + public SarifLogBuilder addRunTimeError(Report.ProcessingError error) { + ArtifactLocation artifactLocation = ArtifactLocation.builder() + .uri(error.getFile()) + .build(); + + PhysicalLocation physicalLocation = PhysicalLocation.builder() + .artifactLocation(artifactLocation) + .build(); + + Location location = Location + .builder() + .physicalLocation(physicalLocation) + .build(); + + Message message = Message.builder() + .text(error.getMsg()) + .build(); + + Exception exception = Exception.builder() + .message(error.getDetail()) + .build(); + + ToolExecutionNotification toolExecutionNotification = ToolExecutionNotification.builder() + .locations(Collections.singletonList(location)) + .message(message) + .exception(exception) + .build(); + + toolExecutionNotifications.add(toolExecutionNotification); + + return this; + } + + public SarifLogBuilder addConfigurationError(Report.ConfigurationError error) { + AssociatedRule associatedRule = AssociatedRule.builder() + .id(error.rule().getName()) + .build(); + + Message message = Message.builder().text(error.issue()).build(); + + ToolConfigurationNotification toolConfigurationNotification = ToolConfigurationNotification.builder() + .associatedRule(associatedRule) + .message(message) + .build(); + + toolConfigurationNotifications.add(toolConfigurationNotification); + + return this; + } + public SarifLog build() { final List rules = new ArrayList<>(locationsByRule.keySet()); @@ -60,65 +118,78 @@ class SarifLogBuilder { final Component driver = getDriverComponent().toBuilder().rules(rules).build(); final Tool tool = Tool.builder().driver(driver).build(); - final Run run = Run.builder().tool(tool).results(results).build(); + final Invocation invocation = Invocation.builder() + .toolExecutionNotifications(toolExecutionNotifications) + .toolConfigurationNotifications(toolConfigurationNotifications) + .executionSuccessful(isExecutionSuccessful()) + .build(); + final Run run = Run.builder() + .tool(tool) + .results(results) + .invocations(Collections.singletonList(invocation)) + .build(); - List runs = Collections.singletonList(run); + List runs = Collections.singletonList(run); return SarifLog.builder().runs(runs).build(); } + private boolean isExecutionSuccessful() { + return toolExecutionNotifications.isEmpty() && toolConfigurationNotifications.isEmpty(); + } + private Result resultFrom(ReportingDescriptor rule, Integer ruleIndex, List locations) { - final Result result = new Result(); - result.setRuleId(rule.getId()); - result.setRuleIndex(ruleIndex); + final Result result = Result.builder() + .ruleId(rule.getId()) + .ruleIndex(ruleIndex) + .build(); + + final Message message = Message.builder() + .text(rule.getShortDescription().getText()) + .build(); - final Message message = new Message(); - message.setText(rule.getShortDescription().getText()); result.setMessage(message); - result.setLocations(locations); return result; } private Location getRuleViolationLocation(RuleViolation rv) { - ArtifactLocation artifactLocation = new ArtifactLocation(); - artifactLocation.setUri(rv.getFilename()); + ArtifactLocation artifactLocation = ArtifactLocation.builder() + .uri(rv.getFilename()) + .build(); - Region region = new Region(); - region.setStartLine(rv.getBeginLine()); - region.setEndLine(rv.getEndLine()); - region.setStartColumn(rv.getBeginColumn()); - region.setEndColumn(rv.getEndColumn()); + Region region = Region.builder() + .startLine(rv.getBeginLine()) + .endLine(rv.getEndLine()) + .startColumn(rv.getBeginColumn()) + .endColumn(rv.getEndColumn()) + .build(); - PhysicalLocation physicalLocation = new PhysicalLocation(); - physicalLocation.setArtifactLocation(artifactLocation); - physicalLocation.setRegion(region); + PhysicalLocation physicalLocation = PhysicalLocation.builder() + .artifactLocation(artifactLocation) + .region(region) + .build(); - Location result = new Location(); - result.setPhysicalLocation(physicalLocation); - - return result; + return Location.builder() + .physicalLocation(physicalLocation) + .build(); } private ReportingDescriptor getReportingDescriptor(RuleViolation rv) { - ReportingDescriptor result = new ReportingDescriptor(); - - result.setId(rv.getRule().getName()); - result.setShortDescription(new MultiformatMessage(rv.getDescription())); - result.setHelpUri(rv.getRule().getExternalInfoUrl()); - result.setProperties(getRuleProperties(rv)); - - return result; + return ReportingDescriptor.builder() + .id(rv.getRule().getName()) + .shortDescription(new MultiformatMessage(rv.getDescription())) + .helpUri(rv.getRule().getExternalInfoUrl()) + .properties(getRuleProperties(rv)) + .build(); } private PropertyBag getRuleProperties(RuleViolation rv) { - PropertyBag result = new PropertyBag(); - - result.setRuleset(rv.getRule().getRuleSetName()); - result.setPriority(rv.getRule().getPriority().getPriority()); - - return result; + return PropertyBag.builder() + .ruleset(rv.getRule().getRuleSetName()) + .priority(rv.getRule().getPriority().getPriority()) + .build(); } private Component getDriverComponent() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java index 4995ba0bd1..5764f874db 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/SarifRenderer.java @@ -5,9 +5,7 @@ package net.sourceforge.pmd.renderers; import java.io.IOException; -import java.util.HashMap; import java.util.Iterator; -import java.util.Map; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.RuleViolation; @@ -51,15 +49,18 @@ public class SarifRenderer extends AbstractIncrementingRenderer { @Override public void end() throws IOException { - if (!hasErrors()) { - writeLog(); - } else { - writeErrors(); - } + addErrors(); + writeLog(); } - private boolean hasErrors() { - return !errors.isEmpty() || !configErrors.isEmpty(); + private void addErrors() { + for (Report.ProcessingError error : this.errors) { + sarifLogBuilder.addRunTimeError(error); + } + + for (Report.ConfigurationError error: this.configErrors) { + sarifLogBuilder.addConfigurationError(error); + } } private void writeLog() throws IOException { @@ -67,28 +68,4 @@ public class SarifRenderer extends AbstractIncrementingRenderer { final String json = gson.toJson(sarifLog); writer.write(json); } - - private void writeErrors() throws IOException { - final Map> errors = new HashMap<>(); - final Map processingErrors = new HashMap<>(); - final Map configErrors = new HashMap<>(); - - for (Report.ProcessingError error : this.errors) { - processingErrors.put("filename", error.getFile()); - processingErrors.put("message", error.getMsg()); - processingErrors.put("detail", error.getDetail()); - } - - for (Report.ConfigurationError error: this.configErrors) { - configErrors.put("rule", error.rule().getName()); - configErrors.put("ruleset", error.rule().getRuleSetName()); - configErrors.put("message", error.issue()); - } - - errors.put("processing-errors", processingErrors); - errors.put("config-errors", configErrors); - - final String json = gson.toJson(errors); - writer.write(json); - } } diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/empty.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/empty.sarif.json index fd4c900be2..91178676f0 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/empty.sarif.json +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/empty.sarif.json @@ -11,7 +11,14 @@ "rules": [] } }, - "results": [] + "results": [], + "invocations": [ + { + "executionSuccessful": true, + "toolConfigurationNotifications": [], + "toolExecutionNotifications": [] + } + ] } ] } \ No newline at end of file diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-configerror.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-configerror.sarif.json index 6da4f10247..16adc0caf0 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-configerror.sarif.json +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-configerror.sarif.json @@ -1,8 +1,33 @@ { - "processing-errors": {}, - "config-errors": { - "ruleset": "RuleSet", - "rule": "Foo", - "message": "a configuration error" - } + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "PMD", + "version": "unknown", + "informationUri": "https://pmd.github.io/pmd/", + "rules": [] + } + }, + "results": [], + "invocations": [ + { + "executionSuccessful": false, + "toolConfigurationNotifications": [ + { + "associatedRule": { + "id": "Foo" + }, + "message": { + "text": "a configuration error" + } + } + ], + "toolExecutionNotifications": [] + } + ] + } + ] } \ No newline at end of file diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error-nomessage.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error-nomessage.sarif.json index 512273cb66..6b7ad4c6fc 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error-nomessage.sarif.json +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error-nomessage.sarif.json @@ -1,8 +1,42 @@ { - "processing-errors": { - "filename": "file", - "detail": "###REPLACE_ME###", - "message": "NullPointerException: null" - }, - "config-errors": {} + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "PMD", + "version": "unknown", + "informationUri": "https://pmd.github.io/pmd/", + "rules": [] + } + }, + "results": [], + "invocations": [ + { + "executionSuccessful": false, + "toolConfigurationNotifications": [], + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "file" + } + } + } + ], + "message": { + "text": "NullPointerException: null" + }, + "exception": { + "message": "###REPLACE_ME###" + } + } + ] + } + ] + } + ] } \ No newline at end of file diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error.sarif.json index 432b411e08..84ef17fe38 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error.sarif.json +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-error.sarif.json @@ -1,8 +1,42 @@ { - "processing-errors": { - "filename": "file", - "detail": "###REPLACE_ME###", - "message": "RuntimeException: Error" - }, - "config-errors": {} + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "PMD", + "version": "unknown", + "informationUri": "https://pmd.github.io/pmd/", + "rules": [] + } + }, + "results": [], + "invocations": [ + { + "executionSuccessful": false, + "toolConfigurationNotifications": [], + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "file" + } + } + } + ], + "message": { + "text": "RuntimeException: Error" + }, + "exception": { + "message": "###REPLACE_ME###" + } + } + ] + } + ] + } + ] } \ No newline at end of file diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json index d3ed7b6508..f8d18a67d4 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json @@ -77,6 +77,13 @@ } ] } + ], + "invocations": [ + { + "executionSuccessful": true, + "toolConfigurationNotifications": [], + "toolExecutionNotifications": [] + } ] } ] diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected.sarif.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected.sarif.json index 539493852f..b0419d83ce 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected.sarif.json +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected.sarif.json @@ -45,6 +45,13 @@ } ] } + ], + "invocations": [ + { + "executionSuccessful": true, + "toolConfigurationNotifications": [], + "toolExecutionNotifications": [] + } ] } ] From 4be2562f01f90ece6a4ddad69c165b108b29b2b5 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 21 Jan 2021 11:03:51 +0100 Subject: [PATCH 085/154] Fixups for #2968 - Use better typeres - Add test cases for records and local var --- .../rule/AbstractInefficientZeroCheck.java | 9 +- .../UseCollectionIsEmptyRule.java | 22 ++--- .../InefficientEmptyStringCheckRule.java | 2 +- .../xml/UseCollectionIsEmpty.xml | 82 +++++++++++++++---- 4 files changed, 83 insertions(+), 32 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractInefficientZeroCheck.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractInefficientZeroCheck.java index 499e5efc9f..96283b47c8 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractInefficientZeroCheck.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractInefficientZeroCheck.java @@ -13,10 +13,10 @@ import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; import net.sourceforge.pmd.lang.java.ast.ASTRelationalExpression; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; +import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; /** @@ -62,10 +62,9 @@ public abstract class AbstractInefficientZeroCheck extends AbstractJavaRule { @Override public Object visit(ASTVariableDeclaratorId node, Object data) { - Node nameNode = node.getTypeNameNode(); - if (nameNode == null || nameNode instanceof ASTPrimitiveType - || node.getNameDeclaration() == null - || !appliesToClassName(node.getNameDeclaration().getTypeImage())) { + VariableNameDeclaration varDecl = node.getNameDeclaration(); + if (varDecl == null || varDecl.getType() == null + || !appliesToClassName(varDecl.getType().getName())) { return data; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java index b5acfc0195..44ffe0d43e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java @@ -13,7 +13,6 @@ import java.util.Map; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTEnumBody; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTName; @@ -21,11 +20,13 @@ import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; import net.sourceforge.pmd.lang.java.ast.ASTResultType; +import net.sourceforge.pmd.lang.java.ast.ASTType; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; import net.sourceforge.pmd.lang.java.rule.AbstractInefficientZeroCheck; import net.sourceforge.pmd.lang.java.symboltable.ClassScope; import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration; +import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; import net.sourceforge.pmd.util.CollectionUtil; @@ -80,11 +81,11 @@ public class UseCollectionIsEmptyRule extends AbstractInefficientZeroCheck { private boolean isSizeMethodCall(ASTPrimarySuffix primarySuffix) { String calledMethodName = primarySuffix.getImage(); - return calledMethodName != null && calledMethodName.endsWith("size"); + return calledMethodName != null && "size".equals(calledMethodName); } private boolean isCalledOnCollection(ASTPrimarySuffix primarySuffix) { - ASTClassOrInterfaceType calledOnType = getTypeOfVariable(primarySuffix); + JavaTypeDefinition calledOnType = getTypeOfVariable(primarySuffix); if (calledOnType == null) { calledOnType = getTypeOfMethodCall(primarySuffix); } @@ -92,11 +93,11 @@ public class UseCollectionIsEmptyRule extends AbstractInefficientZeroCheck { && CollectionUtil.isCollectionType(calledOnType.getType(), true); } - private ASTClassOrInterfaceType getTypeOfVariable(ASTPrimarySuffix primarySuffix) { + private JavaTypeDefinition getTypeOfVariable(ASTPrimarySuffix primarySuffix) { ASTPrimaryExpression primaryExpression = primarySuffix.getFirstParentOfType(ASTPrimaryExpression.class); ASTPrimaryPrefix varPrefix = primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); if (prefixWithNoModifiers(varPrefix)) { - return varPrefix.getFirstDescendantOfType(ASTClassOrInterfaceType.class); + return varPrefix.getTypeDefinition(); } String varName = getVariableNameBySuffix(primaryExpression); return varName != null ? getTypeOfVariableByName(varName, primaryExpression) : null; @@ -112,7 +113,7 @@ public class UseCollectionIsEmptyRule extends AbstractInefficientZeroCheck { return varSuffix.getImage(); } - private ASTClassOrInterfaceType getTypeOfVariableByName(String varName, ASTPrimaryExpression expr) { + private JavaTypeDefinition getTypeOfVariableByName(String varName, ASTPrimaryExpression expr) { Node classOrEnumBody = expr.getFirstParentOfType(ASTClassOrInterfaceBody.class); if (classOrEnumBody == null) { classOrEnumBody = expr.getFirstParentOfType(ASTEnumBody.class); @@ -120,14 +121,14 @@ public class UseCollectionIsEmptyRule extends AbstractInefficientZeroCheck { List varDeclarators = classOrEnumBody.findDescendantsOfType(ASTVariableDeclarator.class); for (ASTVariableDeclarator varDeclarator : varDeclarators) { if (varDeclarator.getName().equals(varName)) { - return varDeclarator.getParent().getFirstDescendantOfType(ASTClassOrInterfaceType.class); + return varDeclarator.getVariableId().getTypeNode().getTypeDefinition(); } } return null; } - private ASTClassOrInterfaceType getTypeOfMethodCall(ASTPrimarySuffix node) { - ASTClassOrInterfaceType type = null; + private JavaTypeDefinition getTypeOfMethodCall(ASTPrimarySuffix node) { + JavaTypeDefinition type = null; ASTName methodName = node.getParent().getFirstChildOfType(ASTPrimaryPrefix.class) .getFirstChildOfType(ASTName.class); if (methodName != null) { @@ -137,7 +138,8 @@ public class UseCollectionIsEmptyRule extends AbstractInefficientZeroCheck { if (e.getKey().getName().equals(methodName.getImage())) { type = e.getKey().getNode().getFirstParentOfType(ASTMethodDeclaration.class) .getFirstChildOfType(ASTResultType.class) - .getFirstDescendantOfType(ASTClassOrInterfaceType.class); + .getFirstDescendantOfType(ASTType.class) + .getTypeDefinition(); break; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java index 63ad694116..d0656338a6 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java @@ -57,7 +57,7 @@ public class InefficientEmptyStringCheckRule extends AbstractInefficientZeroChec @Override public boolean appliesToClassName(String name) { - return "String".equals(name); + return "String".equals(name) || "java.lang.String".equals(name); } @Override diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml index d9d1f5002e..fccbbc4ce0 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml @@ -7,8 +7,10 @@ fail, == 0 1 - 3 + 5 ok, isEmpty 0 fail, != 0 1 - 3 + 5 ok, !isEmpty 0 fail, != 0 1 - 3 + 5 ok, !isEmpty 0 fail, 0 == 1 - 3 + 5 fail, > 0 1 - 3 + 5 0){ @@ -133,6 +149,8 @@ public class Foo { ok, in expression 0 ok, in expression 0 #1304 UseCollectionIsEmpty false positive comparing to 1 0 c = new ArrayList(); @@ -354,11 +376,7 @@ public class Foo { - ##2833 NPE in UseCollectionIsEmptyRule with enums (sanity check) + #2833 NPE in UseCollectionIsEmptyRule with enums (sanity check) 1 - 12 + 11 list; private String size; @@ -405,4 +422,37 @@ public enum ComponentSize { ]]> + + #2833 test with records + 1 + 5 + theList) { + public CollectionRecord { + if (theList.size() == 0) throw new IllegalArgumentException("empty list"); + if (theList.isEmpty()) throw new IllegalArgumentException("empty list"); + } +} + ]]> + java 15-preview + + + + #2833 test local var + 1 + 6 + (); + if (theList.size() == 0) throw new IllegalArgumentException("empty list"); + if (theList.isEmpty()) throw new IllegalArgumentException("empty list"); + } +} + ]]> + From 76566b75bb0b935932357bc1e5f67f8fe5a4c7e5 Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Thu, 21 Jan 2021 11:06:29 +0100 Subject: [PATCH 086/154] Minor changes resulting from review --- .../main/java/net/sourceforge/pmd/lang/ast/GenericToken.java | 3 +++ .../src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java | 1 + 2 files changed, 4 insertions(+) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java index 479fa67a81..04ff174b18 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.ast; +import net.sourceforge.pmd.annotation.Experimental; + /** * Represents a language-independent token such as constants, values language reserved keywords, or comments. */ @@ -57,5 +59,6 @@ public interface GenericToken { * * The semantics of this kind depend on the language. */ + @Experimental int getKind(); } diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java index e0d42d85bd..02e7c4121d 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java @@ -35,6 +35,7 @@ public class CPPTokenizer extends JavaCCTokenizer { * @param properties the properties * @see #OPTION_SKIP_BLOCKS * @see #OPTION_SKIP_BLOCKS_PATTERN + * @see #OPTION_IGNORE_LITERAL_SEQUENCES */ public void setProperties(Properties properties) { skipBlocks = Boolean.parseBoolean(properties.getProperty(OPTION_SKIP_BLOCKS, Boolean.TRUE.toString())); From 7f1f374dc52bdc03f238a8be8f968ac6edcc293e Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 21 Jan 2021 09:21:00 +0100 Subject: [PATCH 087/154] Fixups for #2963 - Add documentation for CPD cli - Add javadoc for the tokenizer options - GenericToken::getKind is @Experimental --- docs/pages/pmd/userdocs/cpd/cpd.md | 2 +- docs/pages/release_notes.md | 12 ++++++++++-- .../net/sourceforge/pmd/cpd/token/AntlrToken.java | 2 ++ .../net/sourceforge/pmd/lang/ast/GenericToken.java | 7 ++++++- .../java/net/sourceforge/pmd/cpd/CPPTokenizer.java | 3 ++- .../java/net/sourceforge/pmd/cpd/CsTokenizer.java | 12 ++++++++++-- 6 files changed, 31 insertions(+), 7 deletions(-) diff --git a/docs/pages/pmd/userdocs/cpd/cpd.md b/docs/pages/pmd/userdocs/cpd/cpd.md index 8d94294926..786885f512 100644 --- a/docs/pages/pmd/userdocs/cpd/cpd.md +++ b/docs/pages/pmd/userdocs/cpd/cpd.md @@ -119,7 +119,7 @@ Novice as much as advanced readers may want to [read on on Refactoring Guru](htt {% include custom/cli_option_row.html options="--ignore-literal-sequences" description="Ignore sequences of literals (common e.g. in list initializers)" default="false" - languages="C#" + languages="C#, C++" %} {% include custom/cli_option_row.html options="--ignore-usings" description="Ignore `using` directives in C# when comparing text" diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index cf766b7686..52dd9dd3b6 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -38,7 +38,7 @@ This is a {{ site.pmd.release_type }} release. ### API Changes -#### Deprecated API +#### Deprecated APIs * {% jdoc !!java::lang.java.ast.ASTPackageDeclaration#getPackageNameImage() %}, {% jdoc !!java::lang.java.ast.ASTTypeParameter#getParameterName() %} @@ -48,7 +48,15 @@ This is a {{ site.pmd.release_type }} release. and {% jdoc !!java::lang.java.ast.ASTClassOrInterfaceBody#isEnumChild() %}, refs [#905](https://github.com/pmd/pmd/issues/905) -#### Internal API +#### Experimental APIs + +* The method {% jdoc !!core::lang.ast.GenericToken#getKind() %} has been added as experimental. This + unifies the token interface for both JavaCC and Antlr. The already existing method + {% jdoc !!core::cpd.token.AntlrToken#getKind() %} is therefore experimental as well. The + returned constant depends on the actual language and might change whenever the grammar + of the language is changed. + +#### Internal APIs Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0. You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrToken.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrToken.java index 0a36e9419a..d21a7c544f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrToken.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrToken.java @@ -10,6 +10,7 @@ import java.util.regex.Pattern; import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.Token; +import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.lang.ast.GenericToken; /** @@ -133,6 +134,7 @@ public class AntlrToken implements GenericToken { } @Override + @Experimental public int getKind() { return token.getType(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java index 04ff174b18..fee80b175c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java @@ -56,8 +56,13 @@ public interface GenericToken { /** * Gets a unique integer representing the kind of token this is. - * * The semantics of this kind depend on the language. + * + *

    Note: This is an experimental API. + * + *

    The returned constants can be looked up in the language's "*ParserConstants", + * e.g. CppParserConstants or JavaParserConstants. These constants are considered + * internal API and may change at any time when the language's grammar is changed. */ @Experimental int getKind(); diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java index 02e7c4121d..500b6ab551 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java @@ -49,7 +49,8 @@ public class CPPTokenizer extends JavaCCTokenizer { skipBlocksEnd = split[1]; } } - ignoreLiteralSequences = Boolean.parseBoolean(properties.getProperty(OPTION_IGNORE_LITERAL_SEQUENCES, "false")); + ignoreLiteralSequences = Boolean.parseBoolean(properties.getProperty(OPTION_IGNORE_LITERAL_SEQUENCES, + Boolean.FALSE.toString())); } private String maybeSkipBlocks(String test) throws IOException { diff --git a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java index 5ae96cbfa1..e70313314d 100644 --- a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java +++ b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java @@ -21,9 +21,17 @@ public class CsTokenizer extends AntlrTokenizer { private boolean ignoreUsings = false; private boolean ignoreLiteralSequences = false; + /** + * Sets the possible options for the C# tokenizer. + * + * @param properties the properties + * @see #IGNORE_USINGS + * @see #OPTION_IGNORE_LITERAL_SEQUENCES + */ public void setProperties(Properties properties) { - ignoreUsings = Boolean.parseBoolean(properties.getProperty(IGNORE_USINGS, "false")); - ignoreLiteralSequences = Boolean.parseBoolean(properties.getProperty(OPTION_IGNORE_LITERAL_SEQUENCES, "false")); + ignoreUsings = Boolean.parseBoolean(properties.getProperty(IGNORE_USINGS, Boolean.FALSE.toString())); + ignoreLiteralSequences = Boolean.parseBoolean(properties.getProperty(OPTION_IGNORE_LITERAL_SEQUENCES, + Boolean.FALSE.toString())); } public void setIgnoreUsings(boolean ignoreUsings) { From c42f0496ca22f530726969623dd4258fd5465f91 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 21 Jan 2021 09:26:38 +0100 Subject: [PATCH 088/154] [doc] Update release notes, refs #2963 --- docs/pages/release_notes.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 1e8ce00b4e..190e7bca06 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -14,6 +14,12 @@ This is a {{ site.pmd.release_type }} release. ### New and noteworthy +#### CPD + +* The C++ module now supports the new option [`--ignore-literal-sequences`](https://pmd.github.io/latest/pmd_userdocs_cpd.html#-ignore-literal-sequences), + which can be used to avoid detection of some uninteresting clones. This options has been + introduced with PMD 6.30.0 for C# and is now available for C++ as well. See [#2963](https://github.com/pmd/pmd/pull/2963). + #### New Rules * The new Apex rule {% rule "apex/errorprone/OverrideBothEqualsAndHashcode" %} brings the well known Java rule @@ -44,6 +50,7 @@ This is a {{ site.pmd.release_type }} release. * [#2666](https://github.com/pmd/pmd/pull/2666): \[swift] Manage swift5 string literals - [kenji21](https://github.com/kenji21) * [#2959](https://github.com/pmd/pmd/pull/2959): \[apex] New Rule: override equals and hashcode rule - [recdevs](https://github.com/recdevs) +* [#2963](https://github.com/pmd/pmd/pull/2963): \[cpp] Add option to ignore sequences of literals - [Maikel Steneker](https://github.com/maikelsteneker) * [#2964](https://github.com/pmd/pmd/pull/2964): \[cs] Update C# grammar for additional C# 7 and C# 8 features - [Maikel Steneker](https://github.com/maikelsteneker) * [#2965](https://github.com/pmd/pmd/pull/2965): \[cs] Improvements for ignore sequences of literals functionality - [Maikel Steneker](https://github.com/maikelsteneker) * [#2983](https://github.com/pmd/pmd/pull/2983): \[java] LiteralsFirstInComparisons should consider constant fields - [Ozan Gulle](https://github.com/ozangulle) From 248d4d78e7a1c7ee5a21796b761b88fe709c8670 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 21 Jan 2021 11:48:51 +0100 Subject: [PATCH 089/154] [doc] Update release notes, refs #2968, refs #2833 --- docs/pages/release_notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 190e7bca06..7dab37f053 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -33,6 +33,7 @@ This is a {{ site.pmd.release_type }} release. * [#2994](https://github.com/pmd/pmd/pull/2994): \[core] Fix code climate severity strings * java-bestpractices * [#575](https://github.com/pmd/pmd/issues/575): \[java] LiteralsFirstInComparisons should consider constant fields + * [#2833](https://github.com/pmd/pmd/issues/2833): \[java] NPE in UseCollectionIsEmptyRule with enums * java-codestyle * [#2960](https://github.com/pmd/pmd/issues/2960): \[java] Thread issue in MethodNamingConventionsRule @@ -53,6 +54,7 @@ This is a {{ site.pmd.release_type }} release. * [#2963](https://github.com/pmd/pmd/pull/2963): \[cpp] Add option to ignore sequences of literals - [Maikel Steneker](https://github.com/maikelsteneker) * [#2964](https://github.com/pmd/pmd/pull/2964): \[cs] Update C# grammar for additional C# 7 and C# 8 features - [Maikel Steneker](https://github.com/maikelsteneker) * [#2965](https://github.com/pmd/pmd/pull/2965): \[cs] Improvements for ignore sequences of literals functionality - [Maikel Steneker](https://github.com/maikelsteneker) +* [#2968](https://github.com/pmd/pmd/pull/2968): \[java] NPE in UseCollectionIsEmptyRule with enums - [foxmason](https://github.com/foxmason) * [#2983](https://github.com/pmd/pmd/pull/2983): \[java] LiteralsFirstInComparisons should consider constant fields - [Ozan Gulle](https://github.com/ozangulle) * [#2994](https://github.com/pmd/pmd/pull/2994): \[core] Fix code climate severity strings - [Vincent Maurin](https://github.com/vmaurin) From dc4cdf696e22ce630295df84c601eb1903063f1a Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 21 Jan 2021 15:01:58 +0100 Subject: [PATCH 090/154] Fixups for #3005 - ElEscapeDetector is utility class now - Improved description and example of new rule --- .../rule/security/VfHtmlStyleTagXssRule.java | 24 +++++++------- .../vf/rule/security/VfUnescapeElRule.java | 33 ++++++++++--------- .../security/internal/ElEscapeDetector.java | 32 +++++++++--------- .../main/resources/category/vf/security.xml | 28 +++++++++++----- 4 files changed, 66 insertions(+), 51 deletions(-) diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java index 14dd489e79..fed109fdcc 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfHtmlStyleTagXssRule.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ @@ -23,8 +23,6 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { private static final EnumSet ANY_ENCODE = EnumSet.of(ElEscapeDetector.Escaping.ANY); private static final Pattern URL_METHOD_PATTERN = Pattern.compile("url\\s*\\([^)]*$", Pattern.CASE_INSENSITIVE); - private final ElEscapeDetector escapeDetector = new ElEscapeDetector(); - public VfHtmlStyleTagXssRule() { addRuleChainVisit(ASTElExpression.class); } @@ -34,12 +32,13 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { * placed inside an ASTContent, which in turn is placed inside * an ASTElement, where the element is not an inbuilt vf tag. * + *

    {@code
          * 
          *     
          *         
          *     
          * 
    -     *
    +     * }
    */ @Override public Object visit(ASTElExpression node, Object data) { @@ -81,7 +80,7 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { ASTElement elementNode, Object data) { final String previousText = getPreviousText(contentNode, node); - final boolean isWithinSafeResource = escapeDetector.startsWithSafeResource(node); + final boolean isWithinSafeResource = ElEscapeDetector.startsWithSafeResource(node); // if El is inside a tag // and is not surrounded by a safe resource, check for violations @@ -104,7 +103,7 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { private void verifyEncodingWithinUrl(ASTElExpression elExpressionNode, Object data) { // only allow URLENCODING or JSINHTMLENCODING - if (escapeDetector.doesElContainAnyUnescapedIdentifiers( + if (ElEscapeDetector.doesElContainAnyUnescapedIdentifiers( elExpressionNode, URLENCODE_JSINHTMLENCODE)) { addViolationWithMessage( @@ -116,7 +115,7 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { } private void verifyEncodingWithoutUrl(ASTElExpression elExpressionNode, Object data) { - if (escapeDetector.doesElContainAnyUnescapedIdentifiers( + if (ElEscapeDetector.doesElContainAnyUnescapedIdentifiers( elExpressionNode, ANY_ENCODE)) { addViolationWithMessage( @@ -132,15 +131,18 @@ public class VfHtmlStyleTagXssRule extends AbstractVfRule { } /** - * Get text content within style tag that leads upto the ElExpression. + * Get text content within style tag that leads up to the ElExpression. * For example, in this snippet: - * + * </style> + * * - * getPreviousText(...) would return "\n div {\n background: url(" + * {@code getPreviousText(...)} would return "\n div {\n background: url(". * */ private String getPreviousText(ASTContent content, ASTElExpression elExpressionNode) { diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java index 7a321abd1e..283d1918c9 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java @@ -49,8 +49,6 @@ public class VfUnescapeElRule extends AbstractVfRule { private static final EnumSet JSENCODE_JSINHTMLENCODE = EnumSet.of(ElEscapeDetector.Escaping.JSENCODE, ElEscapeDetector.Escaping.JSINHTMLENCODE); private static final EnumSet ANY_ENCODE = EnumSet.of(ElEscapeDetector.Escaping.ANY); - private final ElEscapeDetector escapeDetector = new ElEscapeDetector(); - @Override public Object visit(ASTHtmlScript node, Object data) { checkIfCorrectlyEscaped(node, data); @@ -88,15 +86,18 @@ public class VfUnescapeElRule extends AbstractVfRule { } if (quoted) { // check escaping too - if (!(jsonParse || escapeDetector.startsWithSafeResource(elExpression) || escapeDetector.containsSafeFields(elExpression))) { - if (escapeDetector.doesElContainAnyUnescapedIdentifiers(elExpression, + if (!(jsonParse + || ElEscapeDetector.startsWithSafeResource(elExpression) + || ElEscapeDetector.containsSafeFields(elExpression))) { + if (ElEscapeDetector.doesElContainAnyUnescapedIdentifiers(elExpression, JSENCODE_JSINHTMLENCODE)) { addViolation(data, elExpression); } } } else { - if (!(escapeDetector.startsWithSafeResource(elExpression) || escapeDetector.containsSafeFields(elExpression))) { - final boolean hasUnscaped = escapeDetector.doesElContainAnyUnescapedIdentifiers(elExpression, + if (!(ElEscapeDetector.startsWithSafeResource(elExpression) + || ElEscapeDetector.containsSafeFields(elExpression))) { + final boolean hasUnscaped = ElEscapeDetector.doesElContainAnyUnescapedIdentifiers(elExpression, JSENCODE_JSINHTMLENCODE); if (!(jsonParse && !hasUnscaped)) { addViolation(data, elExpression); @@ -181,11 +182,11 @@ public class VfUnescapeElRule extends AbstractVfRule { break; } - if (escapeDetector.startsWithSafeResource(el)) { + if (ElEscapeDetector.startsWithSafeResource(el)) { break; } - if (escapeDetector.doesElContainAnyUnescapedIdentifiers(el, ElEscapeDetector.Escaping.URLENCODE)) { + if (ElEscapeDetector.doesElContainAnyUnescapedIdentifiers(el, ElEscapeDetector.Escaping.URLENCODE)) { isEL = true; toReport.add(el); } @@ -216,12 +217,11 @@ public class VfUnescapeElRule extends AbstractVfRule { if (ON_EVENT.matcher(name).matches()) { final List elsInVal = attr.findDescendantsOfType(ASTElExpression.class); for (ASTElExpression el : elsInVal) { - if (escapeDetector.startsWithSafeResource(el)) { + if (ElEscapeDetector.startsWithSafeResource(el)) { continue; } - if (escapeDetector.doesElContainAnyUnescapedIdentifiers(el, - ANY_ENCODE)) { + if (ElEscapeDetector.doesElContainAnyUnescapedIdentifiers(el, ANY_ENCODE)) { isEL = true; toReport.add(el); } @@ -280,11 +280,12 @@ public class VfUnescapeElRule extends AbstractVfRule { final List elsInVal = attr.findDescendantsOfType(ASTElExpression.class); for (ASTElExpression el : elsInVal) { - if (escapeDetector.startsWithSafeResource(el)) { + if (ElEscapeDetector.startsWithSafeResource(el)) { continue; } - if (escapeDetector.doesElContainAnyUnescapedIdentifiers(el, ElEscapeDetector.Escaping.HTMLENCODE)) { + if (ElEscapeDetector.doesElContainAnyUnescapedIdentifiers(el, + ElEscapeDetector.Escaping.HTMLENCODE)) { isEL = true; toReport.add(el); } @@ -347,11 +348,12 @@ public class VfUnescapeElRule extends AbstractVfRule { for (ASTAttribute attrib : innerAttributes) { final List elsInVal = attrib.findDescendantsOfType(ASTElExpression.class); for (final ASTElExpression el : elsInVal) { - if (escapeDetector.startsWithSafeResource(el)) { + if (ElEscapeDetector.startsWithSafeResource(el)) { continue; } - if (escapeDetector.doesElContainAnyUnescapedIdentifiers(el, ElEscapeDetector.Escaping.HTMLENCODE)) { + if (ElEscapeDetector.doesElContainAnyUnescapedIdentifiers(el, + ElEscapeDetector.Escaping.HTMLENCODE)) { toReturn.add(el); } @@ -363,5 +365,4 @@ public class VfUnescapeElRule extends AbstractVfRule { return toReturn; } - } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/internal/ElEscapeDetector.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/internal/ElEscapeDetector.java index b92a88f5f9..cea7bbd6c5 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/internal/ElEscapeDetector.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/internal/ElEscapeDetector.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ @@ -18,7 +18,6 @@ import net.sourceforge.pmd.lang.vf.ast.ASTElExpression; import net.sourceforge.pmd.lang.vf.ast.ASTExpression; import net.sourceforge.pmd.lang.vf.ast.ASTIdentifier; import net.sourceforge.pmd.lang.vf.ast.ASTNegationExpression; -import net.sourceforge.pmd.lang.vf.ast.AbstractVFNode; import net.sourceforge.pmd.lang.vf.ast.VfNode; import net.sourceforge.pmd.lang.vf.ast.VfTypedNode; @@ -30,14 +29,18 @@ import net.sourceforge.pmd.lang.vf.ast.VfTypedNode; public final class ElEscapeDetector { private static final Set SAFE_EXPRESSIONS = new HashSet<>(Arrays.asList("id", "size", "caseNumber")); - private static final Set NON_EMPTY_ARG_SAFE_RESOURCE = new HashSet<>(Arrays.asList("urlfor", "casesafeid", "begins", "contains", - "len", "getrecordids", "linkto", "sqrt", "round", "mod", "log", "ln", "exp", "abs", "floor", "ceiling", - "nullvalue", "isnumber", "isnull", "isnew", "isblank", "isclone", "year", "month", "day", "datetimevalue", - "datevalue", "date", "now", "today")); - private static final Set EMPTY_ARG_SAFE_RESOURCE = new HashSet<>(Arrays.asList("$action", "$page", "$site", + private static final Set NON_EMPTY_ARG_SAFE_RESOURCE = new HashSet<>(Arrays.asList("urlfor", "casesafeid", + "begins", "contains", "len", "getrecordids", "linkto", "sqrt", "round", "mod", "log", "ln", "exp", "abs", + "floor", "ceiling", "nullvalue", "isnumber", "isnull", "isnew", "isblank", "isclone", "year", "month", + "day", "datetimevalue", "datevalue", "date", "now", "today")); + private static final Set EMPTY_ARG_SAFE_RESOURCE = new HashSet<>(Arrays.asList("$action", "$page", "$site", "$resource", "$label", "$objecttype", "$component", "$remoteaction", "$messagechannel")); - public boolean innerContainsSafeFields(final VfNode expression) { + private ElEscapeDetector() { + // utility class + } + + private static boolean innerContainsSafeFields(final VfNode expression) { for (VfNode child : expression.children()) { if (child instanceof ASTIdentifier && SAFE_EXPRESSIONS.contains(child.getImage().toLowerCase(Locale.ROOT))) { @@ -61,14 +64,13 @@ public final class ElEscapeDetector { return false; } - public boolean containsSafeFields(final AbstractVFNode expression) { + public static boolean containsSafeFields(final VfNode expression) { final ASTExpression ex = expression.getFirstChildOfType(ASTExpression.class); return ex != null && innerContainsSafeFields(ex); - } - public boolean startsWithSafeResource(final ASTElExpression el) { + public static boolean startsWithSafeResource(final ASTElExpression el) { final ASTExpression expression = el.getFirstChildOfType(ASTExpression.class); if (expression != null) { final ASTNegationExpression negation = expression.getFirstChildOfType(ASTNegationExpression.class); @@ -94,12 +96,11 @@ public final class ElEscapeDetector { return false; } - public boolean doesElContainAnyUnescapedIdentifiers(final ASTElExpression elExpression, Escaping escape) { + public static boolean doesElContainAnyUnescapedIdentifiers(final ASTElExpression elExpression, Escaping escape) { return doesElContainAnyUnescapedIdentifiers(elExpression, EnumSet.of(escape)); - } - public boolean doesElContainAnyUnescapedIdentifiers(final ASTElExpression elExpression, + public static boolean doesElContainAnyUnescapedIdentifiers(final ASTElExpression elExpression, EnumSet escapes) { if (elExpression == null) { return false; @@ -154,7 +155,7 @@ public final class ElEscapeDetector { * Return true if the type of all data nodes can be determined and none of them require escaping * @param expression */ - public boolean expressionContainsSafeDataNodes(ASTExpression expression) { + private static boolean expressionContainsSafeDataNodes(ASTExpression expression) { try { for (VfTypedNode node : expression.getDataNodes().keySet()) { DataType dataType = node.getDataType(); @@ -187,5 +188,4 @@ public final class ElEscapeDetector { return text; } } - } diff --git a/pmd-visualforce/src/main/resources/category/vf/security.xml b/pmd-visualforce/src/main/resources/category/vf/security.xml index 29b414402d..2f4408e929 100644 --- a/pmd-visualforce/src/main/resources/category/vf/security.xml +++ b/pmd-visualforce/src/main/resources/category/vf/security.xml @@ -28,22 +28,34 @@ Avoid calling VF action upon page load as the action becomes vulnerable to CSRF. - Use relevant encoding with EL in html tags +Checks for the correct encoding in `<style/>` tags in Visualforce pages. + +The rule is based on Salesforce Security's recommendation to prevent XSS in Visualforce as mentioned +on [Secure Coding Cross Site Scripting](https://developer.salesforce.com/docs/atlas.en-us.secure_coding_guide.meta/secure_coding_guide/secure_coding_cross_site_scripting.htm). + +In order to avoid cross site scripting, the relevant encoding must be used in HTML tags. The rule expects +`URLENCODING` or `JSINHTMLENCODING` for URL-based style values and any kind of encoding +(e.g. `HTMLENCODING`) for non-url style values. + +See also {% rule "VfUnescapeEl" %} to check escaping in other places on Visualforce pages. 3 - - + ]]> From 2e3d94adb823b3a885544e198305dd55898dc43a Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 21 Jan 2021 15:05:37 +0100 Subject: [PATCH 091/154] [doc] Update release notes, refs #3005 --- docs/pages/release_notes.md | 7 +++++++ .../src/main/resources/rulesets/releases/6310.xml | 13 +++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 pmd-core/src/main/resources/rulesets/releases/6310.xml diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index b8f8783555..bfb8ded095 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -14,11 +14,18 @@ This is a {{ site.pmd.release_type }} release. ### New and noteworthy +#### New Rules + +* The new Visualforce rule {% rule "vf/security/VfHtmlStyleTagXss" %} checks for potential XSS problems + when using `