All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wartremover.contrib.warts.ExposedTuples.scala Maven / Gradle / Ivy

The newest version!
package org.wartremover.contrib.warts

import org.wartremover.WartTraverser
import org.wartremover.WartUniverse
import scala.collection.mutable

object ExposedTuples extends WartTraverser {
  def message: String =
    "Avoid using tuples in public interfaces, as they only supply type information. Consider using a custom case class to add semantic meaning."

  private final case class LineInFile(content: Option[String], startLine: Int)

  def apply(u: WartUniverse): u.Traverser = {
    val linesWithError = mutable.Set.empty[LineInFile]

    new u.Traverser(this) {
      import q.reflect.*
      def allTypes(t: TypeRepr): List[TypeRepr] = {
        t match {
          case AppliedType(_, args) =>
            t :: args.flatMap(allTypes)
          case _ =>
            t :: Nil
        }
      }

      def containsTuple(t: Seq[TypeRepr]): Boolean =
        t.flatMap(allTypes).exists(_.isTupleN)

      def addError(pos: Position): Unit = {
        error(pos, message)
        linesWithError.add(LineInFile(pos.sourceFile.content, pos.startLine))
      }

      def errorAlreadyExists(pos: Position): Boolean = {
        linesWithError.contains(LineInFile(pos.sourceFile.content, pos.startLine))
      }

      def implicitClassHasWartAnnotation(owner: Symbol, name: String): Boolean = {
        owner.declaredTypes.collect { case c if c.isClassDef => c.tree }.collect {
          case c: ClassDef if c.name == name => c
        }.exists(hasWartAnnotation)
      }

      override def traverseTree(tree: Tree)(owner: Symbol): Unit = {
        tree match {
          case _ if hasWartAnnotation(tree) =>
          case _ if errorAlreadyExists(tree.pos) =>
          case t: DefDef =>
            if (
              !t.symbol.flags.is(Flags.Private) && t.name != "unapply" && !t.symbol.flags.is(
                Flags.Local
              ) && !implicitClassHasWartAnnotation(owner, t.name)
            ) {
              if (containsTuple(t.returnTpt.tpe :: Nil)) {
                addError(t.returnTpt.pos)
              } else if (containsTuple(t.termParamss.flatMap(_.params).map(_.tpt.tpe))) {
                addError(t.pos)
              }
            }
          case t: ValDef =>
            if (
              !t.symbol.flags.is(Flags.Private) && !owner.flags.is(Flags.Local) && containsTuple(
                t.tpt.tpe :: Nil
              ) && !t.symbol.flags.is(Flags.Synthetic)
            ) {
              addError(tree.pos)
            }
          case _ =>
            super.traverseTree(tree)(owner)
        }
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy