diff --git a/.circleci/config.yml b/.circleci/config.yml index 77a036b..41758bd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,8 +1,8 @@ version: 2.1 orbs: - codacy: codacy/base@12.2.0 - codacy_plugins_test: codacy/plugins-test@1.1.1 + codacy: codacy/base@13.1.1 + codacy_plugins_test: codacy/plugins-test@2.1.2 workflows: version: 2 @@ -41,7 +41,7 @@ workflows: branches: only: master context: CodacyAWS - no_output_timeout: "30m" + no_output_timeout: "60m" cache_prefix: sbt-cache-15092020 requires: - publish_docker_local diff --git a/build.sbt b/build.sbt index 8737a78..50fd7f7 100644 --- a/build.sbt +++ b/build.sbt @@ -104,7 +104,7 @@ val dockerUser = "docker" daemonUser in Docker := dockerUser -dockerBaseImage := "amazoncorretto:8-alpine3.14-jre" +dockerBaseImage := "amazoncorretto:8-alpine3.24-jre" dockerCommands := dockerCommands.value.flatMap { case cmd @ Cmd("ADD", _) => diff --git a/core/build.sbt b/core/build.sbt index bfb7a34..98b2c31 100644 --- a/core/build.sbt +++ b/core/build.sbt @@ -5,7 +5,7 @@ name := "codacy-patterns-core" //core dependencies val scalaLib = "org.scala-lang" % "scala-library" % scalaV val scalaCompiler = "org.scala-lang" % "scala-compiler" % scalaV -val scalameta = "org.scalameta" %% "scalameta" % "4.0.0" withSources () +val scalameta = "org.scalameta" %% "scalameta" % "4.17.2" withSources () val scalametaContrib = "org.scalameta" %% "contrib" % "4.0.0" withSources () scalaVersion := scalaV diff --git a/core/project/plugins.sbt b/core/project/plugins.sbt index 78cada5..6ee600c 100644 --- a/core/project/plugins.sbt +++ b/core/project/plugins.sbt @@ -1,3 +1,3 @@ -addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.3") +addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.12.2") -addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.2") \ No newline at end of file +addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1") \ No newline at end of file diff --git a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_DuplicatedCase.scala b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_DuplicatedCase.scala index 9f3b355..c307b34 100644 --- a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_DuplicatedCase.scala +++ b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_DuplicatedCase.scala @@ -6,7 +6,7 @@ import scala.meta._ case object Custom_Scala_DuplicatedCase extends Pattern { override def apply(tree: Tree): Iterable[Result] = { - def checkDup(cases: List[Case]): Iterable[Result] = { + def checkDup(cases: Seq[Case]): Iterable[Result] = { cases.groupBy(_.pat.structure).collect { case (x, ys) if ys.lengthCompare(1) > 0 => Result(message(ys.head), ys.head) @@ -14,9 +14,15 @@ case object Custom_Scala_DuplicatedCase extends Pattern { } tree.collect { - case q"$_ match { ..case $cases }" => checkDup(cases) - case q"try $_ catch { ..case $cases } finally $_" => checkDup(cases) - case q"{ ..case $cases }" => checkDup(cases) + case expr: Term.Match => + checkDup(expr.cases) + case expr: Term.Try => + checkDup(expr.catchp) + case block: Term.Block => + block.stats.flatMap { + case cases: Term.CasesBlock => checkDup(cases.cases) + case _ => Iterable.empty + } }.flatten } diff --git a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_EnforceMinimumVisibility.scala b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_EnforceMinimumVisibility.scala index f733ba2..5a1a3c8 100644 --- a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_EnforceMinimumVisibility.scala +++ b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_EnforceMinimumVisibility.scala @@ -14,7 +14,8 @@ case object Custom_Scala_EnforceMinimumVisibility extends Pattern { private[this] def isInner(tree: Defn) = tree match { case t @ (_: Defn.Trait | _: Defn.Class | _: Defn.Object) => t.parent.exists { - case q"package $_ { ..$_ }" => false + case _: Pkg => false + case _: Pkg.Object => false case _ => true } case _ => true diff --git a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_FieldNamesChecker.scala b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_FieldNamesChecker.scala index bdea95e..1663e15 100644 --- a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_FieldNamesChecker.scala +++ b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_FieldNamesChecker.scala @@ -13,24 +13,28 @@ class Custom_Scala_FieldNamesChecker(configuration: Custom_Scala_FieldNamesCheck tree .collect { //val definitions - case t @ q"..$_ val ..$patsnel: $tpe = $expr" - if configuration.includeEnums || !isEnumValDefRegexOrDecl(t.parent, Option(expr), tpe) => - conflictingNames(patsnel) - //val declarations - case t @ q"..$_ val ..$pnamesnel: $tpe" - if configuration.includeEnums || !isEnumValDefRegexOrDecl(t.parent, tpe = Option(tpe)) => - conflictingNames(pnamesnel) + case defn: Defn.Val => + if (configuration.includeEnums || !isEnumValDefRegexOrDecl(defn.parent, Some(defn.rhs), defn.decltpe)) + conflictingNames(defn.pats) + else + Iterable.empty //var definitions - case t @ q"..$_ var ..$patsnel: $tpe = $expr" - if configuration.includeEnums || !isEnumValDefRegexOrDecl(t.parent, expr, tpe) => - conflictingNames(patsnel) - //var declarations - case t @ q"..$_ var ..$pnamesnel: $tpe" - if configuration.includeEnums || !isEnumValDefRegexOrDecl(t.parent, tpe = Option(tpe)) => - conflictingNames(pnamesnel) - //parameter values - case t @ param"..$mods $paramname: $atpeopt = $expropt" if isConflictingName(paramname) => - List(paramname) + case defn: Defn.Var => + if (configuration.includeEnums || !isEnumValDefRegexOrDecl(defn.parent, defn.rhs, defn.decltpe)) + conflictingNames(defn.pats) + else + Iterable.empty + //class parameters + case defn: Defn.Class => + defn.ctor.paramss.flatMap { params => + params.collect { + case param if isConflictingName(param.name) => + param.name + } + } + //parameter values (method parameters) + case param: Term.Param if isConflictingName(param.name) => + List(param.name) } .flatten .map { tree => @@ -39,7 +43,7 @@ class Custom_Scala_FieldNamesChecker(configuration: Custom_Scala_FieldNamesCheck .toSet } - private[this] def isEnumValDefRegexOrDecl(parent: Option[Tree], expr: Option[Tree] = None, tpe: Option[Type]) = { + private[this] def isEnumValDefRegexOrDecl(parent: Option[Tree], expr: Option[Tree], tpe: Option[Type]) = { val extendsEnumeration: Boolean = parent .collect { case template"{ ..$stats } with ..${ctorcalls: Seq[Init]} { $param => ..$stats2 }" => diff --git a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_GetInMethodName.scala b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_GetInMethodName.scala index 68a2acd..69720ae 100644 --- a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_GetInMethodName.scala +++ b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_GetInMethodName.scala @@ -8,9 +8,9 @@ case object Custom_Scala_GetInMethodName extends Pattern { override def apply(tree: Tree) = { tree.collect { - case t @ q"..$mods def ${name: Term.Name}[..$tparams](...$paramss): $tpeopt = $expr" - if name.value != "get" && name.value.startsWith("get") => - Result(message(t), t) + case defn: Defn.Def + if defn.name.value != "get" && defn.name.value.startsWith("get") => + Result(message(defn), defn) } } diff --git a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_GroupImports.scala b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_GroupImports.scala index 48507e8..a8c1c45 100644 --- a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_GroupImports.scala +++ b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_GroupImports.scala @@ -10,25 +10,24 @@ case object Custom_Scala_GroupImports extends Pattern { tree .collect { case t: Template => - t.stats + imports(t.stats) case q"package $ref { ..$stats }" => - stats + imports(stats) case t: Term.Block => - t.stats + imports(t.stats) } - .map(imports) .flatMap(duplicatedImporters) .map { tree => Result(message(tree), tree) } } - private[this] def duplicatedImporters(imports: Seq[Tree]) = { + private[this] def duplicatedImporters(imports: Seq[Tree]): Seq[Tree] = { val tupled = imports.collect { case t @ importer"$ref.{..$importeesnel}" => (ref, t) } tupled .groupBy { case (ref, _) => ref.toString } - .collect { + .flatMap { case (_, importers) if importers.length > 1 => importers .map { case (_, importer) => importer } @@ -36,11 +35,12 @@ case object Custom_Scala_GroupImports extends Pattern { (importer.pos.startLine, importer.pos.startColumn) } .take(1) + case _ => Seq.empty } - .flatten + .toSeq } - private[this] def imports(body: Seq[Tree]) = { + private[this] def imports(body: Seq[Tree]): Seq[Tree] = { body.collect { case t @ q"import ..$importersnel" => importersnel }.flatten } diff --git a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_IndexOfPositive.scala b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_IndexOfPositive.scala index 54697ba..97d11ff 100644 --- a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_IndexOfPositive.scala +++ b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_IndexOfPositive.scala @@ -8,9 +8,9 @@ case object Custom_Scala_IndexOfPositive extends Pattern { override def apply(tree: Tree): List[Result] = { tree.collect { - case t @ q"$_.indexOf( ..${args: Seq[Term]}) > ${lit: Lit}" if isNaturalNumber(lit) => + case t @ q"$_.indexOf( ..${args: Term.ArgClause}) > ${lit: Lit}" if isNaturalNumber(lit) => Result(message(t), t) - case t @ q"${lit: Lit} < $_.indexOf( ..${args: Seq[Term]})" if isNaturalNumber(lit) => + case t @ q"${lit: Lit} < $_.indexOf( ..${args: Term.ArgClause})" if isNaturalNumber(lit) => Result(message(t), t) } } diff --git a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_IndexOfStartPosition.scala b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_IndexOfStartPosition.scala index 9cdd57e..894e9ed 100644 --- a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_IndexOfStartPosition.scala +++ b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_IndexOfStartPosition.scala @@ -8,7 +8,7 @@ case object Custom_Scala_IndexOfStartPosition extends Pattern { override def apply(tree: Tree) = { tree.collect { - case t @ q"$expr.indexOf( ..${args: Seq[Term]} )" if args.length == 1 => + case t @ q"$expr.indexOf( ..${args: Term.ArgClause} )" if args.values.length == 1 => Result(message(t), t) } } diff --git a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_LowerCasePatternMatching.scala b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_LowerCasePatternMatching.scala index aa5f8aa..172bf29 100644 --- a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_LowerCasePatternMatching.scala +++ b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_LowerCasePatternMatching.scala @@ -8,36 +8,35 @@ case object Custom_Scala_LowerCasePatternMatching extends Pattern { override def apply(tree: Tree) = { tree.collect { - case t @ p"case ${pat: Pat.Var} => $expr" if isOffender(t, pat) => + case t @ Case(pat: Pat.Var, _, _) if isOffender(t, pat) => Result(message(t), t) } } private[this] def isOffender(tree: Tree, pat: Pat) = { - !hasDeclaredType(pat) && !isCaseFromCollect(tree) && !isCaseFromPartialFunction(tree) && isLowerCase(pat) + !hasDeclaredType(pat) && + !isCaseFromCollect(tree) && + !isCaseFromPartialFunction(tree) && + !isCaseFromCatch(tree) && + isLowerCase(pat) } - private[this] def isPartialApplication(tpe: Term.Name) = tpe.toString match { - case "orElse" | "andThen" => true - case _ => false - } private[this] def isCaseFromPartialFunction(tree: Tree): Boolean = { tree.parent.flatMap(_.parent).exists { - case q"..$mods val ..$patsnel: $tpeopt = { ..case $casesnel }" => true - case q"$expr $tpe { ..case $casesnel}" if isPartialApplication(tpe) => true - case q"$expr.$tpe { ..case $casesnel }" if isPartialApplication(tpe) => true - case q"$expr $tpe[..$tpesnel] { ..case $casesnel }" if isPartialApplication(tpe) => true - case q"$expr.$tpe[..$tpesnel] { ..case $casesnel }" if isPartialApplication(tpe) => true + case _: Defn.Val => true + case block: Term.Block => + block.stats.exists { + case _: Term.CasesBlock => true + case _ => false + } case _ => false } } private[this] def isCaseFromCollect(tree: Tree): Boolean = { tree.parent.flatMap(_.parent).exists { - case q"$_.collect(..$_)" => true - case q"$_.collect[..$_](..$_)" => true - case q"$_ collect $_" => true + case Term.Apply(Term.Select(_, Term.Name("collect")), _) => true case _ => false } } @@ -55,5 +54,12 @@ case object Custom_Scala_LowerCasePatternMatching extends Pattern { pat.collect { case t @ p"$expr: $tpe" => t }.nonEmpty } + private[this] def isCaseFromCatch(tree: Tree): Boolean = { + tree.parent.exists { + case t: Term.Try => t.catchp.exists(_ == tree) + case _ => false + } +} + private[this] def message(tree: Tree) = Message("Lower case pattern matching.") } diff --git a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_NonFatal.scala b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_NonFatal.scala index 05ba62c..1625d73 100644 --- a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_NonFatal.scala +++ b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_NonFatal.scala @@ -9,8 +9,8 @@ case object Custom_Scala_NonFatal extends Pattern { override def apply(tree: Tree) = { tree.collect { - case t @ q"try $expr catch { ..case $cases } finally $expropt" => - cases.collect { + case t: Term.Try => + t.catchp.collect { case cs if isNonFatalCatch(cs) => Result(message(cs), cs) } diff --git a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_ProhibitObjectName.scala b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_ProhibitObjectName.scala index c07f59f..c26be1e 100644 --- a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_ProhibitObjectName.scala +++ b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_ProhibitObjectName.scala @@ -10,19 +10,17 @@ case object Custom_Scala_ProhibitObjectName extends Pattern { tree.collect { case t: Defn.Class => defsWithNameIn(t, t.name.toString) - case t @ q"..$mods trait $tname[..$tparams] extends $template" => - defsWithNameIn(t, tname.toString) - case t @ q"..$mods object $name extends $template" => - defsWithNameIn(t, name.toString) + case t: Defn.Trait => + defsWithNameIn(t, t.name.toString) + case t: Defn.Object => + defsWithNameIn(t, t.name.toString) }.flatten } private[this] def defsWithNameIn(tree: Tree, cName: String) = { tree.collect { - case t @ q"..$mods def $name[..$tparams](...$paramss): $tpeopt = $expr" if name.toString.endsWith(cName) => - Result(message(t), t) - case t @ q"..$mods def $name[..$tparams](...$paramss): $tpe" if name.toString.endsWith(cName) => + case t: Defn.Def if t.name.toString.endsWith(cName) => Result(message(t), t) } } diff --git a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_UnusedPrivateVariables.scala b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_UnusedPrivateVariables.scala index 1078d93..ac950f3 100644 --- a/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_UnusedPrivateVariables.scala +++ b/patterns-base/src/main/scala/codacy/patterns/Custom_Scala_UnusedPrivateVariables.scala @@ -53,21 +53,21 @@ case object Custom_Scala_UnusedPrivateVariables extends Pattern { } private[this] def hasParamOrValDefWithName(tree: Tree, name: Term.Name): Boolean = { - //does the tree have a parameter with the given name? val paramWithName = tree.collect { - case param"..$mods ${paramname: Term.Name}: $atpeopt = $expropt" if paramname != name => - paramname + case param: Term.Param if param.name != name => + param.name } - //does the tree have a valDef with the given name? + val valDefWithName = tree.collect { - case q"..${mods: Seq[Mod]} val ..${patsnel: Seq[Pat]}: $tpeopt = $expr" => - patsnel.flatMap(_.collect { case n: Term.Name if n != name => n }) + case defn: Defn.Val => + defn.pats.flatMap(_.collect { case n: Term.Name if n != name => n }) + case defn: Defn.Var => + defn.pats.flatMap(_.collect { case n: Term.Name if n != name => n }) }.flatten - //does the tree have a defDef with the given name? val defDefWithName = tree.collect { - case q"..$mods def ${name: Term.Name}[..$tparams](...$paramss): $tpeopt = $expr" => - name + case defn: Defn.Def if defn.name != name => + defn.name } (paramWithName ++ valDefWithName ++ defDefWithName).exists(_.toString() == name.toString()) @@ -89,28 +89,27 @@ case object Custom_Scala_UnusedPrivateVariables extends Pattern { tree .collect { //valDefs - case t @ q"..${mods: Seq[Mod]} val ..${patsnel: Seq[Pat]}: $tpeopt = $expr" - if isPrivate(mods) && !isImplicit(mods) => - //first parent should be the stats List[Tree], 2nd the class itself + case t: Defn.Val if isPrivate(t.mods) && !isImplicit(t.mods) => + t.parent.flatMap(_.parent).map { classDef => + (classDef, extractNames(t.pats)) + } + //varDefs + case t: Defn.Var if isPrivate(t.mods) && !isImplicit(t.mods) => t.parent.flatMap(_.parent).map { classDef => - (classDef, patsnel.flatMap(_.collect { case p"${name: Term.Name}" => name })) + (classDef, extractNames(t.pats)) } //paramVals - case t @ param"..$mods ${paramname: Term.Name}: $atpeopt = $expropt" if isPrivate(mods) && !isImplicit(mods) => - //first parent should be the constructor def, 2nd the class itself + case t: Term.Param if isPrivate(t.mods) && !isImplicit(t.mods) => t.parent.flatMap(_.parent).map { classDef => - (classDef, List(paramname)) + (classDef, List(t.name)) } } .flatten .groupBy { case (template, _) => template } - .map { - case (template, lists) => - (template, lists.flatMap { case (_, names) => names }) - } .toList .flatMap { - case (template, names) => + case (template, lists) => + val names: List[Term.Name] = lists.flatMap { case (_, ns) => ns }.asInstanceOf[List[Term.Name]] allStatsForName(template, names, tree) } } @@ -128,11 +127,11 @@ case object Custom_Scala_UnusedPrivateVariables extends Pattern { Option(classDef.ctor) ++ companionStats(classDef.name, tree) - case q"..$mods trait ${name: Type.Name}[..$tparams] extends $template" => - List(template) ++ companionStats(name, tree) + case traitDef: Defn.Trait => + traitDef.templ.stats ++ companionStats(traitDef.name, tree) - case q"..$mods object $name extends $template" => - List(template) + case objDef: Defn.Object => + objDef.templ.stats } .getOrElse(List.empty) @@ -141,10 +140,18 @@ case object Custom_Scala_UnusedPrivateVariables extends Pattern { private[this] def companionStats(name: Type.Name, tree: Tree): Option[Tree] = { tree.collect { - case q"..$mods object ${oName: Term.Name} extends ${template: Tree}" if name.toString() == oName.toString => - template + case obj: Defn.Object if name.toString() == obj.name.toString => + obj.templ }.headOption } + private[this] def extractNames(pats: Seq[Pat]): List[Term.Name] = { + pats.flatMap { + case Pat.Var(name) => List(name) + case p"(..$inPats)" => extractNames(inPats) + case _ => Nil + }.toList + } + private[this] def message(name: Term.Name) = Message(s"Unused private variable.") } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index eb51daa..a6c9c71 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -4,7 +4,7 @@ object Dependencies { val scalaV = "2.12.20" val betterFiles = "com.github.pathikrit" %% "better-files" % "3.8.0" - val codacyEngine = "com.codacy" %% "codacy-engine-scala-seed" % "5.0.2" + val codacyEngine = "com.codacy" %% "codacy-engine-scala-seed" % "6.1.4" val scalaReflect = "org.scala-lang" % "scala-reflect" % scalaV } diff --git a/src/main/scala/codacy/CodacyScalameta.scala b/src/main/scala/codacy/CodacyScalameta.scala index b8214f5..e5f3d72 100644 --- a/src/main/scala/codacy/CodacyScalameta.scala +++ b/src/main/scala/codacy/CodacyScalameta.scala @@ -100,6 +100,9 @@ object CodacyScalameta extends Tool { case Failure(error) => List(Result.FileError(sourcePath, Option(ErrorMessage(error.getMessage)))) + + case _ => + List(Result.FileError(sourcePath, Option(ErrorMessage("Unknown parsing error")))) } case _ => List.empty