Exclude a specific implicit from a Scala project

686 views Asked by At

How can I prevent the usage of a specific implicit in my scala code?

For example, I was recently bit by the default Codec provided by https://github.com/scala/scala/blob/68bad81726d15d03a843dc476d52cbbaf52fb168/src/library/scala/io/Codec.scala#L76. Is there a way to ensure that any code that calls for an implicit codec: Codec never uses the one provided by fallbackSystemCodec? Alternatively, is it possible to block all implicit Codecs?

Is this something that should be doable using scalafix?

2

There are 2 answers

0
Mario Galic On

Scalafix can inspect implicit arguments using SemanticTree. Here is an example solution by defining a custom scalafix rule.

Given

import scala.io.Codec

object Hello {
  def foo(implicit codec: Codec) = 3
  foo
}

we can define a custom rule

class ExcludedImplicitsRule(config: ExcludedImplicitsRuleConfig)
    extends SemanticRule("ExcludedImplicitsRule") {

...

  override def fix(implicit doc: SemanticDocument): Patch = {
    doc.tree.collect {
      case term: Term if term.synthetic.isDefined => // TODO: Use ApplyTree(func, args)
        val struct = term.synthetic.structure
        val isImplicit = struct.contains("implicit")
        val excludedImplicit = config.blacklist.find(struct.contains)
        if (isImplicit && excludedImplicit.isDefined)
          Patch.lint(ExcludedImplicitsDiagnostic(term, excludedImplicit.getOrElse(config.blacklist.mkString(","))))
        else
          Patch.empty
    }.asPatch
  }

}

and corresponding .scalafix.conf

rule = ExcludedImplicitsRule
ExcludedImplicitsRuleConfig.blacklist = [
  fallbackSystemCodec
]

should enable sbt scalafix to raise the diagnostic

[error] /Users/mario/IdeaProjects/scalafix-exclude-implicits/example-project/scalafix-exclude-implicits-example/src/main/scala/example/Hello.scala:7:3: error: [ExcludedImplicitsRule] Attempting to pass excluded implicit fallbackSystemCodec to foo'
[error]   foo
[error]   ^^^
[error] (Compile / scalafix) scalafix.sbt.ScalafixFailed: LinterError

Note the output of println(term.synthetic.structure)

Some(ApplyTree(
  OriginalTree(Term.Name("foo")),
  List(
    IdTree(SymbolInformation(scala/io/LowPriorityCodecImplicits#fallbackSystemCodec. => implicit lazy val method fallbackSystemCodec: Codec))
  )
))

Clearly the above solution is not efficient as it searches strings, however it should give some direction. Perhaps matching on ApplyTree(func, args) would be better.

scalafix-exclude-implicits-example shows how to configure the project to use ExcludedImplicitsRule.

1
Luciano On

You can do this by using a new type altogether; this way, nobody will be able to override it in your dependencies. It's essentially the answer I posted to create an ambiguous low priority implicit

It may not be practical though, if for example you can't change the type.