{"id":13780448,"url":"https://github.com/HairyFotr/linter","last_synced_at":"2025-05-11T13:32:16.123Z","repository":{"id":7611682,"uuid":"8969984","full_name":"HairyFotr/linter","owner":"HairyFotr","description":"Static Analysis Compiler Plugin for Scala","archived":true,"fork":false,"pushed_at":"2018-07-10T07:59:26.000Z","size":2164,"stargazers_count":267,"open_issues_count":22,"forks_count":34,"subscribers_count":18,"default_branch":"master","last_synced_at":"2024-09-30T02:20:19.037Z","etag":null,"topics":["abstract-interpretation","compiler-plugin","linter","program-analysis","scala","static-analysis"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HairyFotr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-03-23T12:36:41.000Z","updated_at":"2024-04-01T17:52:24.000Z","dependencies_parsed_at":"2022-09-01T11:52:10.050Z","dependency_job_id":null,"html_url":"https://github.com/HairyFotr/linter","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HairyFotr%2Flinter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HairyFotr%2Flinter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HairyFotr%2Flinter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HairyFotr%2Flinter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HairyFotr","download_url":"https://codeload.github.com/HairyFotr/linter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225056821,"owners_count":17414217,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["abstract-interpretation","compiler-plugin","linter","program-analysis","scala","static-analysis"],"created_at":"2024-08-03T18:01:15.799Z","updated_at":"2024-11-17T15:31:12.531Z","avatar_url":"https://github.com/HairyFotr.png","language":"Scala","funding_links":["https://www.paypal.me/HairyFotr/10"],"categories":["Linters"],"sub_categories":["Scala"],"readme":"# Linter Compiler Plugin [![Build Status](https://travis-ci.org/HairyFotr/linter.png)](https://travis-ci.org/HairyFotr/linter) [![Join the chat at https://gitter.im/HairyFotr/linter](https://badges.gitter.im/HairyFotr/linter.svg)](https://gitter.im/HairyFotr/linter?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge) [![Analytics](https://ga-beacon.appspot.com/UA-77238175-2/HairyFotr/linter?pixel)](https://github.com/igrigorik/ga-beacon)\n\nLinter is a Scala static analysis compiler plugin which adds compile-time checks for various possible bugs, inefficiencies, and style problems.\n\n## Donations\n\nPlease help support the development of Linter.\n\n[![Paypal](http://hairyfotr.psywerx.org/linter/paypal.png)](https://www.paypal.me/HairyFotr/10) [![Bitcoin](http://hairyfotr.psywerx.org/linter/32Di22QJoSPt3dAfkACvayBwmsAnfRpAUj.png)](http://hairyfotr.psywerx.org/linter/32Di22QJoSPt3dAfkACvayBwmsAnfRpAUj.html)\n\n## Usage from sbt\nAdd Linter to your project by appending this line to your `build.sbt`:\n\n    addCompilerPlugin(\"org.psywerx.hairyfotr\" %% \"linter\" % \"0.1.17\")\n\nIf you would always like to have the latest changes, snapshots are also available:\n\n    resolvers += Resolver.sonatypeRepo(\"snapshots\")\n\n    addCompilerPlugin(\"org.psywerx.hairyfotr\" %% \"linter\" % \"0.1-SNAPSHOT\")\n\n\n## Usage from maven\nAdd Linter to your project by updating your `pom.xml` with a \"compilerPlugin\" section.\n\n    \u003cconfiguration\u003e\n      \u003ccompilerPlugins\u003e\n        \u003ccompilerPlugin\u003e\n          \u003cgroupId\u003eorg.psywerx.hairyfotr\u003c/groupId\u003e\n          \u003cartifactId\u003elinter_2.11\u003c/artifactId\u003e\n          \u003cversion\u003e0.1.17\u003c/version\u003e\n        \u003c/compilerPlugin\u003e\n      \u003c/compilerPlugins\u003e\n    \u003c/configuration\u003e\n    \n## Usage from Jenkins\nUse your usual building method and [Jenkins Warnings Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Warnings+Plugin).\n\n## Manual usage\nAnother possible way to use Linter is to manually download and use these snapshot jars:\u003cbr\u003e\n[Scala 2.12](https://oss.sonatype.org/content/repositories/snapshots/org/psywerx/hairyfotr/linter_2.12/0.1-SNAPSHOT/linter_2.12-0.1-SNAPSHOT.jar), [Scala 2.11](https://oss.sonatype.org/content/repositories/snapshots/org/psywerx/hairyfotr/linter_2.11/0.1-SNAPSHOT/linter_2.11-0.1-SNAPSHOT.jar), [Scala 2.10](https://oss.sonatype.org/content/repositories/snapshots/org/psywerx/hairyfotr/linter_2.10/0.1-SNAPSHOT/linter_2.10-0.1-SNAPSHOT.jar), \u003cbr\u003e\n[Scala 2.9.3](https://github.com/HairyFotr/linteRepo/blob/gh-pages/releases/com/foursquare/lint/linter_2.9.3/0.1-SNAPSHOT/linter_2.9.3-0.1-SNAPSHOT.jar?raw=true) (outdated)\n\n\n    terminal:\n      scalac -Xplugin:\u003cpath-to-linter-jar\u003e.jar ...\n\n    sbt: (in build.sbt)\n      scalacOptions += \"-Xplugin:\u003cpath-to-linter-jar\u003e.jar\"\n\n    maven: (in pom.xml inside scala-maven-plugin configuration)\n      \u003cconfiguration\u003e\n        \u003cargs\u003e\n          \u003carg\u003e-Xplugin:\u003cpath-to-linter-jar\u003e.jar\u003c/arg\u003e\n        \u003c/args\u003e\n      \u003c/configuration\u003e\n\n__Note:__ If you have instructions for another build tool or IDE, please make a pull request.\n\n## Displaying check names\n\nTo disable displaying check names in the output use the printWarningNames switch:\n\n    scalacOptions += \"-P:linter:printWarningNames:false\"\n\nNote: Set to true by default since version 0.1.17\n\n## Enabling/Disabling checks\n\nChecks can be disabled using a plus-separated list of check names:\n\n    scalacOptions += \"-P:linter:disable:UseHypot+CloseSourceFile+OptionOfOption\"\n\nOr only specific checks can be enabled using:\n\n    scalacOptions += \"-P:linter:enable-only:UseHypot+CloseSourceFile+OptionOfOption\"\n\n## Suppressing false positives\n\nIf you believe some warnings are false positives, you can ignore them with a code comment:\n```scala\nscala\u003e val x = math.pow(5, 1/3d) + 1/0 // linter:ignore UseCbrt,DivideByZero // ignores UseCbrt and DivideByZero\n\u003cconsole\u003e:8: warning: Integer division detected in an expression assigned to a floating point variable.\n              math.pow(5, 1/3d) + 1/0 // linter:ignore UseCbrt,DivideByZero // ignores UseCbrt and DivideByZero\n                                ^\nscala\u003e val x = math.pow(5, 1/3d) + 1/0 // linter:ignore // ignores all warnings\n```\n    \n__Note:__ Please consider reporting false positives so that they can be removed in future versions.\n\n## List of implemented checks (123)\n[UnextendedSealedTrait](src/test/scala/LinterPluginTest.scala#L1960), [UnlikelyEquality](src/test/scala/LinterPluginTest.scala#L2056), [UseLog1p](src/test/scala/LinterPluginTest.scala#L1044), [UseLog10](src/test/scala/LinterPluginTest.scala#L294), [UseExpm1](src/test/scala/LinterPluginTest.scala#L1090), [UseHypot](src/test/scala/LinterPluginTest.scala#L216), [UseCbrt](src/test/scala/LinterPluginTest.scala#L238), [UseSqrt](src/test/scala/LinterPluginTest.scala#L248), [SuspiciousPow](src/test/scala/LinterPluginTest.scala#L257), [UseExp](src/test/scala/LinterPluginTest.scala#L283), [UseAbsNotSqrtSquare](src/test/scala/LinterPluginTest.scala#L1227), [UseIsNanNotSelfComparison](src/test/scala/LinterPluginTest.scala#L1911), [UseIsNanNotNanComparison](src/test/scala/LinterPluginTest.scala#L1930), [UseSignum](src/test/scala/LinterPluginTest.scala#L1720), BigDecimalNumberFormat, BigDecimalPrecisionLoss, [ReflexiveAssignment](src/test/scala/LinterPluginTest.scala#L1561), [CloseSourceFile](src/test/scala/LinterPluginTest.scala#L827), [JavaConverters](src/test/scala/LinterPluginTest.scala#L838), [ContainsTypeMismatch](src/test/scala/LinterPluginTest.scala#L844), [NumberInstanceOf](src/test/scala/LinterPluginTest.scala#L195), [PatternMatchConstant](src/test/scala/LinterPluginTest.scala#L986), PreferIfToBooleanMatch, [IdenticalCaseBodies](src/test/scala/LinterPluginTest.scala#L937), [IdenticalCaseConditions](src/test/scala/LinterPluginTest.scala#L1785), ReflexiveComparison, [YodaConditions](src/test/scala/LinterPluginTest.scala#L1644), [UseConditionDirectly](src/test/scala/LinterPluginTest.scala#L888), [UseIfExpression](src/test/scala/LinterPluginTest.scala#L146), [UnnecessaryElseBranch](src/test/scala/LinterPluginTest.scala#L156), [DuplicateIfBranches](src/test/scala/LinterPluginTest.scala#L913), [IdenticalIfElseCondition](src/test/scala/LinterPluginTest.scala#L1587), [MergeNestedIfs](src/test/scala/LinterPluginTest.scala#L1667), [VariableAssignedUnusedValue](src/test/scala/LinterPluginTest.scala#L1821), [MalformedSwap](src/test/scala/LinterPluginTest.scala#L1763), IdenticalIfCondition, [IdenticalStatements](src/test/scala/LinterPluginTest.scala#L1893), IndexingWithNegativeNumber, OptionOfOption, [UndesirableTypeInference](src/test/scala/LinterPluginTest.scala#L1797), [AssigningOptionToNull](src/test/scala/LinterPluginTest.scala#L1151), [WrapNullWithOption](src/test/scala/LinterPluginTest.scala#L1159), AvoidOptionStringSize, AvoidOptionCollectionSize, [UseGetOrElseOnOption](src/test/scala/LinterPluginTest.scala#L1166), UseOptionOrNull, UseOptionGetOrElse, [UseExistsNotFindIsDefined](src/test/scala/LinterPluginTest.scala#L1323), [UseExistsNotFilterIsEmpty](src/test/scala/LinterPluginTest.scala#L1342), [UseFindNotFilterHead](src/test/scala/LinterPluginTest.scala#L509), [UseContainsNotExistsEquals](src/test/scala/LinterPluginTest.scala#L727), [UseQuantifierFuncNotFold](src/test/scala/LinterPluginTest.scala#L750), [UseFuncNotReduce](src/test/scala/LinterPluginTest.scala#L787), [UseFuncNotFold](src/test/scala/LinterPluginTest.scala#L768), [MergeMaps](src/test/scala/LinterPluginTest.scala#L1244), [FuncFirstThenMap](src/test/scala/LinterPluginTest.scala#L1253), [FilterFirstThenSort](src/test/scala/LinterPluginTest.scala#L1268), [UseMinOrMaxNotSort](src/test/scala/LinterPluginTest.scala#L1282), [UseMapNotFlatMap](src/test/scala/LinterPluginTest.scala#L1300), [UseFilterNotFlatMap](src/test/scala/LinterPluginTest.scala#L1311), AvoidOptionMethod, [TransformNotMap](src/test/scala/LinterPluginTest.scala#L606), [DuplicateKeyInMap](src/test/scala/LinterPluginTest.scala#L1138), [InefficientUseOfListSize](src/test/scala/LinterPluginTest.scala#L1193), [OnceEvaluatedStatementsInBlockReturningFunction](src/test/scala/LinterPluginTest.scala#L1984), [IntDivisionAssignedToFloat](src/test/scala/LinterPluginTest.scala#L1992), [UseFlattenNotFilterOption](src/test/scala/LinterPluginTest.scala#L1183), [UseCountNotFilterLength](src/test/scala/LinterPluginTest.scala#L646), [UseExistsNotCountCompare](src/test/scala/LinterPluginTest.scala#L684), [PassPartialFunctionDirectly](src/test/scala/LinterPluginTest.scala#L1975), [UnitImplicitOrdering](src/test/scala/LinterPluginTest.scala#L332), [RegexWarning](src/test/scala/LinterPluginTest.scala#L518), [InvariantCondition](src/test/scala/LinterPluginTest.scala#L560), DecomposingEmptyCollection, InvariantExtrema, [UnnecessaryMethodCall](src/test/scala/LinterPluginTest.scala#L1207), ProducesEmptyCollection, [OperationAlwaysProducesZero](src/test/scala/LinterPluginTest.scala#L2267), [ModuloByOne](src/test/scala/LinterPluginTest.scala#L2016), DivideByOne, DivideByZero, ZeroDivideBy, [UseUntilNotToMinusOne](src/test/scala/LinterPluginTest.scala#L1944), [InvalidParamToRandomNextInt](src/test/scala/LinterPluginTest.scala#L2005), UnusedForLoopIteratorValue, StringMultiplicationByNonPositive, LikelyIndexOutOfBounds, UnnecessaryReturn, InvariantReturn, [UnusedParameter](src/test/scala/LinterPluginTest.scala#L1006), [InvalidStringFormat](src/test/scala/LinterPluginTest.scala#L417), InvalidStringConversion, UnnecessaryStringNonEmpty, UnnecessaryStringIsEmpty, [PossibleLossOfPrecision](src/test/scala/LinterPluginTest.scala#L304), [UnsafeAbs](src/test/scala/LinterPluginTest.scala#L344), [TypeToType](src/test/scala/LinterPluginTest.scala#L360), [EmptyStringInterpolator](src/test/scala/LinterPluginTest.scala#L434), [UnlikelyToString](src/test/scala/LinterPluginTest.scala#L445), [UnthrownException](src/test/scala/LinterPluginTest.scala#L458), [SuspiciousMatches](src/test/scala/LinterPluginTest.scala#L467), [PassingNullIntoOption](src/test/scala/LinterPluginTest.scala#L1172), [IfDoWhile](src/test/scala/LinterPluginTest.scala#L578), [FloatingPointNumericRange](src/test/scala/LinterPluginTest.scala#L2028), [UseInitNotReverseTailReverse](src/test/scala/LinterPluginTest.scala#L1454), [UseTakeRightNotReverseTakeReverse](src/test/scala/LinterPluginTest.scala#L1464), [UseLastNotReverseHead](src/test/scala/LinterPluginTest.scala#L1474), [UseFuncNotReverse](src/test/scala/LinterPluginTest.scala#L1487), [UseHeadNotApply](src/test/scala/LinterPluginTest.scala#L1504), [UseLastNotApply](src/test/scala/LinterPluginTest.scala#L1514), [UseHeadOptionNotIf](src/test/scala/LinterPluginTest.scala#L1524), [UseLastOptionNotIf](src/test/scala/LinterPluginTest.scala#L1538), [UseZipWithIndexNotZipIndices](src/test/scala/LinterPluginTest.scala#L1552), [UseGetOrElseNotPatMatch](src/test/scala/LinterPluginTest.scala#L2187), [UseOrElseNotPatMatch](src/test/scala/LinterPluginTest.scala#L2195), [UseOptionFlatMapNotPatMatch](src/test/scala/LinterPluginTest.scala#L2203), UseOptionMapNotPatMatch, [UseOptionFlattenNotPatMatch](src/test/scala/LinterPluginTest.scala#L2219), [UseOptionForeachNotPatMatch](src/test/scala/LinterPluginTest.scala#L2227), [UseOptionIsDefinedNotPatMatch](src/test/scala/LinterPluginTest.scala#L2235), [UseOptionIsEmptyNotPatMatch](src/test/scala/LinterPluginTest.scala#L2243), [UseOptionForallNotPatMatch](src/test/scala/LinterPluginTest.scala#L2259), [UseOptionExistsNotPatMatch](src/test/scala/LinterPluginTest.scala#L2251)\n\nLinks above currently go to the test for that check.\n\nAnother file to check out is [Warning.scala](src/main/scala/Warning.scala#L155)\n\n## Examples of reported warnings\n\n### If checks\n#### Repeated condition in an else-if chain\n```scala\nscala\u003e if (a == 10 || b == 10) 0 else if (a == 20 \u0026\u0026 b == 10) 1 else 2\n\u003cconsole\u003e:10: warning: This condition has appeared earlier in the if-else chain and will never hold here.\n              if (a == 10 || b == 10) 0 else if (a == 20 \u0026\u0026 b == 10) 1 else 2\n                                                              ^\n```\n\n#### Identical branches\n```scala\nscala\u003e if (b \u003e 4) (2,a) else (2,a)\n\u003cconsole\u003e:9: warning: If statement branches have the same structure.\n              if (b \u003e 4) (2,a) else (2,a)\n                    ^\n```\n\n#### Unnecessary if\n```scala\nscala\u003e if (a == b) true else false\n\u003cconsole\u003e:9: warning: Remove the if expression and use the condition directly.\n              if (a == b) true else false\n              ^\n```\n\n### Pattern matching checks\n#### Detect some unreachable cases\n```scala\nscala\u003e (x,y) match { case (a,5) if a \u003e 5 =\u003e 0 case (c,5) if c \u003e 5 =\u003e 1 }\n\u003cconsole\u003e:10: warning: Identical case condition detected above. This case will never match.\n              (x,y) match { case (a,5) if a \u003e 5 =\u003e 0 case (c,5) if c \u003e 5 =\u003e 1 }\n                                                          ^\n```\n\n#### Identical neighbouring cases\n```scala\nscala\u003e a match { case 3 =\u003e \"hello\" case 4 =\u003e \"hello\" case 5 =\u003e \"hello\" case _ =\u003e \"how low\" }\n\u003cconsole\u003e:9: warning: Bodies of 3 neighbouring cases are identical and could be merged.\n              a match { case 3 =\u003e \"hello\" case 4 =\u003e \"hello\" case 5 =\u003e \"hello\" case _ =\u003e \"how low\" }\n                                                                      ^\n```\n\n#### Match better written as if\n```scala\nscala\u003e bool match { case true =\u003e 0 case false =\u003e 1 }\n\u003cconsole\u003e:9: warning: Pattern matching on Boolean is probably better written as an if statement.\n              a match { case true =\u003e 0 case false =\u003e 1 }\n                ^\n```\n\n### Integer checks (some abstract interpretation)\n#### Check conditions\n```scala\nscala\u003e for (i \u003c- 10 to 20) { if (i \u003e 20) \"\" }\n\u003cconsole\u003e:8: warning: This condition will never hold.\n              for (i \u003c- 10 to 20) { if (i \u003e 20) \"\" }\n                                          ^\n```\n\n#### Detect division by zero\n```scala\nscala\u003e for (i \u003c- 1 to 10) { 1/(i-1)  }\n\u003cconsole\u003e:8: warning: You will likely divide by zero here.\n              for (i \u003c- 1 to 10) { 1/(i-1)  }\n                                    ^\n```\n\n#### Detect too large, or negative indices\n```scala\nscala\u003e { val a = List(1,2,3); for (i \u003c- 1 to 10) { println(a(i)) } }\n\u003cconsole\u003e:8: warning: You will likely use a too large index.\n              { val a = List(1,2,3); for (i \u003c- 1 to 10) { println(a(i)) } }\n                                                                   ^\n```\n\n### String checks (some abstract interpretation)\n#### Attempt to verify string length conditions\n```scala\nscala\u003e for (i \u003c- 10 to 20) { if (i.toString.length == 3) \"\" }\n\u003cconsole\u003e:8: warning: This condition will never hold.\n              for (i \u003c- 10 to 20) { if (i.toString.length == 3) \"\" }\n                                                          ^\n```\n\n#### Attempt to track the prefix, suffix, and pieces\n```scala\nscala\u003e { val a = \"hello\"+util.Random.nextString(10)+\"world\"+util.Random.nextString(10)+\"!\"; if (a contains \"world\") \"\"; if (a startsWith \"hell\") \"\" }\n\u003cconsole\u003e:8: warning: This contains will always returns the same value: true\n              { val a = \"hello\"+util.Random.nextString(10)+\"world\"+util.Random.nextString(10)+\"!\"; if (a contains \"world\") \"\"; if (a startsWith \"hell\") \"\" }\n                                                                                                                   ^\n\u003cconsole\u003e:8: warning: This startsWith always returns the same value: true\n              { val a = \"hello\"+util.Random.nextString(10)+\"world\"+util.Random.nextString(10)+\"!\"; if (a contains \"world\") \"\"; if (a startsWith \"hell\") \"\" }\n                                                                                                                                                ^\n```\n\n#### Regex syntax warnings\n```scala\nscala\u003e str.replaceAll(\"?\", \".\")\n\u003cconsole\u003e:9: warning: Regex pattern syntax error: Dangling meta character '?'\n              str.replaceAll(\"?\", \".\")\n                             ^\n```\n\n### Numeric checks\n#### Using `log(1 + a)` instead of `log1p(a)`\n```scala\nscala\u003e math.log(1d + a)\n\u003cconsole\u003e:9: warning: Use math.log1p(x), instead of math.log(1 + x) for added accuracy when x is near 0.\n              math.log(1 + a)\n                      ^\n```\n\n#### Loss of precision on BigDecimal\n```scala\nscala\u003e BigDecimal(0.555555555555555555555555555)\n\u003cconsole\u003e:8: warning: Possible loss of precision. Literal cannot be represented exactly by Double. (0.555555555555555555555555555 != 0.5555555555555556)\n              BigDecimal(0.555555555555555555555555555)\n                        ^\n```\n\n### Option checks\n#### Using Option.size\n```scala\nscala\u003e val a = Some(List(1,2,3)); if (a.size \u003e 3) \"\"\n\u003cconsole\u003e:9: warning: Did you mean to take the size of the collection inside the Option?\n              if (a.size \u003e 3) \"\"\n                    ^\n```\n\n#### Using if-else instead of getOrElse\n```scala\nscala\u003e if (strOption.isDefined) strOption.get else \"\"\n\u003cconsole\u003e:9: warning: Use strOption.getOrElse(...) instead of if (strOption.isDefined) strOption.get else ...\n              if (strOption.isDefined) strOption.get else \"\"\n                                       ^\n```\n\n### Collection checks\n#### Use exists(...) instead of find(...).isDefined\n```scala\nscala\u003e List(1,2,3,4).find(x =\u003e x % 2 == 0).isDefined\n\u003cconsole\u003e:8: warning: Use col.exists(...) instead of col.find(...).isDefined.\n              List(1,2,3,4).find(x =\u003e x % 2 == 0).isDefined\n                            ^\n```\n\n#### Use filter(...) instead of flatMap(...)\n```scala\nscala\u003e List(1,2,3,4).flatMap(x =\u003e if (x % 2 == 0) List(x) else Nil)\n\u003cconsole\u003e:8: warning: Use col.filter(x =\u003e condition) instead of col.flatMap(x =\u003e if (condition) ... else ...).\n              List(1,2,3,4).flatMap(x =\u003e if (x % 2 == 0) List(x) else Nil)\n                                   ^\n```\n\n### Various possible bugs\n#### Unused method parameters\n```scala\nscala\u003e def func(b: Int, c: String, d: String) = { println(b); b+c }\n\u003cconsole\u003e:7: warning: Parameter d is not used in method func\n              def func(b: Int, c: String, d: String) = { println(b); b+c }\n                  ^\n```\n\n#### Unsafe `contains`\n```scala\nscala\u003e List(1, 2, 3).contains(\"4\")\n\u003cconsole\u003e:29: warning: List[Int].contains(String) will probably return false, since the collection and target element are of unrelated types.\n               List(1, 2, 3).contains(\"4\")\n                             ^\n```\n\n#### Unsafe `==`\n```scala\nscala\u003e Nil == None\n\u003cconsole\u003e:29: warning: Comparing with == on instances of unrelated types (scala.collection.immutable.Nil.type, None.type) will probably return false.\n               Nil == None\n                   ^\n```\n\n## Future Work\n\n* Add more checks\n* Improve documentation (bug/style/optimization/numeric, most valuable checks, descriptions, ...)\n* Improve testing (larger samples, generated tests, ...)\n* Choose whether specific checks should return warnings or errors\n* Reduce false positive rate\n* Check out quasiquotes\n\n### Ideas for new checks\n\nFeel free to add your own ideas, or implement these. Pull requests welcome!\n\n* Warn on shadowing variables, especially those of the same type (`var a = 4; { val a = 5 }`)\n* Warn on inexhaustive pattern matching or unreachable cases\n* Boolean function parameters should be named (`func(\"arg1\", force = true)`)\n* Add some abstract interpretation for collections and floats\n\n\nRule lists from other static analysis tools:\n* ScalaStyle(Scala) - https://github.com/scalastyle/scalastyle/wiki\n* Scapegoat(Scala) - https://github.com/sksamuel/scalac-scapegoat-plugin#inspections\n* WartRemover(Scala) - https://github.com/puffnfresh/wartremover#warts\n* Scala Abide(Scala) - https://github.com/scala/scala-abide\n* ($)SuperSafe™(Scala) - http://www.artima.com/supersafe_user_guide.html\n* Scala Clippy(Scala) - https://scala-clippy.org/\n* IntelliJ IDEA(Scala/...) - https://www.jetbrains.com/idea/features/scala.html\n* Findbugs(JVM) - http://findbugs.sourceforge.net/bugDescriptions.html\n* ($)Julia(JVM) - http://www.juliasoft.com/solutions/warnings\n* CheckStyle(Java) - http://checkstyle.sourceforge.net/availablechecks.html\n* PMD(Java) - http://pmd.sourceforge.net/snapshot/pmd-java/rules/index.html\n* Error-prone(Java) - https://github.com/google/error-prone\n* CodeNarc(Groovy) - http://codenarc.sourceforge.net/codenarc-rule-index.html\n* Shellcheck(shell) - http://www.shellcheck.net/about.html\n* ($)PVS-Studio(C++) - http://www.viva64.com/en/w/\n* ($)Coverity(C++/Java/...) - http://www.slideshare.net/Coverity/static-analysis-primer-22874326 (6,7)\n* CppCheck(C++) - http://sourceforge.net/p/cppcheck/wiki/ListOfChecks/\n* Clang(C++/ObjC) - http://clang-analyzer.llvm.org/available_checks.html\n* OCLint(C++/ObjC) - http://docs.oclint.org/en/dev/rules/index.html\n* Infer(Java/C/ObjC) - http://fbinfer.com/docs/infer-bug-types.html\n* ... https://github.com/mre/awesome-static-analysis\n* ... https://github.com/mcandre/linters\n* ...\n\n### Some resources\n* A repo with various resources about the scala compiler: https://github.com/illandan/scala.compiler.guides\n* A quick overview of writing compiler plugins: http://www.scala-lang.org/old/node/140\n* Basic tree example, and list of AST elements: http://stackoverflow.com/q/10419101/293115\n* More AST examples from Wolfe: https://github.com/wolfe-pack/wolfe/wiki/Scala-AST-reference\n* Notes on compiler plugins from ScalaCL: http://code.google.com/p/scalacl/wiki/WritingScalaCompilerPlugins\n* Notes and a similar compiler plugin from a while ago: https://github.com/ymasory/alacs/blob/master/dev/resources.md\n* Great article about practical static analysis from Coverity authors: http://cacm.acm.org/magazines/2010/2/69354-a-few-billion-lines-of-code-later/fulltext\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHairyFotr%2Flinter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FHairyFotr%2Flinter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHairyFotr%2Flinter/lists"}