Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Scala compiler plugin for static code analysis

License

NotificationsYou must be signed in to change notification settings

scapegoat-scala/scapegoat

Repository files navigation

CodecovScala Steward badge

Scapegoat is a Scala static code analyzer, which is more colloquially known as a code lint tool or linter. Scapegoat works in a similar vein to Java'sFindBugs orcheckstyle, or Scala'sScalastyle.

A static code analyzer is a tool that flags suspicious language usage in code. This can include behavior likely to lead to bugs, non-idiomatic usage of a language, or just code that doesn't conform to specified style guidelines.

What's the difference between this project and Scalastyle (or others)?

Scalastyle is a similar linting tool which focuses mostly on enforcing style/code standards. There are no problems in running multiple analysis tools on the same codebase. In fact, it could be beneficial as the total set of possible warnings is the union of the inspections of all the enabled tools. The worst case is that the same warnings might get generated by multiple tools.

Usage

Scapegoat is developed as a scala compiler plugin, which can then be used inside your build tool.

SBT

See:sbt-scapegoat for SBT integration.

Maven

Firstly you need to add scapegoat plugin as a dependency:

<dependency>    <groupId>com.sksamuel.scapegoat</groupId>    <artifactId>scalac-scapegoat-plugin_${scala.version}</artifactId>    <version>1.3.12</version></dependency>

Then configurescala-maven-plugin by addingcompilerPlugin

<plugin>    <groupId>net.alchim31.maven</groupId>    <artifactId>scala-maven-plugin</artifactId>    <configuration>        <args>            <arg>-P:scapegoat:dataDir:./target/scapegoat</arg>        </args>        <compilerPlugins>            <compilerPlugin>                <groupId>com.sksamuel.scapegoat</groupId>                <artifactId>scalac-scapegoat-plugin_${scala.binary.version}</artifactId>                <version>1.4.6</version>            </compilerPlugin>        </compilerPlugins>    </configuration></plugin>

The only required parameter isdataDir (where report will be generated):

<arg>-P:scapegoat:dataDir:./target/scapegoat</arg>

You can pass other configuration flags same way, e.g.

<arg>-P:scapegoat:disabledInspections:FinalModifierOnCaseClass</arg>

Note: You may use a separate maven profile, so that the dependency doesn't go to you runtime classpath.

Gradle with a plugin

Usegradle-scapegoat-plugin by @eugene-sy

Gradle - manually

Firstly you need to add scapegoat plugin as a dependency:

dependencies {  compile'com.sksamuel.scapegoat:scalac-scapegoat-plugin_2.12.21:1.4.6'  scalaCompilerPlugin"com.sksamuel.scapegoat:scalac-scapegoat-plugin_2.12.21:1.4.6"}

Then configurescala-compiler-plugin

configurations {  scalaCompilerPlugin}tasks.withType(ScalaCompile) {  scalaCompileOptions.additionalParameters= ["-Xplugin:"+ configurations.scalaCompilerPlugin.asPath,"-P:scapegoat:dataDir:"+ buildDir+"/scapegoat"  ]}

The only required parameter isdataDir (where report will be generated):

"-P:scapegoat:dataDir:" + buildDir + "/scapegoat",

You can pass other configuration flags adding it to theadditionalParameters list, e.g.

"-P:scapegoat:disabledInspections:FinalModifierOnCaseClass"

Full list of compiler flags

FlagParametersRequired
-P:scapegoat:dataDir:Path to reports directory for the plugin.true
-P:scapegoat:disabledInspections:Colon separated list of disabled inspections (defaults to none).false
-P:scapegoat:enabledInspections:Colon separated list of enabled inspections (defaults to all).false
-P:scapegoat:customInspectors:Colon separated list of custom inspections.false
-P:scapegoat:ignoredFiles:Colon separated list of regexes to match files to ignore.false
-P:scapegoat:verbose:Boolean flag that enables/disables verbose console messages.false
-P:scapegoat:consoleOutput:Boolean flag that enables/disables console report output.false
-P:scapegoat:reports:Colon separated list of reports to generate. Valid options arenone,xml,html,scalastyle,markdown, ,gitlab-codequality, orall.false
-P:scapegoat:overrideLevels:Overrides the built in warning levels. Should be a colon separated list ofname=level expressions.false
-P:scapegoat:sourcePrefix:Overrides source prefix if it differs fromsrc/main/scala, for ex.app/ for Play applications.false
-P:scapegoat:minimalLevel:Provides minimal level of inspection displayed in reports and in the console.false

