Keep your code fresh smelling: generate "bad code smells" specs2 specifications for any source language.
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.

Keeps your code fresh smelling: generate bad code smells specs2 specifications for any source language.


sniff is available via the Maven Central repository and current supports Scala 2.9.1, 2.9.2 and 2.10.0.


libraryDependencies ++= Seq(
  "net.rosien" %% "sniff" % "0.10" % "test"



Build Status


import org.specs2.Specification

class SniffSpec extends Specification { 
  import net.rosien.sniff._
  def is = "Scala code shouldn't smell" ^ Language.Scala.snippets.sniff("src/main/scala", "src/test/scala")

Running this spec in sbt scans the directories src/main/scala and src/test/scala for bad code smells described by regular expressions:

> test
[info] Compiling 1 Scala source to /Users/arosien/asr/sniff/target/
[info] SniffSpec
[info] Code shouldn't smell
[info] + /Users/arosien/asr/sniff/src/main/scala/sniff.scala smells ok
[info] + /Users/arosien/asr/sniff/src/test/scala/SniffSpec.scala smells ok
[info] Total for specification SniffSpec
[info] Finished in 504 ms
[info] 2 examples, 0 failure, 0 error
[info] Passed: : Total 2, Failed 0, Errors 0, Passed 2, Skipped 0
[success] Total time: 4 s, completed Jan 22, 2012 7:13:31 PM

If I add the string "" to the above code (to make the smell spec fail) I get:

> test
[info] SniffSpec
[info] Code shouldn't smell
[info] + /Users/arosien/asr/sniff/src/main/scala/sniff.scala smells ok
[error] x /Users/arosien/asr/sniff/src/test/scala/SniffSpec.scala smells ok
[error]     /Users/arosien/asr/sniff/src/test/scala/SniffSpec.scala:8: failed snippet 'java\.net\.URL' (URL actually resolves hostnames over the network, use instead) '  //' matches '.*java\.net\.URL.*' (sniff.scala:40)
[info] Total for specification SniffSpec
[info] Finished in 277 ms
[info] 2 examples, 1 failure, 0 error
[error] Failed: : Total 2, Failed 1, Errors 0, Passed 1, Skipped 0
[error] Failed tests:
[error]   net.rosien.sniff.SniffSpec
[error] {file:/Users/arosien/asr/sniff/}default-28e91d/test:test: Tests unsuccessful
[error] Total time: 1 s, completed Jan 23, 2012 9:46:05 AM

If there are bad smells that you temporarily want to ignore you can define an implicit Ignores value:

// snippets.sniff() uses this implicit
implicit val ignore = Ignores(
      Ignore('NoURL, "src/test/scala/SniffSpec.scala"),

Defining your own smells

import org.specs2.Specification
import net.rosien.sniff._
val mySmells = 
  Smell('NoMutableCollections, """scala\.collection\.mutable""".r, rationale = "Immutable is better than mutable. - El Jefe", Scala, 'movieReferences) ::
  // more smells

class SniffSpec extends Specification { 
  def is = "Die smells die" ^ CodeSnippets(Language.Scala, mySmells: _*).sniff("src/main/scala", "src/test/scala")

There is a growing list of default smells for various languages defined at Please fork and send me a pull request to have yours included.

To define a new language just create a singleton object extending net.rosien.sniff.Language.

Regular Expressions? WTF!?

There are lots of ways to model "bad code smells", from simple rules to deep language parsing. sniff emphasizes:

  • smells as regular expressions, because they are easy to write and test for any source language
  • broadly scoped smells rather than complex predicates ("never use X")
  • a simple exception mechanism to ignore false positives

For more complex rules and deeper analysis, try these great tools:



  • Magic token to ignore smells on a line or in a region, e.g., for use in commented-out code.
  • Executable jar to sniff stuff from the command-line, e.g., for use in continuous integration systems.
  • Sniff helper method for maven-style paths (src/main/scala, etc.)


The license is Apache 2.0, see LICENSE-2.0.txt.