- Notifications
You must be signed in to change notification settings - Fork94
Scala compiler plugin for static code analysis
License
scapegoat-scala/scapegoat
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
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.
Scapegoat is developed as a scala compiler plugin, which can then be used inside your build tool.
See:sbt-scapegoat for SBT integration.
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.
Usegradle-scapegoat-plugin by @eugene-sy
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"
| Flag | Parameters | Required |
|---|---|---|
-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 |
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:180And if you prefer a prettier report, here is a screen shot of the type of HTML report scapegoat generates:
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=ignoreThere 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)
| Name | Brief Description | Default Level | Scala 2 | Scala 3 |
|---|---|---|---|---|
| AbstractTrait | Check if trait is abstract | Info | Yes | Yes |
| ArrayEquals | Checks for comparison of arrays using== which will always return false | Info | Yes | No |
| ArraysInFormat | Checks for arrays passed to String.format | Error | Yes | No |
| ArraysToString | Checks for explicit toString calls on arrays | Warning | Yes | No |
| AsInstanceOf | Checks for use ofasInstanceOf | Warning | Yes | No |
| AvoidOperatorOverload | Checks for mental symbolic method names | Info | Yes | No |
| AvoidRequire | Use of require | Warning | Yes | Yes |
| AvoidSizeEqualsZero | Traversable.size can be slow for some data structure, prefer .isEmpty | Warning | Yes | No |
| AvoidSizeNotEqualsZero | Traversable.size can be slow for some data structure, prefer .nonEmpty | Warning | Yes | No |
| AvoidToMinusOne | Checks for loops that usex to n-1 instead ofx until n | Info | Yes | No |
| BigDecimalDoubleConstructor | Checks for use ofBigDecimal(double) which can be unsafe | Warning | Yes | No |
| BigDecimalScaleWithoutRoundingMode | setScale() on aBigDecimal without setting the rounding mode can throw an exception | Warning | Yes | No |
| BooleanParameter | Checks for functions that have a Boolean parameter | Info | Yes | No |
| BoundedByFinalType | Looks for types with upper bounds of a final type | Warning | Yes | No |
| BrokenOddness | Checks for a % 2 == 1 for oddness because this fails on negative numbers | Warning | Yes | No |
| CatchException | Checks for try blocks that catch Exception | Warning | Yes | No |
| CatchExceptionImmediatelyRethrown | Checks for try-catch blocks that immediately rethrow caught exceptions. | Warning | Yes | No |
| CatchFatal | Checks for try blocks that catch fatal exceptions: VirtualMachineError, ThreadDeath, InterruptedException, LinkageError, ControlThrowable | Warning | Yes | No |
| CatchNpe | Checks for try blocks that catch null pointer exceptions | Error | Yes | No |
| CatchThrowable | Checks for try blocks that catch Throwable | Warning | Yes | No |
| ClassNames | Ensures class names adhere to the style guidelines | Info | Yes | No |
| CollectionIndexOnNonIndexedSeq | Checks for indexing on a Seq which is not an IndexedSeq | Warning | Yes | No |
| CollectionNamingConfusion | Checks for variables that are confusingly named | Info | Yes | No |
| CollectionNegativeIndex | Checks for negative access on a sequence eglist.get(-1) | Warning | Yes | No |
| CollectionPromotionToAny | Checks for collection operations that promote the collection toAny | Warning | Yes | No |
| ComparingFloatingPointTypes | Checks for equality checks on floating point types | Error | Yes | Yes |
| ComparingUnrelatedTypes | Checks for equality comparisons that cannot succeed | Error | Yes | No |
| ComparisonToEmptyList | Checks for code likea == List() ora == Nil | Info | Yes | No |
| ComparisonToEmptySet | Checks for code likea == Set() ora == Set.empty | Info | Yes | No |
| ComparisonWithSelf | Checks for equality checks with itself | Warning | Yes | No |
| ConstantIf | Checks for code where the if condition compiles to a constant | Warning | Yes | No |
| DivideByOne | Checks for divide by one, which always returns the original value | Warning | Yes | No |
| DoubleNegation | Checks for code like!(!b) | Info | Yes | No |
| DuplicateImport | Checks for import statements that import the same selector | Info | Yes | No |
| DuplicateMapKey | Checks for duplicate key names in Map literals | Warning | Yes | No |
| DuplicateSetValue | Checks for duplicate values in set literals | Warning | Yes | No |
| EitherGet | Checks for use of .get on Left or Right | Error | Yes | Yes |
| EmptyCaseClass | Checks for case classes likecase class Faceman() | Info | Yes | No |
| EmptyFor | Checks for emptyfor loops | Warning | Yes | No |
| EmptyIfBlock | Checks for emptyif blocks | Warning | Yes | No |
| EmptyInterpolatedString | Looks for interpolated strings that have no arguments | Error | Yes | No |
| EmptyMethod | Looks for empty methods | Warning | Yes | No |
| EmptySynchronizedBlock | Looks for empty synchronized blocks | Warning | Yes | No |
| EmptyTryBlock | Looks for empty try blocks | Warning | Yes | No |
| EmptyWhileBlock | Looks for empty while loops | Warning | Yes | No |
| ExistsSimplifiableToContains | exists(x => x == b) replaceable withcontains(b) | Info | Yes | No |
| FilterDotHead | .filter(x => ).head can be replaced withfind(x => ) match { .. } | Info | Yes | No |
| FilterDotHeadOption | .filter(x =>).headOption can be replaced withfind(x => ) | Info | Yes | No |
| FilterDotIsEmpty | .filter(x => ).isEmpty can be replaced with!exists(x => ) | Info | Yes | No |
| FilterDotSize | .filter(x => ).size can be replaced more concisely with withcount(x => ) | Info | Yes | No |
| FilterOptionAndGet | .filter(_.isDefined).map(_.get) can be replaced withflatten | Info | Yes | No |
| FinalModifierOnCaseClass | Using Case classes withoutfinal modifier can lead to surprising breakage | Info | Yes | No |
| FinalizerWithoutSuper | Checks for overridden finalizers that do not call super | Warning | Yes | No |
| FindAndNotEqualsNoneReplaceWithExists | .find(x => ) != None can be replaced withexist(x => ) | Info | Yes | No |
| FindDotIsDefined | find(x => ).isDefined can be replaced withexist(x => ) | Info | Yes | No |
| IllegalFormatString | Looks for invalid format strings | Error | Yes | No |
| ImpossibleOptionSizeCondition | Checks for code likeoption.size > 2 which can never be true | Error | Yes | No |
| IncorrectNumberOfArgsToFormat | Checks for wrong number of arguments toString.format | Error | Yes | No |
| IncorrectlyNamedExceptions | Checks for exceptions that are not called *Exception and vice versa | Error | Yes | No |
| InvalidRegex | Checks for invalid regex literals | Info | Yes | No |
| IsInstanceOf | Checks for use ofisInstanceOf | Warning | Yes | No |
| JavaConversionsUse | Checks for use of implicit Java conversions | Warning | Yes | No |
| ListAppend | Checks for List :+ which is O(n) | Info | Yes | No |
| ListSize | Checks forList.size which is O(n). | Info | Yes | No |
| LonelySealedTrait | Checks for sealed traits which have no implementation | Error | Yes | No |
| LooksLikeInterpolatedString | Finds strings that look like they should be interpolated but are not | Warning | Yes | No |
| MapGetAndGetOrElse | Map.get(key).getOrElse(value) can be replaced withMap.getOrElse(key, value) | Error | Yes | No |
| MaxParameters | Checks for methods that have over 10 parameters | Info | Yes | No |
| MethodNames | Warns on method names that don't adhere to the Scala style guidelines | Info | Yes | No |
| MethodReturningAny | Checks for defs that are defined or inferred to returnAny | Warning | Yes | No |
| ModOne | Checks forx % 1 which will always return0 | Warning | Yes | No |
| NanComparison | Checks forx == Double.NaN which will always fail | Error | Yes | No |
| NegationIsEmpty | !Traversable.isEmpty can be replaced withTraversable.nonEmpty | Info | Yes | No |
| NegationNonEmpty | !Traversable.nonEmpty can be replaced withTraversable.isEmpty | Info | Yes | No |
| NoOpOverride | Checks for code that overrides parent method but simply calls super | Info | Yes | No |
| NullAssignment | Checks for use ofnull in assignments | Warning | Yes | No |
| NullParameter | Checks for use ofnull in method invocation | Warning | Yes | No |
| ObjectNames | Ensures object names adhere to the Scala style guidelines | Info | Yes | No |
| OptionGet | Checks forOption.get | Error | Yes | Yes |
| OptionSize | Checks forOption.size | Error | Yes | No |
| ParameterlessMethodReturnsUnit | Checks fordef foo : Unit | Warning | Yes | No |
| PartialFunctionInsteadOfMatch | Warns when you could use a partial function directly instead of a match block | Info | Yes | No |
| PointlessTypeBounds | Finds type bounds of the form[A <: Any] or[A >: Nothing] | Warning | Yes | No |
| PreferMapEmpty | Checks for Map() when could use Map.empty | Info | Yes | No |
| PreferSeqEmpty | Checks for Seq() when could use Seq.empty | Info | Yes | No |
| PreferSetEmpty | Checks for Set() when could use Set.empty | Info | Yes | No |
| ProductWithSerializableInferred | Checks for vals that haveProduct with Serializable as their inferred type | Warning | Yes | No |
| PublicFinalizer | Checks for overridden finalizes that are public | Info | Yes | No |
| RedundantFinalModifierOnMethod | Redundantfinal modifier on method that cannot be overridden | Info | Yes | No |
| RedundantFinalModifierOnVar | Redundantfinal modifier on var that cannot be overridden | Info | Yes | No |
| RedundantFinalizer | Checks for empty finalizers. | Warning | Yes | No |
| RepeatedCaseBody | Checks for case statements which have the same body | Warning | Yes | No |
| RepeatedIfElseBody | Checks for the main branch and the else branch of anif being the same | Warning | Yes | No |
| ReverseFunc | reverse followed byhead,headOption,iterator, ormap can be replaced, respectively, withlast,lastOption,reverseIterator, orreverseMap | Info | Yes | No |
| ReverseTailReverse | .reverse.tail.reverse can be replaced withinit | Info | Yes | No |
| ReverseTakeReverse | .reverse.take(...).reverse can be replaced withtakeRight | Info | Yes | No |
| SimplifyBooleanExpression | b == false can be simplified to!b | Info | Yes | No |
| StoreBeforeReturn | Checks for storing a value in a block, and immediately returning the value | Info | Yes | No |
| StripMarginOnRegex | Checks for .stripMargin on regex strings that contain a pipe | Error | Yes | No |
| SubstringZero | Checks forString.substring(0) | Info | Yes | No |
| SuspiciousMatchOnClassObject | Finds code where matching is taking place on class literals | Warning | Yes | No |
| SwallowedException | Finds catch blocks that don't handle caught exceptions | Warning | Yes | No |
| SwapSortFilter | sort.filter can be replaced withfilter.sort for performance | Info | Yes | No |
| TryGet | Checks for use ofTry.get | Error | Yes | No |
| TypeShadowing | Checks for shadowed type parameters in methods | Warning | Yes | No |
| UnnecessaryConversion | Checks for unnecessarytoInt on instances of Int ortoString on Strings, etc. | Warning | Yes | No |
| UnnecessaryIf | Checks for code likeif (expr) true else false | Info | Yes | No |
| UnnecessaryReturnUse | Checks for use ofreturn keyword in blocks | Info | Yes | No |
| UnreachableCatch | Checks for catch clauses that cannot be reached | Warning | Yes | No |
| UnsafeContains | Checks forList.contains(value) for invalid types | Error | Yes | No |
| UnsafeStringContains | Checks forString.contains(value) for invalid types | Error | Yes | No |
| UnsafeTraversableMethods | Check unsafe traversable method usages (head, tail, init, last, reduce, reduceLeft, reduceRight, max, maxBy, min, minBy) | Error | Yes | No |
| UnusedMethodParameter | Checks for unused method parameters | Warning | Yes | No |
| UseCbrt | Checks for use ofmath.pow for calculatingmath.cbrt | Info | Yes | No |
| UseExpM1 | Checks for use ofmath.exp(x) - 1 instead ofmath.expm1(x) | Info | Yes | No |
| UseLog10 | Checks for use ofmath.log(x)/math.log(10) instead ofmath.log10(x) | Info | Yes | No |
| UseLog1P | Checks for use ofmath.log(x + 1) instead ofmath.log1p(x) | Info | Yes | No |
| UseSqrt | Checks for use ofmath.pow for calculatingmath.sqrt | Info | Yes | No |
| VarClosure | Finds closures that reference var | Warning | Yes | No |
| VarCouldBeVal | Checks forvars that could be declared asvals | Warning | Yes | No |
| VariableShadowing | Checks for multiple uses of the variable name in nested scopes | Warning | Yes | No |
| WhileTrue | Checks for code that uses awhile(true) ordo { } while(true) block. | Warning | Yes | No |
| ZeroNumerator | Checks for dividing by 0 by a number, eg0 / x which will always return0 | Warning | Yes | No |
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.
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.
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.
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
Checks for a format string that is not invalid, such as invalid conversions, invalid flags, etc. Eg,"% s","%qs",%.-4f"
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.
Checks for invalid regex literals that would fail at compile time. Either dangling metacharacters, or unclosed escape characters, etc that kind of thing.
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.
Checks for empty finalizers. This is redundant code and should be removed. Eg,override def finalize : Unit = { }
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.
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; }
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.
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.
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.
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]) }}
- ScalaStyle (Scala) -https://github.com/scalastyle/scalastyle/wiki
- Linter (Scala) -https://github.com/HairyFotr/linter
- WartRemover (Scala) -https://github.com/puffnfresh/wartremover
- Findbugs (JVM) -http://findbugs.sourceforge.net/bugDescriptions.html
- Fb-contrib (JVM) -http://fb-contrib.sourceforge.net/
- CheckStyle (Java) -http://checkstyle.sourceforge.net/availablechecks.html
- PMD (Java) -http://pmd.sourceforge.net/pmd-5.0.3/rules/index.html
- Error-prone (Java) -https://github.com/google/error-prone
- CodeNarc (Groovy) -http://codenarc.sourceforge.net/codenarc-rule-index.html
- PVS-Studio (C++) -http://www.viva64.com/en/d/
- Coverity (C++) -http://www.slideshare.net/Coverity/static-analysis-primer-22874326 (6,7)
- CppCheck (C++) -http://cppcheck.sourceforge.net/
- OCLint (C++/ObjC) -http://docs.oclint.org/en/dev/rules/index.html
- JSHint (Javascript) -http://jshint.com/
- JavascriptLint (Javascript) -http://www.javascriptlint.com/
- ClosureLinter (Javascript) -https://developers.google.com/closure/utilities/
About
Scala compiler plugin for static code analysis
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.