Reports

Here is sample output from the console during the build for a project with warnings/errors:

[warning] [scapegoat] Unused method parameter - org.ensime.util.ClassIterator.scala:46[warning] [scapegoat] Unused method parameter - org.ensime.util.ClassIterator.scala:137[warning] [scapegoat] Use of var - org.ensime.util.ClassIterator.scala:22[warning] [scapegoat] Use of var - org.ensime.util.ClassIterator.scala:157[   info] [scapegoat]: Inspecting compilation unit [FileUtil.scala][warning] [scapegoat] Empty if statement - org.ensime.util.FileUtil.scala:157[warning] [scapegoat] Expression as statement - org.ensime.util.FileUtil.scala:180

And if you prefer a prettier report, here is a screen shot of the type of HTML report scapegoat generates:

screenshot

Configuration

For instructions on suppressing warnings by file, by inspection or by line seethe sbt-scapegoat README.

To suppress warnings globally for the project, usedisabledInspections oroverrideLevels flags:

-P:scapegoat:disabledInspections:FinalModifierOnCaseClass-P:scapegoat:overrideLevels:PreferSeqEmpty=ignore:AsInstanceOf=ignore

Inspections

There are currently 123 inspections for Scala 2, and 5 for Scala 3.An overview list is given, followed by a more detailed description of each inspection after the list (todo: finish rest of detailed descriptions)

