diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23d75046d9..c1d49deb59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,7 +67,7 @@ jobs: run: bash scripts/run-version-check.sh - name: Build Project - run: ./gradlew --refresh-dependencies clean assemble spotlessCheck + run: ./gradlew --refresh-dependencies clean assemble spotlessCheck scalafixMain scalafixTest - name: Run Tests run: ./gradlew test reportScoverage checkScoverage diff --git a/.scalafix.conf b/.scalafix.conf new file mode 100644 index 0000000000..4df9c05bec --- /dev/null +++ b/.scalafix.conf @@ -0,0 +1,94 @@ +rules = [ + DisableSyntax, + LeakingImplicitClassVal, + # from scalafix-scapegoat + ArrayEquals, + ArraysInFormat, + ArraysToString, + AvoidSizeEqualsZero, + AvoidSizeNotEqualsZero, + AvoidToMinusOne, + BigDecimalDoubleConstructor, + BigDecimalScaleWithoutRoundingMode, + BooleanParameter, + BoundedByFinalType, + BrokenOddness, + CatchException, + CatchExceptionImmediatelyRethrown, + CatchFatal, + CatchNpe, + CatchThrowable, + ClassNames, + CollectionIndexOnNonIndexedSeq, + CollectionNamingConfusion, + CollectionNegativeIndex, + CollectionPromotionToAny, + ComparingFloatingPointTypes, + ComparisonToEmptyList, + ComparisonToEmptySet, + ComparisonWithSelf, + ConstantIf, + DivideByOne, + DoubleNegation, + DuplicateImport, + DuplicateMapKey, + DuplicateSetValue, + EitherGet, + EmptyCaseClass, + EmptyFor, + EmptyIfBlock, + #EmptyInterpolatedString, + EmptyMethod, + EmptySynchronizedBlock, + EmptyTryBlock, + EmptyWhileBlock, + ExistsSimplifiableToContains, + FilterDotHead, + FilterDotHeadOption, + FilterDotIsEmpty, + FilterDotSize, + FilterOptionAndGet, + FinalModifierOnCaseClass, + FinalizerWithoutSuper, + FindAndNotEqualsNoneReplaceWithExists, + FindDotIsDefined, + IllegalFormatString, + ImpossibleOptionSizeCondition, + #IncorrectNumberOfArgsToFormat, # with this the addition of timestamps fails + IncorrectlyNamedExceptions, + InterpolationToString, + InvalidRegexTest, + #LonelySealedTrait, + LooksLikeInterpolatedString, + MapGetAndGetOrElse, + MethodReturningAny, + NanComparison, + NullAssignment, + NullParameter, + OptionGet, + OptionSize, + RepeatedCaseBody, + RepeatedIfElseBody, + StripMarginOnRegex, + SwallowedException, + #TryGet, # was disabled in scapegoat + UnnecessaryConversion, + UnreachableCatch, + UnsafeContains, + UnsafeStringContains, + UnsafeTraversableMethods, + UnusedMethodParameter, + VarCouldBeVal, + VariableShadowing, + WhileTrue +] + +DisableSyntax { + noWhileLoops = true + #noIsInstanceOf = true # FIXME can be enabled once pureconfig is in use + #noAsInstanceOf = true # FIXME same + noXml = true + noFinalize = true + noValPatterns = true +} + diff --git a/build.gradle b/build.gradle index fba4534871..c15d924daa 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,7 @@ plugins { id 'signing' id 'maven-publish' // publish to a maven repo (local or mvn central, has to be defined) id 'com.diffplug.spotless' version '7.2.1' // code format + id 'io.github.cosmicsilence.scalafix' version '0.2.4' id "com.github.ben-manes.versions" version '0.52.0' id "de.undercouch.download" version "5.6.0" // downloads plugin id "kr.motd.sphinx" version "2.10.1" // documentation generation @@ -145,9 +146,8 @@ dependencies { exclude group: 'junit', module: 'junit' } - /* Scala compiler plugin for static code analysis */ - implementation "com.sksamuel.scapegoat:scalac-scapegoat-plugin_${scalaBinaryVersion}:${scapegoatVersion}" - scalaCompilerPlugin "com.sksamuel.scapegoat:scalac-scapegoat-plugin_${scalaBinaryVersion}:${scapegoatVersion}" + /* Scalafix plugin for static code analysis */ + scalafix "io.github.dedis:scapegoat-scalafix_${scalaVersion}:1.1.4" /* Kafka */ implementation "org.apache.kafka:kafka-clients:${confluentKafkaVersion}-ccs" @@ -201,11 +201,7 @@ tasks.shadowJar { // using compileScala instead of tasks.withType(ScalaCompile) prevents applying scapegoat to scala test classes // see https://docs.gradle.org/current/userguide/scala_plugin.html#sec:configure_scala_classpath for details tasks.withType(ScalaCompile).configureEach { - scalaCompileOptions.additionalParameters = scala3compilerOptions + [ - "-Xplugin:" + configurations.scalaCompilerPlugin.asPath, - "-P:scapegoat:dataDir:" + project.layout.buildDirectory.get().asFile.absolutePath + "/reports/scapegoat/src/", - "-P:scapegoat:disabledInspections:TryGet" - ] + scalaCompileOptions.additionalParameters = scala3compilerOptions scalaCompileOptions.forkOptions.jvmArgs = [ '-Xss2m', '-XX:-UseGCOverheadLimit' @@ -214,11 +210,7 @@ tasks.withType(ScalaCompile).configureEach { // separate scapegoat report for test classes compileTestScala { - scalaCompileOptions.additionalParameters = scala3compilerOptions + [ - "-Xplugin:" + configurations.scalaCompilerPlugin.asPath, - "-P:scapegoat:dataDir:" + project.layout.buildDirectory.get().asFile.absolutePath + "/reports/scapegoat/testsrc/", - "-P:scapegoat:disabledInspections:TryGet" - ] + scalaCompileOptions.additionalParameters = scala3compilerOptions } tasks.register("printVersion") { diff --git a/docs/readthedocs/developersguide.md b/docs/readthedocs/developersguide.md index 89289cc112..ddbea88b0a 100644 --- a/docs/readthedocs/developersguide.md +++ b/docs/readthedocs/developersguide.md @@ -30,6 +30,7 @@ In short, mergeable PRs have to meet our standards in several areas: - Automated checks - GitHub Actions run succeeds, i.e. - The code needs to be properly formatted (`gradle spotlessApply`) + - The code needs to adhere to some standards regarding import order etc. (`gradle scalafix`) - The code needs to compile - All tests need to succeed - [SonarQube](https://simona.ie3.e-technik.tu-dortmund.de/sonar/dashboard?id=edu.ie3%3Asimona) run succeeds, i.e. diff --git a/docs/readthedocs/images/usersguide/intellij-gradle-settings.png b/docs/readthedocs/images/usersguide/intellij-gradle-settings.png new file mode 100644 index 0000000000..d461a36f11 Binary files /dev/null and b/docs/readthedocs/images/usersguide/intellij-gradle-settings.png differ diff --git a/gradle/scripts/scalafix.gradle b/gradle/scripts/scalafix.gradle new file mode 100644 index 0000000000..04ae0140da --- /dev/null +++ b/gradle/scripts/scalafix.gradle @@ -0,0 +1,9 @@ +scalafix { + configFile = file(".scalafix.conf") + includes = ["/edu/**/*.scala"] + excludes = ["**/generated/**"] + ignoreSourceSets = ["scoverage"] + semanticdb { + autoConfigure = false + } +} \ No newline at end of file diff --git a/src/main/scala/edu/ie3/simona/service/weather/WeatherSourceWrapper.scala b/src/main/scala/edu/ie3/simona/service/weather/WeatherSourceWrapper.scala index a244b5d1ca..b74f9867e7 100644 --- a/src/main/scala/edu/ie3/simona/service/weather/WeatherSourceWrapper.scala +++ b/src/main/scala/edu/ie3/simona/service/weather/WeatherSourceWrapper.scala @@ -139,7 +139,7 @@ private[weather] final case class WeatherSourceWrapper private ( case EMPTY_WEATHER_DATA.diffIrr => // Some data sets do not provide diffuse irradiance, so we do not // warn here - logger.debug("Diffuse solar irradiance not available at $point.") + logger.debug(s"Diffuse solar irradiance not available at $point.") (averagedWeather.diffIrr, 0d) case nonEmptyDiffIrr => (averagedWeather.diffIrr + nonEmptyDiffIrr * weight, weight) diff --git a/src/main/scala/edu/ie3/util/scala/io/ScalaReflectionSerde.scala b/src/main/scala/edu/ie3/util/scala/io/ScalaReflectionSerde.scala index d2beeeba3a..8cab1e207f 100644 --- a/src/main/scala/edu/ie3/util/scala/io/ScalaReflectionSerde.scala +++ b/src/main/scala/edu/ie3/util/scala/io/ScalaReflectionSerde.scala @@ -60,7 +60,7 @@ object ScalaReflectionSerde { Option(maybeData) .filter(_.nonEmpty) .map(data => fromRecord.from(inner.deserialize(topic, data))) - .getOrElse(null.asInstanceOf[T]) + .orNull override def close(): Unit = inner.close() }