diff --git a/build.gradle b/build.gradle index 8be069aa8854291766703fe104d0fe764bf22d68..69670a47bb569251891f60c318f0d57d4481b279 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ buildscript { } plugins { - id "com.github.spotbugs-base" version "5.0.4" apply false + id "com.github.spotbugs-base" version "5.0.10" apply false id "com.github.hierynomus.license-report" version "0.15.0" } @@ -73,7 +73,7 @@ allprojects { } spotbugs { - toolVersion = '3.1.12' + toolVersion = '4.7.1' showProgress = true effort = 'max' diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml index e3eac59b513ab89e5454c1f12b06534b034d24ce..d98aad57d0eee555233c83bc87fc101de8e9fe95 100644 --- a/config/checkstyle/suppressions.xml +++ b/config/checkstyle/suppressions.xml @@ -17,4 +17,6 @@ <suppress checks="ParameterNumber" files="CPMController.java" /> + <suppress checks="VisibilityModifier" files=".*Dto\.java$" /> + </suppressions> diff --git a/config/spotbugs/suppressions.xml b/config/spotbugs/suppressions.xml index 8ea457515d9e99bd7a709cf390e9fad6dbb59e08..4b41cb2f36e2f6d9ccb708eb99cc8b9337d54ea6 100644 --- a/config/spotbugs/suppressions.xml +++ b/config/spotbugs/suppressions.xml @@ -1,10 +1,19 @@ <?xml version="1.0" encoding="UTF-8" ?> <FindBugsFilter - xmlns="http://findbugs.sourceforge.net/filter/3.0.0" + xmlns="https://github.com/spotbugs/filter/3.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://findbugs.sourceforge.net/filter/3.0.0 https://raw.githubusercontent.com/findbugsproject/findbugs/master/findbugs/etc/findbugsfilter.xsd"> + xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd"> <Match> <!-- Disable SpotBugs for ANTLR generated classes --> <Class name="~nl\.tudelft\.ewi\.auta\.checker\.grammar.*" /> </Match> + + <Match> + <!-- "may expose internal representation by storing an externally mutable object" is more or + less impossible to fix without a rewrite --> + <Or> + <Bug pattern="EI_EXPOSE_REP" /> + <Bug pattern="EI_EXPOSE_REP2" /> + </Or> + </Match> </FindBugsFilter> diff --git a/worker/src/main/java/nl/tudelft/ewi/auta/checker/asm/AttAsmReader.java b/worker/src/main/java/nl/tudelft/ewi/auta/checker/asm/AttAsmReader.java index af4875bc0bc27a9f08d1e9e5cc2cc88922d6a39c..12ea5c735297efbaa696e803fbe614ebc5a0956c 100644 --- a/worker/src/main/java/nl/tudelft/ewi/auta/checker/asm/AttAsmReader.java +++ b/worker/src/main/java/nl/tudelft/ewi/auta/checker/asm/AttAsmReader.java @@ -10,6 +10,7 @@ import java.util.Set; import java.util.Stack; import java.util.regex.Pattern; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import nl.tudelft.ewi.auta.common.annotation.Unmodifiable; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.misc.Interval; @@ -593,6 +594,7 @@ public class AttAsmReader { * is a target for an unconditional jump or a return instruction */ @Contract(pure = true) + @SuppressFBWarnings(value = {"ES_COMPARING_STRINGS_WITH_EQ"}, justification = "see comment") public boolean isConditional() { // noinspection StringEquality - identity is ensured by constructor return this.falsyTarget != this.truthyTarget; diff --git a/worker/src/main/java/nl/tudelft/ewi/auta/checker/python/PyLint.java b/worker/src/main/java/nl/tudelft/ewi/auta/checker/python/PyLint.java index c916c1530dfa1c5d58b2a4c3f38132f6d73797a4..f187578593dffb0c7e94344e8388267e74043c44 100644 --- a/worker/src/main/java/nl/tudelft/ewi/auta/checker/python/PyLint.java +++ b/worker/src/main/java/nl/tudelft/ewi/auta/checker/python/PyLint.java @@ -12,16 +12,12 @@ import nl.tudelft.ewi.auta.common.model.metric.MetricName; import nl.tudelft.ewi.auta.common.model.metric.PyLintResultMetric; import nl.tudelft.ewi.auta.worker.Job; import nl.tudelft.ewi.auta.worker.config.WorkerSettings; -import nl.tudelft.ewi.auta.worker.tool.python.Python; import nl.tudelft.ewi.auta.worker.tool.python.PythonProcessException; -import nl.tudelft.ewi.auta.worker.files.Unpacker; -import org.apache.commons.compress.utils.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.file.Files; -import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -34,50 +30,22 @@ public class PyLint extends JobAnalyzer { private static final Logger logger = LoggerFactory.getLogger(PyLint.class); - /** - * The path to the radon files. - */ - private final Path pyLintPath; - /** * The worker settings. */ private final WorkerSettings settings; - /** - * The Python instance PyLint will run in. - */ - private final Python python; - /** * Constructor for the Radon analyzer. * * @param workerSettings the worker settings - * @param unzipper the job unzipper - * @param python the python instance to run PyLint in * * @throws IOException if a file is not found */ public PyLint( - final WorkerSettings workerSettings, - final Unpacker unzipper, - final Python python - ) throws IOException { + final WorkerSettings workerSettings + ) { this.settings = workerSettings; - this.python = python; - final var path = Files.createTempDirectory(this.settings.getTemp(), "pylint"); - final var zipPath = Files.createTempFile(this.settings.getTemp(), "stream", ".zip"); - - try (var in = PyLint.class.getResourceAsStream( - "/nl/tudelft/ewi/auta/worker/checker/python/pylint.zip"); - var out = Files.newOutputStream(zipPath)) { - IOUtils.copy(in, out); - } - - // Unzip PyLint for usage - unzipper.unpack(zipPath, path); - this.pyLintPath = path; - Files.delete(zipPath); } @@ -100,11 +68,11 @@ public class PyLint extends JobAnalyzer { final var fileNames = this.getFileNames(victim); // For every module - for (var i = 0; i < fileNames.size(); i++) { + for (final var fileName : fileNames) { // Prepare the command to run PyLint as tool in a separate process - final var process = this.python.prepare( - this.pyLintPath, "pylint", "--output-format=json", fileNames.get(i) - ); + final var process = + new ProcessBuilder("pylint", "--output-format=json", fileName); + process.redirectError(ProcessBuilder.Redirect.INHERIT); // Set the right environment variables and the working directory final var tempOutputFile = Files.createTempFile( @@ -121,7 +89,7 @@ public class PyLint extends JobAnalyzer { logger.warn("The Python process for PyLint timed out during execution"); throw new PythonProcessException( "The Python process for PyLint timed out during execution, " - + "the module that was provided might have been to large" + + "the module that was provided might have been to large" ); } @@ -139,12 +107,12 @@ public class PyLint extends JobAnalyzer { } } - var moduleName = fileNames.get(i).replace("\\", "/"); + var moduleName = fileName.replace("\\", "/"); moduleName = moduleName.substring(moduleName.lastIndexOf("/") + 1); // Create the module entity final var moduleEntity = new Entity(victim.getProject(), moduleName, - false, EntityLevel.MODULE); + false, EntityLevel.MODULE); final var moduleEntityWithMetrics = this.createModuleEntityStructure( moduleEntity, pyLintResults ); diff --git a/worker/src/main/java/nl/tudelft/ewi/auta/worker/tool/python/Python.java b/worker/src/main/java/nl/tudelft/ewi/auta/worker/tool/python/Python.java index 7a7da1be62ab656dd248d3ec5e6eb3472280e6cd..e233c369d62d7d604b2b76081345fa25d360154a 100644 --- a/worker/src/main/java/nl/tudelft/ewi/auta/worker/tool/python/Python.java +++ b/worker/src/main/java/nl/tudelft/ewi/auta/worker/tool/python/Python.java @@ -4,6 +4,7 @@ import org.jetbrains.annotations.Contract; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; @@ -33,27 +34,35 @@ public class Python { /** * Prepares a python process for executing. * - * @param toolDir the directory the tool is installed in - * @param workingDir the working directory for the tool + * @param toolDir the directory the tool is installed in or null to use the global site packages + * @param workingDir the working directory for the tool or null to inherit the current WD * @param cmd any command line arguments (excluding the executable) * * @return a process builder ready to execute the command */ - public ProcessBuilder prepare(final Path toolDir, final Path workingDir, final String... cmd) { + public ProcessBuilder prepare( + final @Nullable Path toolDir, + final @Nullable Path workingDir, + final String... cmd + ) { final var fcmd = new ArrayList<String>(); fcmd.add(this.info.getPath().toAbsolutePath().toString()); fcmd.addAll(Arrays.asList(cmd)); final var process = new ProcessBuilder(fcmd); - process.environment().put("PYTHONPATH", toolDir.toAbsolutePath().toString()); - process.directory(workingDir.toFile()); + if (toolDir != null) { + process.environment().put("PYTHONPATH", toolDir.toAbsolutePath().toString()); + } + if (workingDir != null) { + process.directory(workingDir.toFile()); + } process.redirectError(ProcessBuilder.Redirect.INHERIT); if (logger.isTraceEnabled()) { logger.trace("Starting `{}` as tool in {} working in or on {}", String.join(" ", fcmd), - toolDir.toAbsolutePath(), - workingDir.toAbsolutePath() + toolDir, + workingDir ); } @@ -65,20 +74,20 @@ public class Python { * * The working directory is set to the tool directory. * - * @param toolDir the directory the tool is installed in + * @param toolDir the directory the tool is installed in or null to use the global site-packages * @param cmd any command line arguments (excluding the executable) * * @return a process builder ready to execute the command */ - public ProcessBuilder prepare(final Path toolDir, final String... cmd) { + public ProcessBuilder prepare(final @Nullable Path toolDir, final String... cmd) { return this.prepare(toolDir, toolDir, cmd); } /** * Executes a python process. * - * @param toolDir the directory the tool is installed in - * @param workingDir the working directory for the tool + * @param toolDir the directory the tool is installed in or null to use the global site-packages + * @param workingDir the working directory for the tool or null to inherit the current WD * @param cmd any command line arguments (excluding the executable) * * @return the running executable @@ -86,7 +95,7 @@ public class Python { * @throws IOException if an I/O error occurs while communicating with the process */ public Process run( - final Path toolDir, final Path workingDir, final String... cmd + final @Nullable Path toolDir, final @Nullable Path workingDir, final String... cmd ) throws IOException { return this.prepare(toolDir, workingDir, cmd).start(); } @@ -96,14 +105,14 @@ public class Python { * * The working directory is set to the tool directory. * - * @param toolDir the directory the tool is installed in + * @param toolDir the directory the tool is installed in or null to use the global site-packages * @param cmd any command line arguments (excluding the executable) * * @return the running executable * * @throws IOException if an I/O error occurs while communicating with the process */ - public Process run(final Path toolDir, final String... cmd) throws IOException { + public Process run(final @Nullable Path toolDir, final String... cmd) throws IOException { return this.run(toolDir, toolDir, cmd); } } diff --git a/worker/src/main/resources/nl/tudelft/ewi/auta/worker/checker/python/pylint.zip b/worker/src/main/resources/nl/tudelft/ewi/auta/worker/checker/python/pylint.zip index 99394b9679362512aec2fd5de436a027b6bfdf52..8a6a8cbdf9f88d5d536ca841735e60da4228e71e 100644 Binary files a/worker/src/main/resources/nl/tudelft/ewi/auta/worker/checker/python/pylint.zip and b/worker/src/main/resources/nl/tudelft/ewi/auta/worker/checker/python/pylint.zip differ diff --git a/worker/src/test/java/nl/tudelft/ewi/auta/checker/python/PyLintTest.java b/worker/src/test/java/nl/tudelft/ewi/auta/checker/python/PyLintTest.java index 561bccbe6cb759fe5153ccd92a88f7ece2f663cd..18571fcdb84300b113009c3a38a5282aa3ee7a33 100644 --- a/worker/src/test/java/nl/tudelft/ewi/auta/checker/python/PyLintTest.java +++ b/worker/src/test/java/nl/tudelft/ewi/auta/checker/python/PyLintTest.java @@ -10,8 +10,6 @@ import nl.tudelft.ewi.auta.worker.config.WorkerSettings; import nl.tudelft.ewi.auta.worker.files.UnicodePathEncoder; import nl.tudelft.ewi.auta.worker.files.Unpacker; import nl.tudelft.ewi.auta.worker.jobconfig.JobConfig; -import nl.tudelft.ewi.auta.worker.tool.python.Python; -import nl.tudelft.ewi.auta.worker.tool.python.PythonDetector; import org.apache.commons.compress.utils.IOUtils; import org.junit.jupiter.api.Test; @@ -36,8 +34,6 @@ public class PyLintTest { final var tempDir = Paths.get(System.getProperty("java.io.tmpdir")); final var zipPath = Files.createTempFile(tempDir, "pyFilesForTesting", ".zip"); - final var python = new Python(new PythonDetector().findPython()); - try (var in = PyLintTest.class .getResourceAsStream("/lizard.zip"); var out = Files.newOutputStream(zipPath)) { @@ -64,8 +60,7 @@ public class PyLintTest { "name", Collections.singletonList("Otto"), "api-token", List.of("TOKEN!!!"))); - final var pyLint = new PyLint(settings, - new Unpacker(settings, new UnicodePathEncoder()), python); + final var pyLint = new PyLint(settings); pyLint.analyze(job, new HashMap<>()); assertThat(job.getProject().getAllChildren()).hasSizeGreaterThan(100); } @@ -101,9 +96,7 @@ public class PyLintTest { "name", Collections.singletonList("Otto"), "api-token", List.of("TOKEN!!!"))); - final var python = new Python(new PythonDetector().findPython()); - final var pyLint = new PyLint(settings, new Unpacker(settings, new UnicodePathEncoder()), - python); + final var pyLint = new PyLint(settings); pyLint.analyze(job, Collections.emptyMap()); assertThat(job.getProject().getLevel()).isEqualTo(EntityLevel.PROJECT);