NameBrief DescriptionDefault LevelScala 2Scala 3
AbstractTraitCheck if trait is abstractInfoYesYes
ArrayEqualsChecks for comparison of arrays using== which will always return falseInfoYesNo
ArraysInFormatChecks for arrays passed to String.formatErrorYesNo
ArraysToStringChecks for explicit toString calls on arraysWarningYesNo
AsInstanceOfChecks for use ofasInstanceOfWarningYesNo
AvoidOperatorOverloadChecks for mental symbolic method namesInfoYesNo
AvoidRequireUse of requireWarningYesYes
AvoidSizeEqualsZeroTraversable.size can be slow for some data structure, prefer .isEmptyWarningYesNo
AvoidSizeNotEqualsZeroTraversable.size can be slow for some data structure, prefer .nonEmptyWarningYesNo
AvoidToMinusOneChecks for loops that usex to n-1 instead ofx until nInfoYesNo
BigDecimalDoubleConstructorChecks for use ofBigDecimal(double) which can be unsafeWarningYesNo
BigDecimalScaleWithoutRoundingModesetScale() on aBigDecimal without setting the rounding mode can throw an exceptionWarningYesNo
BooleanParameterChecks for functions that have a Boolean parameterInfoYesNo
BoundedByFinalTypeLooks for types with upper bounds of a final typeWarningYesNo
BrokenOddnessChecks for a % 2 == 1 for oddness because this fails on negative numbersWarningYesNo
CatchExceptionChecks for try blocks that catch ExceptionWarningYesNo
CatchExceptionImmediatelyRethrownChecks for try-catch blocks that immediately rethrow caught exceptions.WarningYesNo
CatchFatalChecks for try blocks that catch fatal exceptions: VirtualMachineError, ThreadDeath, InterruptedException, LinkageError, ControlThrowableWarningYesNo
CatchNpeChecks for try blocks that catch null pointer exceptionsErrorYesNo
CatchThrowableChecks for try blocks that catch ThrowableWarningYesNo
ClassNamesEnsures class names adhere to the style guidelinesInfoYesNo
CollectionIndexOnNonIndexedSeqChecks for indexing on a Seq which is not an IndexedSeqWarningYesNo
CollectionNamingConfusionChecks for variables that are confusingly namedInfoYesNo
CollectionNegativeIndexChecks for negative access on a sequence eglist.get(-1)WarningYesNo
CollectionPromotionToAnyChecks for collection operations that promote the collection toAnyWarningYesNo
ComparingFloatingPointTypesChecks for equality checks on floating point typesErrorYesYes
ComparingUnrelatedTypesChecks for equality comparisons that cannot succeedErrorYesNo
ComparisonToEmptyListChecks for code likea == List() ora == NilInfoYesNo
ComparisonToEmptySetChecks for code likea == Set() ora == Set.emptyInfoYesNo
ComparisonWithSelfChecks for equality checks with itselfWarningYesNo
ConstantIfChecks for code where the if condition compiles to a constantWarningYesNo
DivideByOneChecks for divide by one, which always returns the original valueWarningYesNo
DoubleNegationChecks for code like!(!b)InfoYesNo
DuplicateImportChecks for import statements that import the same selectorInfoYesNo
DuplicateMapKeyChecks for duplicate key names in Map literalsWarningYesNo
DuplicateSetValueChecks for duplicate values in set literalsWarningYesNo
EitherGetChecks for use of .get on Left or RightErrorYesYes
EmptyCaseClassChecks for case classes likecase class Faceman()InfoYesNo
EmptyForChecks for emptyfor loopsWarningYesNo
EmptyIfBlockChecks for emptyif blocksWarningYesNo
EmptyInterpolatedStringLooks for interpolated strings that have no argumentsErrorYesNo
EmptyMethodLooks for empty methodsWarningYesNo
EmptySynchronizedBlockLooks for empty synchronized blocksWarningYesNo
EmptyTryBlockLooks for empty try blocksWarningYesNo
EmptyWhileBlockLooks for empty while loopsWarningYesNo
ExistsSimplifiableToContainsexists(x => x == b) replaceable withcontains(b)InfoYesNo
FilterDotHead.filter(x => ).head can be replaced withfind(x => ) match { .. }InfoYesNo
FilterDotHeadOption.filter(x =>).headOption can be replaced withfind(x => )InfoYesNo
FilterDotIsEmpty.filter(x => ).isEmpty can be replaced with!exists(x => )InfoYesNo
FilterDotSize.filter(x => ).size can be replaced more concisely with withcount(x => )InfoYesNo
FilterOptionAndGet.filter(_.isDefined).map(_.get) can be replaced withflattenInfoYesNo
FinalModifierOnCaseClassUsing Case classes withoutfinal modifier can lead to surprising breakageInfoYesNo
FinalizerWithoutSuperChecks for overridden finalizers that do not call superWarningYesNo
FindAndNotEqualsNoneReplaceWithExists.find(x => ) != None can be replaced withexist(x => )InfoYesNo
FindDotIsDefinedfind(x => ).isDefined can be replaced withexist(x => )InfoYesNo
IllegalFormatStringLooks for invalid format stringsErrorYesNo
ImpossibleOptionSizeConditionChecks for code likeoption.size > 2 which can never be trueErrorYesNo
IncorrectNumberOfArgsToFormatChecks for wrong number of arguments toString.formatErrorYesNo
IncorrectlyNamedExceptionsChecks for exceptions that are not called *Exception and vice versaErrorYesNo
InvalidRegexChecks for invalid regex literalsInfoYesNo
IsInstanceOfChecks for use ofisInstanceOfWarningYesNo
JavaConversionsUseChecks for use of implicit Java conversionsWarningYesNo
ListAppendChecks for List :+ which is O(n)InfoYesNo
ListSizeChecks forList.size which is O(n).InfoYesNo
LonelySealedTraitChecks for sealed traits which have no implementationErrorYesNo
LooksLikeInterpolatedStringFinds strings that look like they should be interpolated but are notWarningYesNo
MapGetAndGetOrElseMap.get(key).getOrElse(value) can be replaced withMap.getOrElse(key, value)ErrorYesNo
MaxParametersChecks for methods that have over 10 parametersInfoYesNo
MethodNamesWarns on method names that don't adhere to the Scala style guidelinesInfoYesNo
MethodReturningAnyChecks for defs that are defined or inferred to returnAnyWarningYesNo
ModOneChecks forx % 1 which will always return0WarningYesNo
NanComparisonChecks forx == Double.NaN which will always failErrorYesNo
NegationIsEmpty!Traversable.isEmpty can be replaced withTraversable.nonEmptyInfoYesNo
NegationNonEmpty!Traversable.nonEmpty can be replaced withTraversable.isEmptyInfoYesNo
NoOpOverrideChecks for code that overrides parent method but simply calls superInfoYesNo
NullAssignmentChecks for use ofnull in assignmentsWarningYesNo
NullParameterChecks for use ofnull in method invocationWarningYesNo
ObjectNamesEnsures object names adhere to the Scala style guidelinesInfoYesNo
OptionGetChecks forOption.getErrorYesYes
OptionSizeChecks forOption.sizeErrorYesNo
ParameterlessMethodReturnsUnitChecks fordef foo : UnitWarningYesNo
PartialFunctionInsteadOfMatchWarns when you could use a partial function directly instead of a match blockInfoYesNo
PointlessTypeBoundsFinds type bounds of the form[A <: Any] or[A >: Nothing]WarningYesNo
PreferMapEmptyChecks for Map() when could use Map.emptyInfoYesNo
PreferSeqEmptyChecks for Seq() when could use Seq.emptyInfoYesNo
PreferSetEmptyChecks for Set() when could use Set.emptyInfoYesNo
ProductWithSerializableInferredChecks for vals that haveProduct with Serializable as their inferred typeWarningYesNo
PublicFinalizerChecks for overridden finalizes that are publicInfoYesNo
RedundantFinalModifierOnMethodRedundantfinal modifier on method that cannot be overriddenInfoYesNo
RedundantFinalModifierOnVarRedundantfinal modifier on var that cannot be overriddenInfoYesNo
RedundantFinalizerChecks for empty finalizers.WarningYesNo
RepeatedCaseBodyChecks for case statements which have the same bodyWarningYesNo
RepeatedIfElseBodyChecks for the main branch and the else branch of anif being the sameWarningYesNo
ReverseFuncreverse followed byhead,headOption,iterator, ormap can be replaced, respectively, withlast,lastOption,reverseIterator, orreverseMapInfoYesNo
ReverseTailReverse.reverse.tail.reverse can be replaced withinitInfoYesNo
ReverseTakeReverse.reverse.take(...).reverse can be replaced withtakeRightInfoYesNo
SimplifyBooleanExpressionb == false can be simplified to!bInfoYesNo
StoreBeforeReturnChecks for storing a value in a block, and immediately returning the valueInfoYesNo
StripMarginOnRegexChecks for .stripMargin on regex strings that contain a pipeErrorYesNo
SubstringZeroChecks forString.substring(0)InfoYesNo
SuspiciousMatchOnClassObjectFinds code where matching is taking place on class literalsWarningYesNo
SwallowedExceptionFinds catch blocks that don't handle caught exceptionsWarningYesNo
SwapSortFiltersort.filter can be replaced withfilter.sort for performanceInfoYesNo
TryGetChecks for use ofTry.getErrorYesNo
TypeShadowingChecks for shadowed type parameters in methodsWarningYesNo
UnnecessaryConversionChecks for unnecessarytoInt on instances of Int ortoString on Strings, etc.WarningYesNo
UnnecessaryIfChecks for code likeif (expr) true else falseInfoYesNo
UnnecessaryReturnUseChecks for use ofreturn keyword in blocksInfoYesNo
UnreachableCatchChecks for catch clauses that cannot be reachedWarningYesNo
UnsafeContainsChecks forList.contains(value) for invalid typesErrorYesNo
UnsafeStringContainsChecks forString.contains(value) for invalid typesErrorYesNo
UnsafeTraversableMethodsCheck unsafe traversable method usages (head, tail, init, last, reduce, reduceLeft, reduceRight, max, maxBy, min, minBy)ErrorYesNo
UnusedMethodParameterChecks for unused method parametersWarningYesNo
UseCbrtChecks for use ofmath.pow for calculatingmath.cbrtInfoYesNo
UseExpM1Checks for use ofmath.exp(x) - 1 instead ofmath.expm1(x)InfoYesNo
UseLog10Checks for use ofmath.log(x)/math.log(10) instead ofmath.log10(x)InfoYesNo
UseLog1PChecks for use ofmath.log(x + 1) instead ofmath.log1p(x)InfoYesNo
UseSqrtChecks for use ofmath.pow for calculatingmath.sqrtInfoYesNo
VarClosureFinds closures that reference varWarningYesNo
VarCouldBeValChecks forvars that could be declared asvalsWarningYesNo
VariableShadowingChecks for multiple uses of the variable name in nested scopesWarningYesNo
WhileTrueChecks for code that uses awhile(true) ordo { } while(true) block.WarningYesNo
ZeroNumeratorChecks for dividing by 0 by a number, eg0 / x which will always return0WarningYesNo
Arrays to string

