diff --git a/SUPPORTED-FORMATS.md b/SUPPORTED-FORMATS.md index 1fe47dcdb..514b1e0fd 100644 --- a/SUPPORTED-FORMATS.md +++ b/SUPPORTED-FORMATS.md @@ -1681,7 +1681,7 @@ analyze - iccxxxxcompiler_opts cstat2.cFor details check the IAR C- - :bulb:

Create a./pylintrc that contains:

msg-template={path}:{module}:{line}: [{msg_id}({symbol}), {obj}] {msg}

Start pylint using the command:

pylint --rcfile=./pylintrc CODE > pylint.log

+ :bulb:

Start Pylint using this custom message template (can also be configured via a pylintrc configuration file):

pylint --msg-template='{path}:{line}: [{msg_id}, {obj}] {msg} ({symbol})' modules_or_packages > pylint.log

diff --git a/pom.xml b/pom.xml index 952918ffc..7b3129a4e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,4 @@ - 4.0.0 @@ -54,7 +53,7 @@ HEAD - 11.4.0 + 11.5.0 -SNAPSHOT edu.hm.hafner.analysis.model @@ -71,7 +70,7 @@ 1.156.5 1.16.1 20230618 - 2.4.11 + 2.5.0 6.55.0 4.7.3 @@ -477,6 +476,15 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + all + true + 1C + + diff --git a/src/main/java/edu/hm/hafner/analysis/parser/AbstractMavenLogParser.java b/src/main/java/edu/hm/hafner/analysis/parser/AbstractMavenLogParser.java index 84f5d4bb1..5eca404e5 100644 --- a/src/main/java/edu/hm/hafner/analysis/parser/AbstractMavenLogParser.java +++ b/src/main/java/edu/hm/hafner/analysis/parser/AbstractMavenLogParser.java @@ -26,6 +26,7 @@ public abstract class AbstractMavenLogParser extends LookaheadParser { private static final String MAVEN_PLUGIN_SUFFIX = "-plugin"; static final String MAVEN_COMPILER_PLUGIN = MAVEN_PLUGIN_PREFIX + "compiler" + MAVEN_PLUGIN_SUFFIX; static final String MAVEN_JAVADOC_PLUGIN = MAVEN_PLUGIN_PREFIX + "javadoc" + MAVEN_PLUGIN_SUFFIX; + static final String MAVEN_HPI_PLUGIN = MAVEN_PLUGIN_PREFIX + "hpi" + MAVEN_PLUGIN_SUFFIX; static final String MAVEN_ENFORCER_PLUGIN = MAVEN_PLUGIN_PREFIX + "enforcer" + MAVEN_PLUGIN_SUFFIX; private String goal = StringUtils.EMPTY; private String module = StringUtils.EMPTY; diff --git a/src/main/java/edu/hm/hafner/analysis/parser/ClangAnalyzerPlistParser.java b/src/main/java/edu/hm/hafner/analysis/parser/ClangAnalyzerPlistParser.java index 96ce84f3a..ff9a17d46 100644 --- a/src/main/java/edu/hm/hafner/analysis/parser/ClangAnalyzerPlistParser.java +++ b/src/main/java/edu/hm/hafner/analysis/parser/ClangAnalyzerPlistParser.java @@ -17,6 +17,7 @@ import edu.hm.hafner.analysis.ParsingException; import edu.hm.hafner.analysis.ReaderFactory; import edu.hm.hafner.analysis.Report; +import edu.hm.hafner.analysis.util.IntegerParser; import edu.hm.hafner.analysis.util.XmlElementUtil; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -126,7 +127,6 @@ private static String extractField(final Element diag, final XPathExpression exp } private static int extractIntField(final Element diag, final XPathExpression expr) throws XPathExpressionException { - String val = extractField(diag, expr); - return Integer.parseInt(val); + return IntegerParser.parseInt(extractField(diag, expr)); } } diff --git a/src/main/java/edu/hm/hafner/analysis/parser/FlawfinderParser.java b/src/main/java/edu/hm/hafner/analysis/parser/FlawfinderParser.java index 7be982ad5..6659ef054 100644 --- a/src/main/java/edu/hm/hafner/analysis/parser/FlawfinderParser.java +++ b/src/main/java/edu/hm/hafner/analysis/parser/FlawfinderParser.java @@ -7,6 +7,7 @@ import edu.hm.hafner.analysis.IssueBuilder; import edu.hm.hafner.analysis.LookaheadParser; import edu.hm.hafner.analysis.Severity; +import edu.hm.hafner.analysis.util.IntegerParser; import edu.hm.hafner.util.LookaheadStream; /** @@ -35,15 +36,8 @@ protected Optional createIssue(final Matcher matcher, final LookaheadStre final IssueBuilder builder) { String message = matcher.group("message"); String category = matcher.group("category"); - int severity = Integer.parseInt(matcher.group("severity")); - Severity priority = Severity.WARNING_LOW; - if (severity >= FLAWFINDER_HIGH_THRESHOLD) { - priority = Severity.WARNING_HIGH; - } - else if (severity >= FLAWFINDER_NORMAL_THRESHOLD) { - priority = Severity.WARNING_NORMAL; - } + var priority = extractPriority(IntegerParser.parseInt(matcher.group("severity"))); return builder.setFileName(matcher.group("file")) .setLineStart(matcher.group("line")) @@ -52,4 +46,14 @@ else if (severity >= FLAWFINDER_NORMAL_THRESHOLD) { .setSeverity(priority) .buildOptional(); } + + private Severity extractPriority(final int severity) { + if (severity >= FLAWFINDER_HIGH_THRESHOLD) { + return Severity.WARNING_HIGH; + } + else if (severity >= FLAWFINDER_NORMAL_THRESHOLD) { + return Severity.WARNING_NORMAL; + } + return Severity.WARNING_LOW; + } } diff --git a/src/main/java/edu/hm/hafner/analysis/parser/IdeaInspectionParser.java b/src/main/java/edu/hm/hafner/analysis/parser/IdeaInspectionParser.java index fa0a1963e..78188b6b5 100644 --- a/src/main/java/edu/hm/hafner/analysis/parser/IdeaInspectionParser.java +++ b/src/main/java/edu/hm/hafner/analysis/parser/IdeaInspectionParser.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.Optional; +import edu.hm.hafner.analysis.util.IntegerParser; import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringEscapeUtils; import org.w3c.dom.Document; @@ -43,7 +44,7 @@ private Report parseProblems(final List elements) { if (problemClass.isPresent()) { Element problem = problemClass.get(); issueBuilder.setFileName(stripPathPrefix(file)) - .setLineStart(Integer.parseInt(getChildValue(element, "line"))) + .setLineStart(IntegerParser.parseInt(getChildValue(element, "line"))) .setCategory(StringEscapeUtils.unescapeXml(getValue(problem))) .setMessage(StringEscapeUtils.unescapeXml(getChildValue(element, "description"))) .setModuleName(StringEscapeUtils.unescapeXml(getChildValue(element, "module"))) diff --git a/src/main/java/edu/hm/hafner/analysis/parser/JavacParser.java b/src/main/java/edu/hm/hafner/analysis/parser/JavacParser.java index 76748d65d..3c422b868 100644 --- a/src/main/java/edu/hm/hafner/analysis/parser/JavacParser.java +++ b/src/main/java/edu/hm/hafner/analysis/parser/JavacParser.java @@ -25,17 +25,19 @@ public class JavacParser extends AbstractMavenLogParser { private static final String ERROR_PRONE_URL_PATTERN = "\\s+\\(see https?://\\S+\\s*\\)"; private static final String JAVAC_WARNING_PATTERN - = "^(?:\\S+\\s+)?" // optional preceding arbitrary number of characters that are not a - // whitespace followed by whitespace. This can be used for timestamps. + = "^(?:\\S+\\s+)?" // optional preceding arbitrary number of characters that are not a + // whitespace followed by whitespace. This can be used for timestamps. + "(?:(?:\\[(WARNING|ERROR)\\]|w:|e:)\\s+)" // optional [WARNING] or [ERROR] or w: or e: - + "([^\\[\\(]*):\\s*" // group 1: filename - + "[\\[\\(]" // [ or ( - + "(\\d+)[.,;]*" // group 2: line number - + "\\s?(\\d+)?" // group 3: optional column - + "[\\]\\)]\\s*" // ] or ) - + ":?" // optional : - + "(?:\\[(\\w+)\\])?" // group 4: optional category - + "\\s*(.*)$"; // group 5: message + + "(((\\/?[a-zA-Z]|file):)?[^\\[\\(:]*):" // group 2: filename starting path with C:\ or /C:\ or file:/// or / + + "(" // start group 5 + + "(\\s*[\\[\\(]?)?" // optional ( or [ + + "(\\d+)" // group 7 line + + "[.,;]?\\s?:?" // separator + + "(\\d+)?" // group 8 column + + "[\\]\\)]?\\s*:?\\s?" // optional ) or ] or whitespace or : + + ")" // end group 5 + + "(?:\\[(\\w+)\\])?" // group 9: optional category + + "\\s*(.*)$"; // group 10: message private static final String SEVERITY_ERROR = "ERROR"; private static final String SEVERITY_ERROR_SHORT = "e:"; @@ -51,7 +53,7 @@ public JavacParser() { @Override protected boolean isLineInteresting(final String line) { return (line.contains("[") || line.contains("w:") || line.contains("e:")) - && !hasGoals(MAVEN_JAVADOC_PLUGIN); + && !hasGoals(MAVEN_JAVADOC_PLUGIN, MAVEN_HPI_PLUGIN); } @Override @@ -69,14 +71,14 @@ protected Optional createIssue(final Matcher matcher, final LookaheadStre builder.setSeverity(Severity.WARNING_NORMAL); } - String message = matcher.group(6); - String category = guessCategoryIfEmpty(matcher.group(5), message); + String message = matcher.group(10); + String category = guessCategoryIfEmpty(matcher.group(9), message); // get rid of leading / from windows compiler output JENKINS-66738 return builder.setFileName(RegExUtils.replaceAll(matcher.group(2), "^/([a-zA-Z]):", "$1:")) - .setLineStart(matcher.group(3)) + .setLineStart(matcher.group(7)) .setType(StringUtils.defaultString(getGoal(), DEFAULT_GOAL)) - .setColumnStart(matcher.group(4)) + .setColumnStart(matcher.group(8)) .setCategory(category) .setMessage(message) .buildOptional(); diff --git a/src/main/java/edu/hm/hafner/analysis/parser/StyleCopParser.java b/src/main/java/edu/hm/hafner/analysis/parser/StyleCopParser.java index 47809ba98..284839762 100644 --- a/src/main/java/edu/hm/hafner/analysis/parser/StyleCopParser.java +++ b/src/main/java/edu/hm/hafner/analysis/parser/StyleCopParser.java @@ -13,10 +13,9 @@ import edu.hm.hafner.analysis.ReaderFactory; import edu.hm.hafner.analysis.Report; import edu.hm.hafner.analysis.Severity; +import edu.hm.hafner.analysis.util.IntegerParser; import edu.hm.hafner.analysis.util.XmlElementUtil; -import static java.lang.Integer.*; - /** * Parses a StyleCop XML report files. * @@ -105,7 +104,7 @@ private String getString(final Element element, final String name) { */ private int getLineNumber(final Element violation) { if (violation.hasAttribute("LineNumber")) { - return parseInt(violation.getAttribute("LineNumber")); + return IntegerParser.parseInt(violation.getAttribute("LineNumber")); } else { return 0; diff --git a/src/main/java/edu/hm/hafner/analysis/parser/XmlParser.java b/src/main/java/edu/hm/hafner/analysis/parser/XmlParser.java index 5be1a1f3d..02a52fc05 100644 --- a/src/main/java/edu/hm/hafner/analysis/parser/XmlParser.java +++ b/src/main/java/edu/hm/hafner/analysis/parser/XmlParser.java @@ -135,9 +135,8 @@ private LineRangeList readLineRanges(final XPath path, final NodeList lineRanges ranges.add(new LineRange(start, end)); } catch (NumberFormatException e) { - // Invalid value in xml. + // Ignore invalid values in xml } - } } } diff --git a/src/main/java/edu/hm/hafner/analysis/registry/PyLintDescriptor.java b/src/main/java/edu/hm/hafner/analysis/registry/PyLintDescriptor.java index 541032be2..2d67af972 100644 --- a/src/main/java/edu/hm/hafner/analysis/registry/PyLintDescriptor.java +++ b/src/main/java/edu/hm/hafner/analysis/registry/PyLintDescriptor.java @@ -28,11 +28,8 @@ public IssueParser createParser(final Option... options) { @Override public String getHelp() { - return "

Create a ./pylintrc that contains:" - + "

msg-template={path}:{module}:{line}: [{msg_id}({symbol}), {obj}] {msg}

" - + "

" - + "

Start pylint using the command:" - + "

pylint --rcfile=./pylintrc CODE > pylint.log

" + return "

Start Pylint using this custom message template (can also be configured via a pylintrc configuration file):" + + "

pylint --msg-template='{path}:{line}: [{msg_id}, {obj}] {msg} ({symbol})' modules_or_packages > pylint.log

" + "

"; } diff --git a/src/test/java/edu/hm/hafner/analysis/ArchitectureTest.java b/src/test/java/edu/hm/hafner/analysis/ArchitectureTest.java index dd86d784e..ce25ee811 100644 --- a/src/test/java/edu/hm/hafner/analysis/ArchitectureTest.java +++ b/src/test/java/edu/hm/hafner/analysis/ArchitectureTest.java @@ -1,4 +1,4 @@ -package edu.hm.hafner.analysis; +package edu.hm.hafner.analysis; //NOPMD - suppressed TooManyStaticImports import javax.xml.parsers.SAXParser; @@ -12,6 +12,10 @@ import edu.hm.hafner.util.ArchitectureRules; +import static com.tngtech.archunit.core.domain.JavaAccess.Predicates.*; +import static com.tngtech.archunit.core.domain.JavaClass.Predicates.*; +import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.*; +import static com.tngtech.archunit.lang.conditions.ArchPredicates.*; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*; /** @@ -22,6 +26,12 @@ @SuppressWarnings("hideutilityclassconstructor") @AnalyzeClasses(packages = "edu.hm.hafner.analysis") class ArchitectureTest { + /** Replace all calls of {@link Integer#parseInt(String)} with IntegerParser alternative. */ + @ArchTest + static final ArchRule NO_INTEGER_PARSE_INT = + noClasses().should().callCodeUnitWhere(targetOwner(is(type(Integer.class))).and(target(name("parseInt")))) + .because("only save IntegerParser.parseInt should be used to parse integer values"); + /** Digester must not be used directly, rather use a SecureDigester instance. */ @ArchTest static final ArchRule NO_DIGESTER_CONSTRUCTOR_CALLED = diff --git a/src/test/java/edu/hm/hafner/analysis/parser/IdeaInspectionParserTest.java b/src/test/java/edu/hm/hafner/analysis/parser/IdeaInspectionParserTest.java index a3b3765c2..fab0ecf3f 100644 --- a/src/test/java/edu/hm/hafner/analysis/parser/IdeaInspectionParserTest.java +++ b/src/test/java/edu/hm/hafner/analysis/parser/IdeaInspectionParserTest.java @@ -50,7 +50,7 @@ protected void assertThatIssuesArePresent(final Report report, final SoftAsserti @Test void issue56235() { Report warnings = parse("issue56235.xml"); - assertThat(warnings).hasSize(6); + assertThat(warnings).hasSize(7); Iterator iterator = warnings.iterator(); @@ -101,6 +101,14 @@ void issue56235() { "Method invocation getCodeFragment may produce NullPointerException") .hasFileName( "$PROJECT_DIR$/src/test/java/edu/hm/hafner/analysis/parser/dry/cpd/CpdParserTest.java"); + softly.assertThat(iterator.next()) + .hasSeverity(Severity.WARNING_LOW) + .hasCategory("@NotNull/@Nullable problems") + .hasLineStart(0) + .hasLineEnd(0) + .hasMessage( + "Not 'edu.umd.cs.findbugs.annotations.Nullable' but 'org.jetbrains.annotations.Nullable' would be used for code generation.") + .hasFileName("$PROJECT_DIR$/src/main/java/edu/hm/hafner/analysis/IssueBuilder.java"); } } } diff --git a/src/test/java/edu/hm/hafner/analysis/parser/JavacParserTest.java b/src/test/java/edu/hm/hafner/analysis/parser/JavacParserTest.java index fd202fc0d..23273b060 100644 --- a/src/test/java/edu/hm/hafner/analysis/parser/JavacParserTest.java +++ b/src/test/java/edu/hm/hafner/analysis/parser/JavacParserTest.java @@ -299,5 +299,48 @@ void shouldParseJavaWarningsInMavenCompilerPlugin() { .hasLineStart(194) .hasFileName("/home/runner/work/warnings-ng-plugin/warnings-ng-plugin/plugin/target/generated-test-sources/assertj-assertions/io/jenkins/plugins/analysis/core/assertions/Assertions.java"); } + + /** + * Parses a warning log written by Gradle containing 2 Kotlin warnings. + * One in kotlin 1.8 style and the other one in the old style. + */ + @Test + void kotlin18WarningStyle() { + Report warnings = parse("kotlin-1_8.txt"); + + assertThat(warnings).hasSize(7); + + assertThat(warnings.get(0)).hasSeverity(Severity.WARNING_NORMAL) + .hasLineStart(214) + .hasColumnStart(35) + .hasFileName("/project/app/src/main/java/ui/Activity.kt"); + assertThat(warnings.get(1)).hasSeverity(Severity.WARNING_NORMAL) + .hasLineStart(424) + .hasColumnStart(29) + .hasFileName("/project/app/src/main/java/ui/Activity.kt"); + assertThat(warnings.get(2)).hasSeverity(Severity.WARNING_NORMAL) + .hasLineStart(425) + .hasColumnStart(29) + .hasFileName("/project/app/src/main/java/ui/Activity.kt") + .hasCategory("Deprecation") + .hasMessage("deprecated: Serializable! to kotlin.collections.HashMap /* = java.util.HashMap */"); + assertThat(warnings.get(3)).hasSeverity(Severity.WARNING_NORMAL) + .hasLineStart(424) + .hasColumnStart(29) + .hasFileName("/project/app/src/main/java/ui/Activity.kt"); + assertThat(warnings.get(4)).hasSeverity(Severity.WARNING_NORMAL) + .hasLineStart(123) + .hasColumnStart(456); + assertThat(warnings.get(5)).hasSeverity(Severity.WARNING_NORMAL) + .hasLineStart(426) + .hasColumnStart(29) + .hasMessage("Unchecked cast: Serializable! to kotlin.collections.HashMap /* = java.util.HashMap */"); + assertThat(warnings.get(6)).hasSeverity(Severity.WARNING_NORMAL) + .hasLineStart(8) + .hasColumnStart(27) + .hasCategory("Deprecation") + .hasFileName("file:///project/src/main/java/com/app/ui/model/Activity.kt") + .hasMessage("'PackageStats' is deprecated. Deprecated in Java"); + } } diff --git a/src/test/resources/archunit_ignore_patterns.txt b/src/test/resources/archunit_ignore_patterns.txt index 653fad059..e07ede8b1 100644 --- a/src/test/resources/archunit_ignore_patterns.txt +++ b/src/test/resources/archunit_ignore_patterns.txt @@ -5,3 +5,8 @@ // Assertions.assertTimeoutPreemptively from JUnit 5 is ok to use .*org.junit.jupiter.api.Assertions.assertTimeoutPreemptively.* + +// Here Integer.parseInt is ok to use since the exception is caught +.*edu.hm.hafner.analysis.parser.XmlParser.readLineRanges.* +.*edu.hm.hafner.analysis.registry.DryDescriptor.convertThreshold.* +.*edu.hm.hafner.analysis.util.IntegerParser.parseInt.* diff --git a/src/test/resources/edu/hm/hafner/analysis/parser/issue56235.xml b/src/test/resources/edu/hm/hafner/analysis/parser/issue56235.xml index 6a7704a1d..3126f3ab2 100644 --- a/src/test/resources/edu/hm/hafner/analysis/parser/issue56235.xml +++ b/src/test/resources/edu/hm/hafner/analysis/parser/issue56235.xml @@ -58,4 +58,12 @@ Constant conditions & exceptions Method invocation <code>getCodeFragment</code> may produce <code>NullPointerException</code> + + file://$PROJECT_DIR$/src/main/java/edu/hm/hafner/analysis/IssueBuilder.java + analysis-model + edu.hm.hafner.analysis + + @NotNull/@Nullable problems + Not 'edu.umd.cs.findbugs.annotations.Nullable' but 'org.jetbrains.annotations.Nullable' would be used for code generation. + diff --git a/src/test/resources/edu/hm/hafner/analysis/parser/kotlin-1_8.txt b/src/test/resources/edu/hm/hafner/analysis/parser/kotlin-1_8.txt new file mode 100644 index 000000000..c2a022737 --- /dev/null +++ b/src/test/resources/edu/hm/hafner/analysis/parser/kotlin-1_8.txt @@ -0,0 +1,13 @@ +> Configure project :app +Configuration 'compile' in project ':app' is deprecated. Use 'implementation' instead. +registerResGeneratingTask is deprecated, use registerGeneratedFolders(FileCollection) +registerResGeneratingTask is deprecated, use registerGeneratedFolders(FileCollection) +registerResGeneratingTask is deprecated, use registerGeneratedFolders(FileCollection) +app: 'annotationProcessor' dependencies won't be recognized as kapt annotation processors. Please change the configuration name to 'kapt' for these artifacts: 'com.android.databinding:compiler:3.0.1'. +w: /project/app/src/main/java/ui/Activity.kt: (214, 35): Unchecked cast: Serializable! to kotlin.collections.HashMap /* = java.util.HashMap */ +w: /project/app/src/main/java/ui/Activity.kt:424:29 Unchecked cast: Serializable! to kotlin.collections.HashMap /* = java.util.HashMap */ +e: /project/app/src/main/java/ui/Activity.kt:425:29 deprecated: Serializable! to kotlin.collections.HashMap /* = java.util.HashMap */ +w: /project/app/src/main/java/ui/Activity.kt:424:29 Serializable! to kotlin.collections.HashMap /* = java.util.HashMap */ +w: /project/app/src/main/java/ui/Activity.kt:123:456 Unchecked cast: Serializable! to kotlin.collections.HashMap /* = java.util.HashMap */ +w: c:\project\app\src\main\java\ui\Activity.kt:426:29 Unchecked cast: Serializable! to kotlin.collections.HashMap /* = java.util.HashMap */ +w: file:///project/src/main/java/com/app/ui/model/Activity.kt:8:27 'PackageStats' is deprecated. Deprecated in Java