From 91936b000b8e94c2a7505a438ffe28212179d9fc Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 26 Oct 2020 19:06:21 +0100 Subject: [PATCH 01/32] [core] Fix XMLRenderer with UTF-16 When using UTF-16 as encoding, the XMLRenderer produced invalid XML: When inserting the linebreak encoded with the given encoding "UTF-16", a BOM was created. This inserted additional characters U+FEFF in the middle of the file, which is not allowed. A partial workaround for this issue would be, to use "UTF-16BE" as encoding instead. This doesn't create a BOM. However, the resulting XML file is then completely without a BOM. --- .../pmd/renderers/XMLRenderer.java | 25 ++++++++++++++++++- .../pmd/renderers/XMLRendererTest.java | 6 +++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java index 840b786ca6..204fc4f31a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java @@ -10,6 +10,8 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.text.SimpleDateFormat; import java.util.Date; @@ -66,7 +68,8 @@ public class XMLRenderer extends AbstractIncrementingRenderer { @Override public void start() throws IOException { String encoding = getProperty(ENCODING); - lineSeparator = PMD.EOL.getBytes(encoding); + String unmarkedEncoding = toUnmarkedEncoding(encoding); + lineSeparator = PMD.EOL.getBytes(unmarkedEncoding); try { xmlWriter.writeStartDocument(encoding, "1.0"); @@ -86,6 +89,26 @@ public class XMLRenderer extends AbstractIncrementingRenderer { } } + /** + * Return a encoding, which doesn't write a BOM (byte order mark). + * Only UTF-16 encoders might write a BOM, see {@link Charset}. + * + *