Checks for explicit toString calls on arrays. Since toString on an array does not perform a deep toString, like say scala's List, this is usually a mistake.

CollectionIndexOnNonIndexedSeq

Checks for calls of.apply(idx) on aSeq where the index is not a literal and theSeq is not anIndexedSeq.

Rationale If code which expects O(1) positional access to a Seq is given a non-IndexedSeq (such as a List, where indexing is O(n)) then this may cause poor performance.

ComparingUnrelatedTypes

Checks for equality comparisons that cannot succeed because the types are unrelated. Eg"string" == BigDecimal(1.0). The scala compiler has a less strict version of this inspection.

ConstantIf

Checks for if statements where the condition is always true or false. Not only checks for the boolean literals, but also any expression that the compiler is able to turn into a constant value. Eg,if (0 < 1) then else that

IllegalFormatString

Checks for a format string that is not invalid, such as invalid conversions, invalid flags, etc. Eg,"% s","%qs",%.-4f"

IncorrectNumberOfArgsToFormat

Checks for an incorrect number of arguments to String.format. Eg,"%s %s %f".format("need", "three") flags an error because the format string specifies 3 parameters but the call only provides 2.

InvalidRegex

Checks for invalid regex literals that would fail at compile time. Either dangling metacharacters, or unclosed escape characters, etc that kind of thing.