This is needed, so that we don't accidentally add BOMs whenever + * we insert a newline. + * + * @return + */ + private static String toUnmarkedEncoding(String encoding) { + if (StandardCharsets.UTF_16.name().equalsIgnoreCase(encoding)) { + return StandardCharsets.UTF_16BE.name(); + } + // edge case: UTF-16LE with BOM + if ("UTF-16LE_BOM".equalsIgnoreCase(encoding)) { + return StandardCharsets.UTF_16LE.name(); + } + return encoding; + } + /** * Outputs a platform dependent line separator. * 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 e72e44561f..62e628c106 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 @@ -123,6 +123,12 @@ public class XMLRendererTest extends AbstractRendererTest { verifyXmlEscaping(renderer, "\ud801\udc1c", StandardCharsets.UTF_8); } + @Test + public void testXMLEscapingWithUTF16() throws Exception { + Renderer renderer = getRenderer(); + verifyXmlEscaping(renderer, "𐐜", StandardCharsets.UTF_16); + } + @Test public void testXMLEscapingWithoutUTF8() throws Exception { Renderer renderer = getRenderer(); From 299e2e817d443254b0f7d5fd9eee1efdd385b2df Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 26 Oct 2020 19:32:34 +0100 Subject: [PATCH 02/32] [doc][skip ci] Update release notes --- 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..4e56f1b297 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 + * [#2874](https://github.com/pmd/pmd/pull/2874): \[core] Fix XMLRenderer with UTF-16 + ### API Changes ### External Contributions From 8ac13b769ed0b2601fa2f580e017b077e1ffacb9 Mon Sep 17 00:00:00 2001 From: Gunther Schrijvers Date: Thu, 12 Nov 2020 11:41:10 +0100 Subject: [PATCH 03/32] [core] Modified test cases for TextRenderer to include the RuleName in the generated output --- .../src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java | 2 +- .../net/sourceforge/pmd/renderers/TextRendererTest.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java index 396bda7876..6e865fd888 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java @@ -78,7 +78,7 @@ public class PMDTaskTest { String actual = IOUtils.toString(in, StandardCharsets.UTF_8); // remove any trailing newline actual = actual.replaceAll("\n|\r", ""); - Assert.assertEquals("sample.dummy:0:\tTest Rule 2", actual); + Assert.assertEquals("sample.dummy:0:\tTest Rule 2:\tSampleXPathRule", actual); } } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java index e5118ab038..57e560818e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java @@ -17,7 +17,7 @@ public class TextRendererTest extends AbstractRendererTest { @Override public String getExpected() { - return getSourceCodeFilename() + ":1:\tblah" + PMD.EOL; + return getSourceCodeFilename() + ":1:\tblah:\tFoo" + PMD.EOL; } @Override @@ -27,8 +27,8 @@ public class TextRendererTest extends AbstractRendererTest { @Override public String getExpectedMultiple() { - return getSourceCodeFilename() + ":1:\tblah" + PMD.EOL - + getSourceCodeFilename() + ":1:\tblah" + PMD.EOL; + return getSourceCodeFilename() + ":1:\tblah:\tFoo" + PMD.EOL + + getSourceCodeFilename() + ":1:\tblah:\tFoo" + PMD.EOL; } @Override From 3a2c1b8334d9df24e0a7e357f79f2067107c95ba Mon Sep 17 00:00:00 2001 From: Gunther Schrijvers Date: Thu, 12 Nov 2020 11:44:18 +0100 Subject: [PATCH 04/32] [core] Modified output of TextRenderer to include the RuleName in the generated output --- .../main/java/net/sourceforge/pmd/renderers/TextRenderer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java index 6830c97b7c..448364bf6b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java @@ -36,7 +36,8 @@ public class TextRenderer extends AbstractIncrementingRenderer { RuleViolation rv = violations.next(); buf.append(determineFileName(rv.getFilename())); buf.append(':').append(Integer.toString(rv.getBeginLine())); - buf.append(":\t").append(rv.getDescription()).append(PMD.EOL); + buf.append(":\t").append(rv.getDescription()); + buf.append(":\t").append(rv.getRule().getName()).append(PMD.EOL); writer.write(buf.toString()); } } From 72291e9a59ea9216a308051832b5a2c111943a86 Mon Sep 17 00:00:00 2001 From: Gunther Schrijvers Date: Thu, 12 Nov 2020 11:46:05 +0100 Subject: [PATCH 05/32] [core] Removed unnecessary explicit casting of Integer to String --- .../main/java/net/sourceforge/pmd/renderers/TextRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java index 448364bf6b..4b44c700ed 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java @@ -35,7 +35,7 @@ public class TextRenderer extends AbstractIncrementingRenderer { buf.setLength(0); RuleViolation rv = violations.next(); buf.append(determineFileName(rv.getFilename())); - buf.append(':').append(Integer.toString(rv.getBeginLine())); + buf.append(':').append(rv.getBeginLine()); buf.append(":\t").append(rv.getDescription()); buf.append(":\t").append(rv.getRule().getName()).append(PMD.EOL); writer.write(buf.toString()); From 7ef6084a79c8a43bc0cccfb4077f90e11a234f4f Mon Sep 17 00:00:00 2001 From: Gunther Schrijvers Date: Thu, 12 Nov 2020 11:57:01 +0100 Subject: [PATCH 06/32] [core] Refactored generation of Text output to use constants defining the 3 different types of text seperators: small '-'; medium ':\t' and large '\t-\t' to increase readability and to standardise the text output. --- .../sourceforge/pmd/renderers/TextRenderer.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java index 4b44c700ed..dddc4c2440 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java @@ -16,6 +16,10 @@ import net.sourceforge.pmd.RuleViolation; */ public class TextRenderer extends AbstractIncrementingRenderer { + private static final Character SMALL_SEPERATOR = ':'; + private static final String MEDIUM_SEPERATOR = ":\t"; + private static final String LARGE_SEPERATOR = "\t-\t"; + public static final String NAME = "text"; public TextRenderer() { @@ -35,9 +39,9 @@ public class TextRenderer extends AbstractIncrementingRenderer { buf.setLength(0); RuleViolation rv = violations.next(); buf.append(determineFileName(rv.getFilename())); - buf.append(':').append(rv.getBeginLine()); - buf.append(":\t").append(rv.getDescription()); - buf.append(":\t").append(rv.getRule().getName()).append(PMD.EOL); + buf.append(SMALL_SEPERATOR).append(rv.getBeginLine()); + buf.append(MEDIUM_SEPERATOR).append(rv.getDescription()); + buf.append(MEDIUM_SEPERATOR).append(rv.getRule().getName()).append(PMD.EOL); writer.write(buf.toString()); } } @@ -49,7 +53,7 @@ public class TextRenderer extends AbstractIncrementingRenderer { for (Report.ProcessingError error : errors) { buf.setLength(0); buf.append(determineFileName(error.getFile())); - buf.append("\t-\t").append(error.getMsg()).append(PMD.EOL); + buf.append(LARGE_SEPERATOR).append(error.getMsg()).append(PMD.EOL); writer.write(buf.toString()); } @@ -65,7 +69,7 @@ public class TextRenderer extends AbstractIncrementingRenderer { for (Report.ConfigurationError error : configErrors) { buf.setLength(0); buf.append(error.rule().getName()); - buf.append("\t-\t").append(error.issue()).append(PMD.EOL); + buf.append(LARGE_SEPERATOR).append(error.issue()).append(PMD.EOL); writer.write(buf.toString()); } } From c7022c4881a206e8b914a44eeb4287972a6b5955 Mon Sep 17 00:00:00 2001 From: Gunther Schrijvers Date: Thu, 12 Nov 2020 13:05:03 +0100 Subject: [PATCH 07/32] [core] Corrected constant naming from SEPERATOR to SEPARATOR. --- .../sourceforge/pmd/renderers/TextRenderer.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java index dddc4c2440..043a3c6707 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java @@ -16,9 +16,9 @@ import net.sourceforge.pmd.RuleViolation; */ public class TextRenderer extends AbstractIncrementingRenderer { - private static final Character SMALL_SEPERATOR = ':'; - private static final String MEDIUM_SEPERATOR = ":\t"; - private static final String LARGE_SEPERATOR = "\t-\t"; + private static final Character SMALL_SEPARATOR = ':'; + private static final String MEDIUM_SEPARATOR = ":\t"; + private static final String LARGE_SEPARATOR = "\t-\t"; public static final String NAME = "text"; @@ -39,9 +39,9 @@ public class TextRenderer extends AbstractIncrementingRenderer { buf.setLength(0); RuleViolation rv = violations.next(); buf.append(determineFileName(rv.getFilename())); - buf.append(SMALL_SEPERATOR).append(rv.getBeginLine()); - buf.append(MEDIUM_SEPERATOR).append(rv.getDescription()); - buf.append(MEDIUM_SEPERATOR).append(rv.getRule().getName()).append(PMD.EOL); + buf.append(SMALL_SEPARATOR).append(rv.getBeginLine()); + buf.append(MEDIUM_SEPARATOR).append(rv.getDescription()); + buf.append(MEDIUM_SEPARATOR).append(rv.getRule().getName()).append(PMD.EOL); writer.write(buf.toString()); } } @@ -53,7 +53,7 @@ public class TextRenderer extends AbstractIncrementingRenderer { for (Report.ProcessingError error : errors) { buf.setLength(0); buf.append(determineFileName(error.getFile())); - buf.append(LARGE_SEPERATOR).append(error.getMsg()).append(PMD.EOL); + buf.append(LARGE_SEPARATOR).append(error.getMsg()).append(PMD.EOL); writer.write(buf.toString()); } @@ -69,7 +69,7 @@ public class TextRenderer extends AbstractIncrementingRenderer { for (Report.ConfigurationError error : configErrors) { buf.setLength(0); buf.append(error.rule().getName()); - buf.append(LARGE_SEPERATOR).append(error.issue()).append(PMD.EOL); + buf.append(LARGE_SEPARATOR).append(error.issue()).append(PMD.EOL); writer.write(buf.toString()); } } From fbe2e6bb2e33072c197df5a154e160a8743c594c Mon Sep 17 00:00:00 2001 From: Gunther Schrijvers Date: Thu, 12 Nov 2020 13:13:08 +0100 Subject: [PATCH 08/32] [core] Changed output for a violating rule to put the RuleName in front of the description after receiving feedback on the PR. --- .../java/net/sourceforge/pmd/renderers/TextRenderer.java | 4 ++-- .../src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java | 2 +- .../net/sourceforge/pmd/renderers/TextRendererTest.java | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java index 043a3c6707..f66fd971bb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java @@ -40,8 +40,8 @@ public class TextRenderer extends AbstractIncrementingRenderer { RuleViolation rv = violations.next(); buf.append(determineFileName(rv.getFilename())); buf.append(SMALL_SEPARATOR).append(rv.getBeginLine()); - buf.append(MEDIUM_SEPARATOR).append(rv.getDescription()); - buf.append(MEDIUM_SEPARATOR).append(rv.getRule().getName()).append(PMD.EOL); + buf.append(MEDIUM_SEPARATOR).append(rv.getRule().getName()); + buf.append(MEDIUM_SEPARATOR).append(rv.getDescription()).append(PMD.EOL); writer.write(buf.toString()); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java index 6e865fd888..0bdf85b5ad 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java @@ -78,7 +78,7 @@ public class PMDTaskTest { String actual = IOUtils.toString(in, StandardCharsets.UTF_8); // remove any trailing newline actual = actual.replaceAll("\n|\r", ""); - Assert.assertEquals("sample.dummy:0:\tTest Rule 2:\tSampleXPathRule", actual); + Assert.assertEquals("sample.dummy:0:\tSampleXPathRule:\tTest Rule 2", actual); } } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java index 57e560818e..69983b5f85 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java @@ -17,7 +17,7 @@ public class TextRendererTest extends AbstractRendererTest { @Override public String getExpected() { - return getSourceCodeFilename() + ":1:\tblah:\tFoo" + PMD.EOL; + return getSourceCodeFilename() + ":1:\tFoo:\tblah" + PMD.EOL; } @Override @@ -27,8 +27,8 @@ public class TextRendererTest extends AbstractRendererTest { @Override public String getExpectedMultiple() { - return getSourceCodeFilename() + ":1:\tblah:\tFoo" + PMD.EOL - + getSourceCodeFilename() + ":1:\tblah:\tFoo" + PMD.EOL; + return getSourceCodeFilename() + ":1:\tFoo:\tblah" + PMD.EOL + + getSourceCodeFilename() + ":1:\tFoo:\tblah" + PMD.EOL; } @Override From e4ecc3bfb78e13cc7018c95bec656f84499ee8d8 Mon Sep 17 00:00:00 2001 From: GuntherSchrijvers <56870283+GuntherSchrijvers@users.noreply.github.com> Date: Thu, 12 Nov 2020 14:24:05 +0100 Subject: [PATCH 09/32] Update pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ClĂ©ment Fournier --- .../main/java/net/sourceforge/pmd/renderers/TextRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java index f66fd971bb..afce5e0740 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java @@ -16,7 +16,7 @@ import net.sourceforge.pmd.RuleViolation; */ public class TextRenderer extends AbstractIncrementingRenderer { - private static final Character SMALL_SEPARATOR = ':'; + private static final char SMALL_SEPARATOR = ':'; private static final String MEDIUM_SEPARATOR = ":\t"; private static final String LARGE_SEPARATOR = "\t-\t"; From 4fe3874824f3736547d808ad37db2e1bbc47d5cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 14:57:25 +0100 Subject: [PATCH 10/32] Simplify constructor of JavaTypeDefinitionSimple --- .../JavaTypeDefinitionSimple.java | 89 +++++++++++-------- 1 file changed, 50 insertions(+), 39 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java index 1f0fbbd7e5..4d5c2e2f61 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java @@ -12,6 +12,7 @@ import static net.sourceforge.pmd.lang.java.typeresolution.typedefinition.TypeDe import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -26,21 +27,44 @@ import java.util.logging.Logger; /* default */ class JavaTypeDefinitionSimple extends JavaTypeDefinition { + private final Class clazz; - private final JavaTypeDefinition[] genericArgs; + private JavaTypeDefinition[] genericArgs; // cached because calling clazz.getTypeParameters().length create a new array every time - private final int typeParameterCount; - private final boolean isGeneric; - private final boolean isRawType; + private int typeParameterCount; + private final int typeArgumentCount; private final JavaTypeDefinition enclosingClass; private static final Logger LOG = Logger.getLogger(JavaTypeDefinitionSimple.class.getName()); - private static final JavaTypeDefinition[] NO_GENERICS = {}; protected JavaTypeDefinitionSimple(Class clazz, JavaTypeDefinition... boundGenerics) { super(EXACT); this.clazz = clazz; + typeArgumentCount = boundGenerics.length; + if (boundGenerics.length > 0) { + genericArgs = Arrays.copyOf(boundGenerics, boundGenerics.length); + } // otherwise stays null + + enclosingClass = forClass(loadEnclosing(clazz)); + } + + private Class loadEnclosing(Class clazz) { + if (Modifier.isStatic(clazz.getModifiers())) { + return null; + } + Class enclosing = null; + try { + enclosing = clazz.getEnclosingClass(); + } catch (LinkageError e) { + if (LOG.isLoggable(Level.WARNING)) { + LOG.log(Level.WARNING, "Could not load enclosing class of " + clazz.getName() + ", due to: " + e); + } + } + return enclosing; + } + + private TypeVariable[] getTypeParameters(Class clazz) { final TypeVariable[] typeParameters; // the anonymous class can't have generics, but we may be binding generics from super classes if (clazz.isAnonymousClass()) { @@ -53,27 +77,7 @@ import java.util.logging.Logger; } else { typeParameters = clazz.getTypeParameters(); } - - typeParameterCount = typeParameters.length; - isGeneric = typeParameters.length != 0; - isRawType = isGeneric && boundGenerics.length == 0; - - if (isGeneric) { - // Generics will be lazily loaded if not already known - this.genericArgs = Arrays.copyOf(boundGenerics, typeParameterCount); - } else { - this.genericArgs = NO_GENERICS; - } - - Class enclosing = null; - try { - enclosing = clazz.getEnclosingClass(); - } catch (LinkageError e) { - if (LOG.isLoggable(Level.WARNING)) { - LOG.log(Level.WARNING, "Could not load enclosing class of " + clazz.getName() + ", due to: " + e); - } - } - enclosingClass = forClass(enclosing); + return typeParameters; } @Override @@ -86,9 +90,22 @@ import java.util.logging.Logger; return enclosingClass; } + @Override + public boolean isRawType() { + return isGeneric() && typeArgumentCount == 0; + } + @Override public boolean isGeneric() { - return isGeneric; + return getTypeParameterCount() != 0; + } + + @Override + public int getTypeParameterCount() { + if (typeParameterCount == -1) { + typeParameterCount = getTypeParameters(clazz).length; + } + return typeParameterCount; } private JavaTypeDefinition getGenericType(final String parameterName, Method method, @@ -131,6 +148,10 @@ import java.util.logging.Logger; @Override public JavaTypeDefinition getGenericType(final int index) { + if (genericArgs == null) { + genericArgs = new JavaTypeDefinition[getTypeParameterCount()]; + } + // Check if it has been lazily initialized first final JavaTypeDefinition cachedDefinition = genericArgs[index]; if (cachedDefinition != null) { @@ -265,11 +286,6 @@ import java.util.logging.Logger; return clazz == def.getType(); } - @Override - public int getTypeParameterCount() { - return typeParameterCount; - } - @Override public String toString() { @@ -290,7 +306,7 @@ import java.util.logging.Logger; sb.replace(sb.length() - 3, sb.length() - 1, ""); // remove last comma } - return sb.append("], isGeneric=").append(isGeneric) + return sb.append("], isGeneric=").append(isGeneric()) .append("]\n").toString(); } @@ -298,7 +314,7 @@ import java.util.logging.Logger; public String shallowString() { return new StringBuilder("JavaTypeDefinition [clazz=").append(clazz) .append(", definitionType=").append(getDefinitionType()) - .append(", isGeneric=").append(isGeneric) + .append(", isGeneric=").append(isGeneric()) .append("]\n").toString(); } @@ -411,11 +427,6 @@ import java.util.logging.Logger; return 1; } - @Override - public boolean isRawType() { - return isRawType; - } - @Override public boolean isIntersectionType() { return false; From 00d76b03ab1bfd7b6eb517be1f565464d26dcf72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 15:01:40 +0100 Subject: [PATCH 11/32] Restrict caching to classes loaded by the bootstrap loader --- .../typedefinition/JavaTypeDefinition.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java index 101fa8a9bd..b490169216 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java @@ -16,7 +16,10 @@ import org.apache.commons.lang3.ArrayUtils; public abstract class JavaTypeDefinition implements TypeDefinition { - // contains non-generic and raw EXACT types + // Contains non-generic and raw EXACT types + // Only contains classes loaded by the bootstrap classloader so as not to + // keep references to the user classloader alive + // This is enough to cache eg Object, String, and other common types private static final Map, JavaTypeDefinition> CLASS_EXACT_TYPE_DEF_CACHE = new ConcurrentHashMap<>(); private final TypeDefinitionType definitionType; @@ -76,7 +79,10 @@ public abstract class JavaTypeDefinition implements TypeDefinition { return null; // Can happen if a parent class references a class not in classpath } - CLASS_EXACT_TYPE_DEF_CACHE.put(clazz, newDef); + if (clazz.getClassLoader() == null) { + // loaded by the bootstrap classloader + CLASS_EXACT_TYPE_DEF_CACHE.put(clazz, newDef); + } return newDef; } From 307eacd20aecd7cabda70e3df27f78b5bd9d91a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 15:34:06 +0100 Subject: [PATCH 12/32] Fix errors --- .../typedefinition/JavaTypeDefinitionSimple.java | 15 ++++++++------- .../pmd/typeresolution/ClassTypeResolverTest.java | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java index 4d5c2e2f61..3c24a2cad9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java @@ -31,7 +31,7 @@ import java.util.logging.Logger; private final Class clazz; private JavaTypeDefinition[] genericArgs; // cached because calling clazz.getTypeParameters().length create a new array every time - private int typeParameterCount; + private int typeParameterCount = -1; private final int typeArgumentCount; private final JavaTypeDefinition enclosingClass; @@ -294,15 +294,12 @@ import java.util.logging.Logger; .append(", genericArgs=["); // Forcefully resolve all generic types - for (int i = 0; i < genericArgs.length; i++) { - getGenericType(i); - } - - for (final JavaTypeDefinition jtd : genericArgs) { + for (int i = 0; i < getTypeParameterCount(); i++) { + JavaTypeDefinition jtd = getGenericType(i); sb.append(jtd.shallowString()).append(", "); } - if (genericArgs.length != 0) { + if (getTypeParameterCount() != 0) { sb.replace(sb.length() - 3, sb.length() - 1, ""); // remove last comma } @@ -341,6 +338,10 @@ import java.util.logging.Logger; return false; } + if (isRawType() || otherTypeDef.isRawType()) { + return this.isRawType() == otherTypeDef.isRawType(); + } + for (int i = 0; i < getTypeParameterCount(); ++i) { // Note: we assume that cycles can only exist because of raw types if (!getGenericType(i).equals(otherTypeDef.getGenericType(i))) { 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 f2e9661ed5..f5a7add61a 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 @@ -1703,8 +1703,8 @@ public class ClassTypeResolverTest { a = forClass(JavaTypeDefinitionEquals.class); b = forClass(JavaTypeDefinitionEquals.class, forClass(List.class, a)); - assertEquals(a, b); - assertEquals(b, a); + assertNotEquals(a, b); + assertNotEquals(b, a); } @Test From 4ee7b7ae027fee6755fd4c6375fbfb37cd22149b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 16:44:27 +0100 Subject: [PATCH 13/32] Load fewer class names --- .../typeresolution/ClassTypeResolver.java | 52 ++++++++++++++++--- 1 file changed, 44 insertions(+), 8 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 296504ff29..e5f237775a 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 @@ -14,6 +14,7 @@ import static net.sourceforge.pmd.lang.java.typeresolution.typedefinition.TypeDe import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -21,6 +22,8 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import org.apache.commons.lang3.StringUtils; + import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.QualifiableNode; @@ -289,20 +292,24 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla /** * Set's the node's type to the found Class in the node's name (if there is a class to be found). * - * @param node - * * @return The index in the array produced by splitting the node's name by '.', which is not part of the - * class name found. Example: com.package.SomeClass.staticField.otherField, return would be 3 + * class name found. Example: com.package.SomeClass.staticField.otherField, return would be 3 */ - private int searchNodeNameForClass(TypeNode node) { + private int searchNodeNameForClass(ASTName node, String[] segments) { // this is the index from which field/method names start in the dotSplitImage array - int startIndex = node.getImage().split("\\.").length; + int startIndex = lastIndexThatMayBeAClassNameExclusive(node, segments); + if (startIndex == 0) { + return 0; + } + + String reducedImage = StringUtils.join(Arrays.asList(segments).subList(0, startIndex), '.'); // tries to find a class in the node's image by omitting the parts after each '.', example: // First try: com.package.SomeClass.staticField.otherField // Second try: com.package.SomeClass.staticField // Third try: com.package.SomeClass <- found a class! - for (String reducedImage = node.getImage();;) { + + while (StringUtils.isNotEmpty(reducedImage) && startIndex > 0) { populateType(node, reducedImage); if (node.getType() != null) { break; // we found a class! @@ -323,6 +330,35 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla return startIndex; } + private int lastIndexThatMayBeAClassNameExclusive(ASTName name, String[] segments) { + assert segments.length > 0; + + + + if (name.getParent() instanceof ASTPrimarySuffix) { + // not a class name as class name in primary + // expressions are only prefixes of the expr + return 0; + } + + JavaNode opa = name.getParent().getParent(); + if (opa.getNumChildren() > 1 + name.getParent().getIndexInParent()) { + // there is a following sibling to the primary prefix + JavaNode nextSibling = opa.getChild(name.getParent().getIndexInParent() + 1); + if (isArguments(nextSibling)) { + return segments.length - 1; + } + } + return segments.length; + } + + private boolean isArguments(JavaNode node) { + if (node instanceof ASTPrimarySuffix) { + return ((ASTPrimarySuffix) node).isArguments(); + } + return false; + } + private ASTArgumentList getArgumentList(ASTArguments args) { if (args != null) { return args.getFirstChildOfType(ASTArgumentList.class); @@ -344,7 +380,7 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla Class accessingClass = getEnclosingTypeDeclarationClass(node); String[] dotSplitImage = node.getImage().split("\\."); - int startIndex = searchNodeNameForClass(node); + int startIndex = searchNodeNameForClass(node, dotSplitImage); ASTArguments astArguments = getSuffixMethodArgs(node); ASTArgumentList astArgumentList = getArgumentList(astArguments); @@ -964,7 +1000,7 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla // restore type of the name and search again ASTName name = previousChild.getFirstChildOfType(ASTName.class); name.setTypeDefinition(null); - searchNodeNameForClass(name); + searchNodeNameForClass(name, name.getImage().split("\\.")); if (name.getTypeDefinition() != null) { // rollup from Name -> PrimaryPrefix previousChild.setTypeDefinition(name.getTypeDefinition()); From 30124fef118e215d04cd7f67f186155bd6213088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 16:54:11 +0100 Subject: [PATCH 14/32] Don't cache enclosing class (pretty rare to hit it) --- .../typedefinition/JavaTypeDefinitionSimple.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java index 3c24a2cad9..66d3d13974 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java @@ -12,7 +12,6 @@ import static net.sourceforge.pmd.lang.java.typeresolution.typedefinition.TypeDe import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -33,7 +32,6 @@ import java.util.logging.Logger; // cached because calling clazz.getTypeParameters().length create a new array every time private int typeParameterCount = -1; private final int typeArgumentCount; - private final JavaTypeDefinition enclosingClass; private static final Logger LOG = Logger.getLogger(JavaTypeDefinitionSimple.class.getName()); @@ -45,23 +43,17 @@ import java.util.logging.Logger; if (boundGenerics.length > 0) { genericArgs = Arrays.copyOf(boundGenerics, boundGenerics.length); } // otherwise stays null - - enclosingClass = forClass(loadEnclosing(clazz)); } private Class loadEnclosing(Class clazz) { - if (Modifier.isStatic(clazz.getModifiers())) { - return null; - } - Class enclosing = null; try { - enclosing = clazz.getEnclosingClass(); + return clazz.getEnclosingClass(); } catch (LinkageError e) { if (LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Could not load enclosing class of " + clazz.getName() + ", due to: " + e); } + return null; } - return enclosing; } private TypeVariable[] getTypeParameters(Class clazz) { @@ -87,7 +79,7 @@ import java.util.logging.Logger; @Override public JavaTypeDefinition getEnclosingClass() { - return enclosingClass; + return JavaTypeDefinition.forClass(loadEnclosing(clazz)); } @Override From 3ccdb5597a751d055b67f2501f05413e359b8b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 17:05:41 +0100 Subject: [PATCH 15/32] Make overload without varargs --- .../typedefinition/JavaTypeDefinition.java | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java index b490169216..b78853d979 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java @@ -21,6 +21,8 @@ public abstract class JavaTypeDefinition implements TypeDefinition { // keep references to the user classloader alive // This is enough to cache eg Object, String, and other common types private static final Map, JavaTypeDefinition> CLASS_EXACT_TYPE_DEF_CACHE = new ConcurrentHashMap<>(); + private static final JavaTypeDefinition[] NO_GENERICS = {}; + private final TypeDefinitionType definitionType; @@ -55,6 +57,34 @@ public abstract class JavaTypeDefinition implements TypeDefinition { } } + public static JavaTypeDefinition forClass(final Class clazz) { + if (clazz == null) { + return null; + } + + if (clazz.getClassLoader() == null) { + // loaded by the bootstrap classloader + JavaTypeDefinition typeDef = CLASS_EXACT_TYPE_DEF_CACHE.get(clazz); + + if (typeDef != null) { + return typeDef; + } + typeDef = makeWithNoCache(clazz); + CLASS_EXACT_TYPE_DEF_CACHE.put(clazz, typeDef); + return typeDef; + } + + return makeWithNoCache(clazz); + } + + private static JavaTypeDefinitionSimple makeWithNoCache(Class clazz) { + try { + return new JavaTypeDefinitionSimple(clazz, JavaTypeDefinition.NO_GENERICS); + } catch (final NoClassDefFoundError e) { + return null; // Can happen if a parent class references a class not in classpath + } + } + public static JavaTypeDefinition forClass(final Class clazz, JavaTypeDefinition... boundGenerics) { if (clazz == null) { return null; @@ -66,25 +96,7 @@ public abstract class JavaTypeDefinition implements TypeDefinition { return new JavaTypeDefinitionSimple(clazz, boundGenerics); } - final JavaTypeDefinition typeDef = CLASS_EXACT_TYPE_DEF_CACHE.get(clazz); - - if (typeDef != null) { - return typeDef; - } - - final JavaTypeDefinition newDef; - try { - newDef = new JavaTypeDefinitionSimple(clazz); - } catch (final NoClassDefFoundError e) { - return null; // Can happen if a parent class references a class not in classpath - } - - if (clazz.getClassLoader() == null) { - // loaded by the bootstrap classloader - CLASS_EXACT_TYPE_DEF_CACHE.put(clazz, newDef); - } - - return newDef; + return forClass(clazz); } @Override From 51c0df8770e4aa22f9d50212be675f022c16b011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 17:35:59 +0100 Subject: [PATCH 16/32] Remove cache entirely --- .../typedefinition/JavaTypeDefinition.java | 42 +++---------------- .../JavaTypeDefinitionLower.java | 3 +- .../JavaTypeDefinitionSimple.java | 1 + 3 files changed, 8 insertions(+), 38 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java index b78853d979..09ce29655d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java @@ -8,19 +8,13 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang3.ArrayUtils; public abstract class JavaTypeDefinition implements TypeDefinition { - // Contains non-generic and raw EXACT types - // Only contains classes loaded by the bootstrap classloader so as not to - // keep references to the user classloader alive - // This is enough to cache eg Object, String, and other common types - private static final Map, JavaTypeDefinition> CLASS_EXACT_TYPE_DEF_CACHE = new ConcurrentHashMap<>(); + private static final JavaTypeDefinition[] NO_GENERICS = {}; @@ -58,31 +52,9 @@ public abstract class JavaTypeDefinition implements TypeDefinition { } public static JavaTypeDefinition forClass(final Class clazz) { - if (clazz == null) { - return null; - } + return clazz == Object.class ? JavaTypeDefinitionSimple.OBJECT_DEFINITION + : forClass(clazz, NO_GENERICS); // very common - if (clazz.getClassLoader() == null) { - // loaded by the bootstrap classloader - JavaTypeDefinition typeDef = CLASS_EXACT_TYPE_DEF_CACHE.get(clazz); - - if (typeDef != null) { - return typeDef; - } - typeDef = makeWithNoCache(clazz); - CLASS_EXACT_TYPE_DEF_CACHE.put(clazz, typeDef); - return typeDef; - } - - return makeWithNoCache(clazz); - } - - private static JavaTypeDefinitionSimple makeWithNoCache(Class clazz) { - try { - return new JavaTypeDefinitionSimple(clazz, JavaTypeDefinition.NO_GENERICS); - } catch (final NoClassDefFoundError e) { - return null; // Can happen if a parent class references a class not in classpath - } } public static JavaTypeDefinition forClass(final Class clazz, JavaTypeDefinition... boundGenerics) { @@ -90,13 +62,11 @@ public abstract class JavaTypeDefinition implements TypeDefinition { return null; } - // deal with generic types - if (boundGenerics.length != 0) { - // With generics there is no cache + try { return new JavaTypeDefinitionSimple(clazz, boundGenerics); + } catch (final LinkageError e) { + return null; // Can happen if a parent class references a class not in classpath } - - return forClass(clazz); } @Override diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionLower.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionLower.java index 2012d4bb5a..16700e256c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionLower.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionLower.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.lang.java.typeresolution.typedefinition; import static net.sourceforge.pmd.lang.java.typeresolution.typedefinition.TypeDefinitionType.LOWER_WILDCARD; /* default */ class JavaTypeDefinitionLower extends JavaTypeDefinitionUpper { - private static final JavaTypeDefinition OBJECT_DEFINITION = forClass(Object.class); protected JavaTypeDefinitionLower(JavaTypeDefinition... typeList) { super(LOWER_WILDCARD, typeList); @@ -15,6 +14,6 @@ import static net.sourceforge.pmd.lang.java.typeresolution.typedefinition.TypeDe @Override protected JavaTypeDefinition firstJavaType() { - return OBJECT_DEFINITION; + return forClass(Object.class); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java index 66d3d13974..5ced2ec288 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java @@ -27,6 +27,7 @@ import java.util.logging.Logger; /* default */ class JavaTypeDefinitionSimple extends JavaTypeDefinition { + static final JavaTypeDefinitionSimple OBJECT_DEFINITION = new JavaTypeDefinitionSimple(Object.class); private final Class clazz; private JavaTypeDefinition[] genericArgs; // cached because calling clazz.getTypeParameters().length create a new array every time From b70462b79a5595c6921dc13a4c66517964ee52bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 18:16:20 +0100 Subject: [PATCH 17/32] Update release notes, refs #2920 --- 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 bda687c234..2f8ddf80cb 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -18,6 +18,8 @@ This is a {{ site.pmd.release_type }} release. * pmd-core * [#1939](https://github.com/pmd/pmd/issues/1939): \[core] XPath expressions return handling +* pmd-java + * [#2911](https://github.com/pmd/pmd/issues/2911): \[java] `ClassTypeResolver#searchNodeNameForClass` leaks memory ### API Changes From 84e6c0997ba4a120bada3c831ffdc2c75fea5b72 Mon Sep 17 00:00:00 2001 From: abhishek-kumar09 Date: Sun, 15 Nov 2020 16:52:45 +0530 Subject: [PATCH 18/32] Correct Annotation Array Initialiation indents from checkstyle #8083 --- .../pmd/renderers/RenderersTests.java | 24 +++++++++---------- .../java/cpd/testdata/discardedElements.java | 2 +- .../discardedElements_no_ignore_annots.txt | 4 ++-- .../java/cpd/testdata/specialComments.java | 4 ++-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/RenderersTests.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/RenderersTests.java index dcb68112b9..b97d81e0e2 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/RenderersTests.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/RenderersTests.java @@ -15,18 +15,18 @@ import org.junit.runners.Suite.SuiteClasses; */ @RunWith(Suite.class) @SuiteClasses({ - CodeClimateRendererTest.class, - CSVRendererTest.class, - EmacsRendererTest.class, - HTMLRendererTest.class, - IDEAJRendererTest.class, - PapariTextRendererTest.class, - SummaryHTMLRendererTest.class, - TextPadRendererTest.class, - VBHTMLRendererTest.class, - XMLRendererTest.class, - XSLTRendererTest.class, - YAHTMLRendererTest.class + CodeClimateRendererTest.class, + CSVRendererTest.class, + EmacsRendererTest.class, + HTMLRendererTest.class, + IDEAJRendererTest.class, + PapariTextRendererTest.class, + SummaryHTMLRendererTest.class, + TextPadRendererTest.class, + VBHTMLRendererTest.class, + XMLRendererTest.class, + XSLTRendererTest.class, + YAHTMLRendererTest.class }) public class RenderersTests { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements.java index 54858f8423..93869d1726 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements.java @@ -29,7 +29,7 @@ public class Foo { // class Bar @AnnotationWithParams({@Nested(1) , @Nested(2) , @Nested - }) + }) public void foo() { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements_no_ignore_annots.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements_no_ignore_annots.txt index 705523dcca..40d68ba293 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements_no_ignore_annots.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements_no_ignore_annots.txt @@ -49,8 +49,8 @@ L31 [@] 28 28 [Nested] 29 34 L32 - [}] 14 14 - [)] 15 15 + [}] 9 9 + [)] 10 10 L33 [public] 5 10 [void] 12 15 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/specialComments.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/specialComments.java index 7b985c8bde..93852a818c 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/specialComments.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/specialComments.java @@ -4,8 +4,8 @@ package foo.bar.baz; // another irrelevant comment @ MyAnnotation ("ugh") @NamedQueries({ - @NamedQuery( - )}) + @NamedQuery( + )}) public class Foo {// CPD-ON From cb5ce5d9d1ef29ab9ea2e8f8091f1cc4c82d620b Mon Sep 17 00:00:00 2001 From: Andy Robinson Date: Mon, 16 Nov 2020 12:08:47 +0000 Subject: [PATCH 19/32] [scala] adding support for CPD-ON and CPD-OFF special comments --- .../pmd/cpd/ScalaTokenAdapter.java | 59 ++++++++++++++++ .../sourceforge/pmd/cpd/ScalaTokenizer.java | 68 +++++++++++++++---- .../pmd/cpd/ScalaTokenizerTest.java | 5 ++ .../scala/cpd/testdata/special_comments.scala | 24 +++++++ .../scala/cpd/testdata/special_comments.txt | 52 ++++++++++++++ 5 files changed, 194 insertions(+), 14 deletions(-) create mode 100644 pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java create mode 100644 pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.scala create mode 100644 pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt 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 new file mode 100644 index 0000000000..8650c67892 --- /dev/null +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java @@ -0,0 +1,59 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cpd; + +import net.sourceforge.pmd.lang.ast.GenericToken; + +import scala.meta.tokens.Token; + +/** + * Adapts the scala.meta.tokens.Token so that it can be used with the generic BaseTokenFilter + */ +public class ScalaTokenAdapter implements GenericToken { + + private Token token; + private GenericToken previousComment; + + ScalaTokenAdapter(Token token, GenericToken comment) { + this.token = token; + this.previousComment = comment; + } + + @Override + public GenericToken getNext() { + // not required + return null; + } + + @Override + public GenericToken getPreviousComment() { + return previousComment; + } + + @Override + public String getImage() { + return token.text(); + } + + @Override + public int getBeginLine() { + return token.pos().startLine(); + } + + @Override + public int getEndLine() { + return token.pos().endLine(); + } + + @Override + public int getBeginColumn() { + return token.pos().startColumn(); + } + + @Override + public int getEndColumn() { + return token.pos().endColumn(); + } +} diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index 4685f63bd9..44ab1ee1e7 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -9,8 +9,11 @@ import java.util.Properties; import org.apache.commons.lang3.StringUtils; +import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.TokenManager; +import net.sourceforge.pmd.lang.ast.GenericToken; import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.scala.ScalaLanguageHandler; import net.sourceforge.pmd.lang.scala.ScalaLanguageModule; @@ -24,7 +27,7 @@ import scala.meta.tokenizers.TokenizeException; import scala.meta.tokens.Token; /** - * Scala Tokenizer class. Uses the Scala Meta Tokenizer. + * Scala Tokenizer class. Uses the Scala Meta Tokenizer, but adapts it for use with generic filtering */ public class ScalaTokenizer implements Tokenizer { @@ -72,19 +75,21 @@ public class ScalaTokenizer implements Tokenizer { // tokenize with a filter try { scala.meta.tokens.Tokens tokens = tokenizer.tokenize(); - ScalaTokenFilter filter = new ScalaTokenFilter(tokens.iterator()); - Token token; + // use extensions to the standard PMD TokenManager and Filter + ScalaTokenManager scalaTokenManager = new ScalaTokenManager(tokens.iterator()); + ScalaTokenFilter filter = new ScalaTokenFilter(scalaTokenManager); + + GenericToken token; while ((token = filter.getNextToken()) != null) { - if (StringUtils.isEmpty(token.text())) { + if (StringUtils.isEmpty(token.getImage())) { continue; } - Position pos = token.pos(); - TokenEntry cpdToken = new TokenEntry(token.text(), + TokenEntry cpdToken = new TokenEntry(token.getImage(), filename, - pos.startLine() + 1, - pos.startColumn() + 1, - pos.endColumn() + 1); + token.getBeginLine() + 1, + token.getBeginColumn() + 1, + token.getEndColumn() + 1); tokenEntries.add(cpdToken); } } catch (Exception e) { @@ -103,19 +108,26 @@ public class ScalaTokenizer implements Tokenizer { } /** - * Token Filter skips un-helpful tokens to only register important tokens + * Implementation of the generic Token Manager, also skips un-helpful tokens to only register important tokens * and patterns. + * + * Keeps track of comments, for special comment processing */ - private static class ScalaTokenFilter { + private class ScalaTokenManager implements TokenManager { + Iterator tokenIter; Class[] skippableTokens = new Class[] { Token.Space.class, Token.Tab.class, Token.CR.class, Token.LF.class, Token.FF.class, Token.LFLF.class, Token.EOF.class }; - ScalaTokenFilter(Iterator iterator) { + GenericToken previousComment = null; + GenericToken result = null; + + ScalaTokenManager(Iterator iterator) { this.tokenIter = iterator; } - Token getNextToken() { + @Override + public GenericToken getNextToken() { if (!tokenIter.hasNext()) { return null; } @@ -125,7 +137,13 @@ public class ScalaTokenizer implements Tokenizer { token = tokenIter.next(); } while (token != null && skipToken(token) && tokenIter.hasNext()); - return token; + result = new ScalaTokenAdapter(token, previousComment); + + if (isComment(token)) { + previousComment = result; + } + + return result; } private boolean skipToken(Token token) { @@ -137,5 +155,27 @@ public class ScalaTokenizer implements Tokenizer { } return skip; } + + private boolean isComment(Token token) { + return token != null && (token.text().startsWith("//") || token.text().startsWith("/*")); + } + + @Override + public void setFileName(String fileName) { + throw new UnsupportedOperationException("setFileName deprecated"); + } } + + private class ScalaTokenFilter extends BaseTokenFilter { + ScalaTokenFilter(TokenManager tokenManager) { + super(tokenManager); + } + + @Override + protected boolean shouldStopProcessing(ScalaTokenAdapter currentToken) { + return currentToken == null; + } + + } + } diff --git a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java index 36865795c5..d09b6c84d1 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java +++ b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java @@ -36,6 +36,11 @@ public class ScalaTokenizerTest extends CpdTextComparisonTest { doTest("sample-LiftActor"); } + @Test + public void testSuppressionComments() { + doTest("special_comments"); + } + @Test public void tokenizeFailTest() { ex.expect(TokenMgrError.class); diff --git a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.scala b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.scala new file mode 100644 index 0000000000..655a06a4a5 --- /dev/null +++ b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.scala @@ -0,0 +1,24 @@ +// Testing CPD suppression + +// Irrelevant comment +// CPD-OFF +// CPD-ON + +case class Foo() { // special multiline comments + + /* CPD-OFF + * + * + * */ val hi = "Hello" /* This is a comment ending in CPD-ON */ + + private def bar(i: Int) : Int = { + val CPD = 40 + val OFF = 60 + CPD-OFF // This should tokenize + } + + /* CPD-OFF */ + def bar2(s: String): String = "bar2" + /* CPD-ON */ + +} diff --git a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt new file mode 100644 index 0000000000..0a54729e1a --- /dev/null +++ b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt @@ -0,0 +1,52 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [// Testing CPD suppression] 1 27 +L3 + [// Irrelevant comment] 1 22 +L4 + [// CPD-OFF] 1 11 +L7 + [case] 1 5 + [class] 6 11 + [Foo] 12 15 + [(] 15 16 + [)] 16 17 + [{] 18 19 + [// special multiline comments] 20 49 +L9 + [/* CPD-OFF\n *\n *\n * */] 3 7 +L14 + [private] 3 10 + [def] 11 14 + [bar] 15 18 + [(] 18 19 + [i] 19 20 + [:] 20 21 + [Int] 22 25 + [)] 25 26 + [:] 27 28 + [Int] 29 32 + [=] 33 34 + [{] 35 36 +L15 + [val] 5 8 + [CPD] 9 12 + [=] 13 14 + [40] 15 17 +L16 + [val] 5 8 + [OFF] 9 12 + [=] 13 14 + [60] 15 17 +L17 + [CPD] 5 8 + [-] 8 9 + [OFF] 9 12 + [// This should tokenize] 15 38 +L18 + [}] 3 4 +L20 + [/* CPD-OFF */] 3 16 +L24 + [}] 1 2 +EOF From 035ec0a5370f409c34d305c6b86f86f16e9823a0 Mon Sep 17 00:00:00 2001 From: Andy Robinson Date: Wed, 18 Nov 2020 13:49:32 +0000 Subject: [PATCH 20/32] [scala] fixing support for CPD-ON and CPD-OFF special comments - minor fix ups as per PR comments - comments are skipped and no longer tokenised --- .../pmd/cpd/ScalaTokenAdapter.java | 19 ++++-- .../sourceforge/pmd/cpd/ScalaTokenizer.java | 23 +++---- .../scala/cpd/testdata/sample-LiftActor.txt | 65 ------------------- .../scala/cpd/testdata/special_comments.txt | 12 ---- 4 files changed, 23 insertions(+), 96 deletions(-) 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 8650c67892..c4842eccf9 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 @@ -23,8 +23,7 @@ public class ScalaTokenAdapter implements GenericToken { @Override public GenericToken getNext() { - // not required - return null; + throw new UnsupportedOperationException(); } @Override @@ -39,21 +38,29 @@ public class ScalaTokenAdapter implements GenericToken { @Override public int getBeginLine() { - return token.pos().startLine(); + return token.pos().startLine() + 1; } @Override public int getEndLine() { - return token.pos().endLine(); + return token.pos().endLine() + 1; } @Override public int getBeginColumn() { - return token.pos().startColumn(); + return token.pos().startColumn() + 1; } @Override public int getEndColumn() { - return token.pos().endColumn(); + return token.pos().endColumn() + 1; + } + + @Override + public String toString() { + return "ScalaTokenAdapter{" + + "token=" + token + + ", previousComment=" + previousComment + + "}"; } } diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index 44ab1ee1e7..a7a009b0d0 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -87,9 +87,9 @@ public class ScalaTokenizer implements Tokenizer { } TokenEntry cpdToken = new TokenEntry(token.getImage(), filename, - token.getBeginLine() + 1, - token.getBeginColumn() + 1, - token.getEndColumn() + 1); + token.getBeginLine(), + token.getBeginColumn(), + token.getEndColumn()); tokenEntries.add(cpdToken); } } catch (Exception e) { @@ -108,7 +108,7 @@ public class ScalaTokenizer implements Tokenizer { } /** - * Implementation of the generic Token Manager, also skips un-helpful tokens to only register important tokens + * Implementation of the generic Token Manager, also skips un-helpful tokens and comments to only register important tokens * and patterns. * * Keeps track of comments, for special comment processing @@ -117,7 +117,7 @@ public class ScalaTokenizer implements Tokenizer { Iterator tokenIter; Class[] skippableTokens = new Class[] { Token.Space.class, Token.Tab.class, Token.CR.class, - Token.LF.class, Token.FF.class, Token.LFLF.class, Token.EOF.class }; + Token.LF.class, Token.FF.class, Token.LFLF.class, Token.EOF.class, Token.Comment.class }; GenericToken previousComment = null; GenericToken result = null; @@ -135,15 +135,12 @@ public class ScalaTokenizer implements Tokenizer { Token token; do { token = tokenIter.next(); + if (isComment(token)) { + previousComment = new ScalaTokenAdapter(token, previousComment); + } } while (token != null && skipToken(token) && tokenIter.hasNext()); - result = new ScalaTokenAdapter(token, previousComment); - - if (isComment(token)) { - previousComment = result; - } - - return result; + return new ScalaTokenAdapter(token, previousComment); } private boolean skipToken(Token token) { @@ -157,7 +154,7 @@ public class ScalaTokenizer implements Tokenizer { } private boolean isComment(Token token) { - return token != null && (token.text().startsWith("//") || token.text().startsWith("/*")); + return token instanceof Token.Comment; } @Override diff --git a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt index 0534d3c538..6c4b243eb2 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt +++ b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt @@ -1,6 +1,4 @@ [Image] or [Truncated image[ Bcol Ecol -L1 - [/* Example source code copied from[ 1 4 L19 [package] 1 8 [net] 9 12 @@ -40,14 +38,10 @@ L26 [Unit] 19 23 L27 [}] 1 2 -L29 - [/**\n * The definition of a schedu[ 1 4 L32 [trait] 1 6 [LAScheduler] 7 18 [{] 19 20 -L33 - [/**\n * Execute some code on ano[ 3 6 L38 [def] 3 6 [execute] 7 14 @@ -79,8 +73,6 @@ L43 [onSameThread] 7 19 [=] 20 21 [false] 22 27 -L45 - [/**\n * Set this variable to the[ 3 6 L48 [@] 3 4 [volatile] 4 12 @@ -88,7 +80,6 @@ L48 [threadPoolSize] 17 31 [=] 32 33 [16] 34 36 - [// issue 194] 37 49 L50 [@] 3 4 [volatile] 4 12 @@ -98,8 +89,6 @@ L50 [threadPoolSize] 37 51 [*] 52 53 [25] 54 56 -L52 - [/**\n * If it's Full, then creat[ 3 6 L57 [@] 3 4 [volatile] 4 12 @@ -149,7 +138,6 @@ L64 [val] 15 18 [es] 19 21 [=] 22 23 - [// Executors.newFixedThreadPool(th[ 24 71 L65 [new] 9 12 [ThreadPoolExecutor] 13 31 @@ -282,8 +270,6 @@ L91 [ILAExecute] 13 23 [=] 24 25 [_] 26 27 -L93 - [/**\n * Execute some code on ano[ 3 6 L98 [def] 3 6 [execute] 7 14 @@ -464,8 +450,6 @@ L127 [MailboxItem] 15 26 [=] 27 28 [_] 29 30 -L129 - [/*\n def find(f: MailboxItem =>[ 5 7 L134 [def] 5 8 [remove] 9 15 @@ -588,8 +572,6 @@ L157 [\]] 71 72 [)] 72 73 [{] 74 75 -L158 - [// override def find(f: MailboxIte[ 5 78 L159 [next] 5 9 [=] 10 11 @@ -659,8 +641,6 @@ L167 [)] 42 43 L168 [}] 5 6 -L170 - [/**\n * Send a message to the Ac[ 3 6 L175 [def] 3 6 [send] 7 11 @@ -675,8 +655,6 @@ L175 [this] 28 32 [!] 33 34 [msg] 35 38 -L177 - [/**\n * Send a message to the Ac[ 3 6 L182 [def] 3 6 [!] 7 8 @@ -793,8 +771,6 @@ L199 [)] 10 11 L200 [}] 3 4 -L202 - [/**\n * This method inserts the [ 3 6 L207 [protected] 3 12 [def] 13 16 @@ -936,8 +912,6 @@ L230 [}] 5 6 L231 [}] 3 4 -L233 - [/**\n * A list of LoanWrappers t[ 3 6 L236 [protected] 3 12 [def] 13 16 @@ -949,8 +923,6 @@ L236 [\]] 52 53 [=] 54 55 [Nil] 56 59 -L238 - [/**\n * You can wrap calls aroun[ 3 6 L242 [protected] 3 12 [def] 13 16 @@ -1574,8 +1546,6 @@ L349 [}] 3 4 L350 [}] 1 2 -L352 - [/**\n * A SpecializedLiftActor des[ 1 4 L362 [class] 1 6 [MockSpecializedLiftActor] 7 31 @@ -1602,8 +1572,6 @@ L363 [\]] 45 46 [=] 47 48 [Nil] 49 52 -L365 - [/**\n * Send a message to the mo[ 3 6 L369 [override] 3 11 [def] 12 15 @@ -1630,10 +1598,6 @@ L372 [}] 5 6 L373 [}] 3 4 -L375 - [// We aren't required to implement[ 3 79 -L376 - [// since the message handler never[ 3 43 L377 [override] 3 11 [def] 12 15 @@ -1653,8 +1617,6 @@ L378 [=>] 12 14 L379 [}] 3 4 -L381 - [/**\n * Test to see if this acto[ 3 6 L384 [def] 3 6 [hasReceivedMessage_?] 7 27 @@ -1672,8 +1634,6 @@ L384 [(] 72 73 [msg] 73 76 [)] 76 77 -L386 - [/**\n * Returns the list of mess[ 3 6 L389 [def] 3 6 [messages] 7 15 @@ -1684,8 +1644,6 @@ L389 [\]] 23 24 [=] 25 26 [messagesReceived] 27 43 -L391 - [/**\n * Return the number of mes[ 3 6 L394 [def] 3 6 [messageCount] 7 19 @@ -1830,8 +1788,6 @@ L417 [msg] 24 27 L418 [}] 3 4 -L420 - [/**\n * Send a message to the Act[ 3 5 L425 [def] 3 6 [sendAndGetFuture] 7 23 @@ -1849,8 +1805,6 @@ L425 [this] 51 55 [!<] 56 58 [msg] 59 62 -L427 - [/**\n * Send a message to the Act[ 3 5 L431 [def] 3 6 [!<] 7 9 @@ -1888,8 +1842,6 @@ L434 [future] 5 11 L435 [}] 3 4 -L437 - [/**\n * Send a message to the Act[ 3 5 L442 [def] 3 6 [sendAndGetReply] 7 22 @@ -1904,8 +1856,6 @@ L442 [this] 40 44 [!?] 45 47 [msg] 48 51 -L444 - [/**\n * Send a message to the Act[ 3 5 L448 [def] 3 6 [!?] 7 9 @@ -1942,8 +1892,6 @@ L451 [get] 12 15 L452 [}] 3 4 -L455 - [/**\n * Send a message to the Act[ 3 5 L461 [def] 3 6 [sendAndGetReply] 7 22 @@ -1967,8 +1915,6 @@ L461 [,] 70 71 [msg] 72 75 [)] 75 76 -L463 - [/**\n * Send a message to the Act[ 3 5 L468 [def] 3 6 [!?] 7 9 @@ -1995,8 +1941,6 @@ L469 [,] 21 22 [timeout] 23 30 [)] 30 31 -L472 - [/**\n * Send a message to the A[ 5 7 L477 [def] 3 6 [!!] 7 9 @@ -2043,8 +1987,6 @@ L480 [)] 23 24 L481 [}] 3 4 -L483 - [/**\n * Send a message to the Act[ 3 5 L487 [def] 3 6 [!!] 7 9 @@ -2193,8 +2135,6 @@ L506 [)] 18 19 L507 [}] 3 4 -L509 - [/**\n * The Actor should call thi[ 3 5 L513 [protected] 3 12 [def] 13 16 @@ -2226,8 +2166,6 @@ L517 [}] 3 4 L518 [}] 1 2 -L520 - [/**\n * A MockLiftActor for use in[ 1 4 L529 [class] 1 6 [MockLiftActor] 7 20 @@ -2503,7 +2441,6 @@ L565 [(] 24 25 [true] 25 29 [)] 29 30 - [// access private and protected me[ 31 70 L566 [m] 9 10 [.] 10 11 @@ -2826,8 +2763,6 @@ L604 [}] 5 6 L605 [}] 1 2 -L607 - [/**\n * Java versions of Actors sh[ 1 4 L612 [class] 1 6 [LiftActorJ] 7 17 diff --git a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt index 0a54729e1a..174012eb14 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt +++ b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt @@ -1,10 +1,4 @@ [Image] or [Truncated image[ Bcol Ecol -L1 - [// Testing CPD suppression] 1 27 -L3 - [// Irrelevant comment] 1 22 -L4 - [// CPD-OFF] 1 11 L7 [case] 1 5 [class] 6 11 @@ -12,9 +6,6 @@ L7 [(] 15 16 [)] 16 17 [{] 18 19 - [// special multiline comments] 20 49 -L9 - [/* CPD-OFF\n *\n *\n * */] 3 7 L14 [private] 3 10 [def] 11 14 @@ -42,11 +33,8 @@ L17 [CPD] 5 8 [-] 8 9 [OFF] 9 12 - [// This should tokenize] 15 38 L18 [}] 3 4 -L20 - [/* CPD-OFF */] 3 16 L24 [}] 1 2 EOF From ff77650801001c5e33a175b5ec5182fd47c02026 Mon Sep 17 00:00:00 2001 From: Igor Moreno Date: Thu, 19 Nov 2020 22:59:17 +0100 Subject: [PATCH 21/32] Fix typo: "an accessor" not "a" --- pmd-java/src/main/resources/category/java/bestpractices.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index cf37657286..2961f28cf1 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -71,7 +71,7 @@ public class Outer { class="net.sourceforge.pmd.lang.java.rule.bestpractices.AccessorMethodGenerationRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#accessormethodgeneration"> -When accessing a private field / method from another class, the Java compiler will generate a accessor methods +When accessing a private field / method from another class, the Java compiler will generate an accessor methods with package-private visibility. This adds overhead, and to the dex method count on Android. This situation can be avoided by changing the visibility of the field / method from private to package-private. From 40d4f4a910a9fb3587892fe510cea4d0be6c70d0 Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Fri, 20 Nov 2020 15:30:23 +0100 Subject: [PATCH 22/32] [cs] CPD: fix issue where ignoring using directives could not be disabled via the GUI after enabling it --- pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java | 4 +--- 1 file changed, 1 insertion(+), 3 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 f56ee3d1a4..10fb5aed89 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,7 @@ public class CsTokenizer extends AntlrTokenizer { private boolean ignoreUsings = false; public void setProperties(Properties properties) { - if (properties.containsKey(IGNORE_USINGS)) { - ignoreUsings = Boolean.parseBoolean(properties.getProperty(IGNORE_USINGS, "false")); - } + ignoreUsings = Boolean.parseBoolean(properties.getProperty(IGNORE_USINGS, "false")); } public void setIgnoreUsings(boolean ignoreUsings) { From 74c0c020e1c0a0cbdcaf0de9d36c1797400657f8 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 20 Nov 2020 16:18:18 +0100 Subject: [PATCH 23/32] Use new dokka javadoc path --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4085faae59..7de63c440a 100644 --- a/pom.xml +++ b/pom.xml @@ -310,7 +310,7 @@ false - ${project.basedir}/../pmd-lang-test/target/dokka/pmd-lang-test + ${project.basedir}/../pmd-lang-test/target/dokkaJavadocJar ../../pmd-lang-test/${project.version} From af117ac9c7fd3c9175c8232660ab9cf5f7b2a301 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 09:10:25 +0100 Subject: [PATCH 24/32] Bump m-pmd-p from 3.13.0 to 3.14.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7de63c440a..6cc41bf1d9 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ 3.0.0-M5 8.30 3.1.1 - 3.13.0 + 3.14.0 1.10.8 3.2.0 4.7.2 From 08f2ce30f02cf5c82aee95fb610c0eb88a3ea680 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 09:43:50 +0100 Subject: [PATCH 25/32] [doc] Update release notes, refs #2938 --- 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 12ef7a5855..c1021dcc1e 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -16,6 +16,8 @@ This is a {{ site.pmd.release_type }} release. ### Fixed Issues +* cs + * [#2938](https://github.com/pmd/pmd/pull/2938): \[cs] CPD: ignoring using directives could not be disabled * pmd-core * [#1939](https://github.com/pmd/pmd/issues/1939): \[core] XPath expressions return handling @@ -49,5 +51,6 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr ### External Contributions * [#2925](https://github.com/pmd/pmd/pull/2925): Cleanup: Correct annotation array initializer indents from checkstyle #8083 - [Abhishek Kumar](https://github.com/Abhishek-kumar09) +* [#2938](https://github.com/pmd/pmd/pull/2938): \[cs] CPD: fix issue where ignoring using directives could not be disabled - [Maikel Steneker](https://github.com/maikelsteneker) {% endtocmaker %} From 43d291ba735988f2f2450faa39f1cff08d42b294 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 10:25:05 +0100 Subject: [PATCH 26/32] [scala] ScalaTokenizer: Make inner classes static --- .../main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index a7a009b0d0..c545b26701 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -113,14 +113,13 @@ public class ScalaTokenizer implements Tokenizer { * * Keeps track of comments, for special comment processing */ - private class ScalaTokenManager implements TokenManager { + private static class ScalaTokenManager implements TokenManager { Iterator tokenIter; Class[] skippableTokens = new Class[] { Token.Space.class, Token.Tab.class, Token.CR.class, Token.LF.class, Token.FF.class, Token.LFLF.class, Token.EOF.class, Token.Comment.class }; GenericToken previousComment = null; - GenericToken result = null; ScalaTokenManager(Iterator iterator) { this.tokenIter = iterator; @@ -163,7 +162,7 @@ public class ScalaTokenizer implements Tokenizer { } } - private class ScalaTokenFilter extends BaseTokenFilter { + private static class ScalaTokenFilter extends BaseTokenFilter { ScalaTokenFilter(TokenManager tokenManager) { super(tokenManager); } From f7be9256e030b5631e8ceffdd7f310b6a51fc76f Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 10:44:50 +0100 Subject: [PATCH 27/32] [doc] Update release notes, refs #2929, fixes #2480 --- docs/pages/release_notes.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index bda687c234..97d3c7d95e 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -18,7 +18,8 @@ This is a {{ site.pmd.release_type }} release. * pmd-core * [#1939](https://github.com/pmd/pmd/issues/1939): \[core] XPath expressions return handling - +* scala + * [#2480](https://github.com/pmd/pmd/issues/2480): \[scala] Support CPD suppressions ### API Changes @@ -48,4 +49,6 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr ### External Contributions +* [#2929](https://github.com/pmd/pmd/pull/2929): \[scala] Add support for CPD-ON and CPD-OFF special comments - [Andy Robinson](https://github.com/andyrobinson) + {% endtocmaker %} From 8cf426a4c3ea34e3bf3b2cb8e09ed8d861938e5b Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 11:15:58 +0100 Subject: [PATCH 28/32] [doc] Update text renderer format example --- docs/pages/pmd/userdocs/pmd_report_formats.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pages/pmd/userdocs/pmd_report_formats.md b/docs/pages/pmd/userdocs/pmd_report_formats.md index 4bbfce41ec..5ca846ac69 100644 --- a/docs/pages/pmd/userdocs/pmd_report_formats.md +++ b/docs/pages/pmd/userdocs/pmd_report_formats.md @@ -169,8 +169,8 @@ and configuration errors are reported. Example: ``` -/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java:124: Logger calls should be surrounded by log level guards. -/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java:58: This for loop can be replaced by a foreach loop +/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java:124: GuardLogStatement: Logger calls should be surrounded by log level guards. +/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java:58: ForLoopCanBeForeach: This for loop can be replaced by a foreach loop /home/pmd/source/pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java - PMDException: Error while parsing /home/pmd/source/pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java CloseResource rule violation suppressed by Annotation in /home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java LoosePackageCoupling - No packages or classes specified From 7d82cec763df935c487be81a177766cc62d03c08 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 11:16:12 +0100 Subject: [PATCH 29/32] [doc] Update release notes, refs #2914, fixes #1961 --- 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 bda687c234..4e9e47e532 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -18,6 +18,7 @@ This is a {{ site.pmd.release_type }} release. * pmd-core * [#1939](https://github.com/pmd/pmd/issues/1939): \[core] XPath expressions return handling + * [#1961](https://github.com/pmd/pmd/issues/1961): \[core] Text renderer should include name of violated rule ### API Changes @@ -48,4 +49,6 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr ### External Contributions +* [#2914](https://github.com/pmd/pmd/pull/2914): \[core] Include rule name in text renderer - [Gunther Schrijvers](https://github.com/GuntherSchrijvers) + {% endtocmaker %} From 1783b1daa108726d43363ac1e4899edd8730d52b Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 13:55:48 +0100 Subject: [PATCH 30/32] Update pmd-java/src/main/resources/category/java/bestpractices.xml Co-authored-by: Igor Moreno --- pmd-java/src/main/resources/category/java/bestpractices.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 2961f28cf1..59e5e56c72 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -71,7 +71,7 @@ public class Outer { class="net.sourceforge.pmd.lang.java.rule.bestpractices.AccessorMethodGenerationRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#accessormethodgeneration"> -When accessing a private field / method from another class, the Java compiler will generate an accessor methods +When accessing private fields / methods from another class, the Java compiler will generate accessor methods with package-private visibility. This adds overhead, and to the dex method count on Android. This situation can be avoided by changing the visibility of the field / method from private to package-private. From 30b8a41bbafb2eb385e5e48c59825f0cc50f5dd7 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 13:58:01 +0100 Subject: [PATCH 31/32] [doc] Update release notes, refs #2936 --- 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 12ef7a5855..529a0b6478 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -49,5 +49,6 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr ### External Contributions * [#2925](https://github.com/pmd/pmd/pull/2925): Cleanup: Correct annotation array initializer indents from checkstyle #8083 - [Abhishek Kumar](https://github.com/Abhishek-kumar09) +* [#2936](https://github.com/pmd/pmd/pull/2936): \[java] (doc) Fix typo: "an accessor" not "a" - [Igor Moreno](https://github.com/igormoreno) {% endtocmaker %} From 9aaae814c482e9261b64545670df0e81b5dd5020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 23 Nov 2020 14:39:51 +0100 Subject: [PATCH 32/32] Handle TNPE and LinkageE when loading type params --- .../typeresolution/typedefinition/JavaTypeDefinition.java | 7 +------ .../typedefinition/JavaTypeDefinitionSimple.java | 6 +++++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java index 09ce29655d..ad6af12386 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java @@ -61,12 +61,7 @@ public abstract class JavaTypeDefinition implements TypeDefinition { if (clazz == null) { return null; } - - try { - return new JavaTypeDefinitionSimple(clazz, boundGenerics); - } catch (final LinkageError e) { - return null; // Can happen if a parent class references a class not in classpath - } + return new JavaTypeDefinitionSimple(clazz, boundGenerics); } @Override diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java index 5ced2ec288..37fdab51bd 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java @@ -96,7 +96,11 @@ import java.util.logging.Logger; @Override public int getTypeParameterCount() { if (typeParameterCount == -1) { - typeParameterCount = getTypeParameters(clazz).length; + try { + typeParameterCount = getTypeParameters(clazz).length; + } catch (LinkageError | TypeNotPresentException ignored) { + typeParameterCount = 0; // don't stay stuck on -1 + } } return typeParameterCount; }