List size

Checks for .size on an instance of List. Eg,val a = List(1,2,3); a.size

Rationale: List.size is O(n) so for performance reasons if .size is needed on a list that could be large, consider using an alternative with O(1), eg Array, Vector or ListBuffer.

Redundant finalizer

Checks for empty finalizers. This is redundant code and should be removed. Eg,override def finalize : Unit = { }

PreferSetEmpty

Indicates where code using Set() could be replaced with Set.empty. Set() instantiates a new instance each time it is invoked, whereas Set.empty returns a pre-instantiated instance.

UnnecessaryReturnUse

Checks for use of return in a function or method. Since the final expression of a block is always the return value, using return is unnecessary. Eg,def foo = { println("hello"); return 12; }

UnreachableCatch

Checks for catch clauses that cannot be reached. This means the exception is dead and if you want that exception to take precedence you should move up further up the case list.

UnsafeContains

Checks forList.contains(value) for invalid types. The method for contains accepts any types. This inspection finds situations when you have a list of type A and you are checking for contains on type B which cannot hold.

While true

Checks for code that uses awhile(true) ordo { } while(true) block.

Rationale: This type of code is usually not meant for production as it will not return normally. If you need to loop until interrupted, then consider using a flag.

Suppressing Warnings by Method or Class

You can suppress a specific warning by method or by class using the java.lang.SuppressWarnings annotation.

Use the simple name of the inspection to be ignored as the argument, or use "all" to suppress all scapegoat warnings in the specified scope.

Some examples:

@SuppressWarnings(Array("all"))classTest {defhello:Unit= {vals:Any="sammy"    println(s.asInstanceOf[String])  }}classTest2 {@SuppressWarnings(Array("AsInstanceOf"))defhello:Unit= {vals:Any="sammy"    println(s.asInstanceOf[String])  }}

Other static analysis tools:

About

Scala compiler plugin for static code analysis

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors63


[8]ページ先頭

©2009-2025 Movatter.jp