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

com.sksamuel.scapegoat.Inspection.scala Maven / Gradle / Ivy

package com.sksamuel.scapegoat

import scala.reflect.internal.util.Position
import scala.tools.nsc.Global

/**
 * @author Stephen Samuel
 */
abstract class Inspection(
  val text: String,
  val defaultLevel: Level,
  val description: String,
  val explanation: String
) {

  val self = this

  def inspector(context: InspectionContext): Inspector

  def isEnabled: Boolean = true

  def name: String = getClass.getSimpleName
}

abstract class Inspector(val context: InspectionContext) {

  /**
   * This traverser, if defined, is invoked after the typer phase of the compiler has returned.
   */
  def postTyperTraverser: context.Traverser

  /**
   * This method is invoked after all phases of the compiler have completed.
   * This method can be used to clean up inspections; to report errors after all phases are complete.
   */
  def postInspection(): Unit = ()
}

case class InspectionContext(global: Global, feedback: Feedback) {

  def warn(pos: Position, inspection: Inspection): Unit =
    feedback.warn(pos, inspection, None, None)

  def warn(pos: Position, inspection: Inspection, snippet: String): Unit =
    feedback.warn(pos, inspection, Some(snippet), None)

  def warn(
    pos: Position,
    inspection: Inspection,
    snippet: String,
    adhocExplanation: String
  ): Unit =
    feedback.warn(pos, inspection, Some(snippet), Some(adhocExplanation))

  trait Traverser extends global.Traverser {

    import global._

    private val SuppressWarnings = typeOf[SuppressWarnings]

    @scala.annotation.tailrec
    private def inspectionClass(klass: Class[_]): Class[_] =
      Option(klass.getEnclosingClass) match {
        case None    => klass
        case Some(k) => inspectionClass(k)
      }

    private def isThisDisabled(an: AnnotationInfo): Boolean = {
      val cls = inspectionClass(getClass)
      val names = Set("all", cls.getSimpleName, cls.getCanonicalName).map(_.toLowerCase)
      val suppressedNames: Seq[String] = an.javaArgs.values.headOption.toSeq.flatMap {
        case ArrayAnnotArg(args) =>
          args.collect { case LiteralAnnotArg(Constant(suppressedName: String)) =>
            suppressedName.toLowerCase
          }
        case _ =>
          Seq.empty[String]
      }
      names.intersect(suppressedNames.toSet).nonEmpty
    }

    private def isSkipAnnotation(an: AnnotationInfo) =
      // Workaround for #222: we can't use typeOf[Safe] here it requires Scapegoat to be on the
      // compile classpath.
      an.tree.tpe =:= SuppressWarnings || an.tree.tpe.erasure.toString == "com.sksamuel.scapegoat.Safe"

    private def isSuppressed(symbol: Symbol) =
      symbol != null &&
      symbol.annotations.exists(an => isSkipAnnotation(an) && isThisDisabled(an))

    protected def continue(tree: Tree) = super.traverse(tree)

    protected def inspect(tree: Tree): Unit

    override final def traverse(tree: Tree): Unit = {
      tree match {
        // ignore synthetic methods added
        case DefDef(_, _, _, _, _, _) if tree.symbol.isSynthetic =>
        case ValDef(_, _, _, _) if tree.symbol.isSynthetic       =>
        case member: MemberDef if isSuppressed(member.symbol)    =>
        case block @ Block(_, _) if isSuppressed(block.symbol)   =>
        case iff @ If(_, _, _) if isSuppressed(iff.symbol)       =>
        case tri @ Try(_, _, _) if isSuppressed(tri.symbol)      =>
        case ClassDef(_, _, _, Template(parents, _, _))
            if parents.map(_.tpe.typeSymbol.fullName).contains("scala.reflect.api.TypeCreator") =>
        case _ if analyzer.hasMacroExpansionAttachment(tree) => //skip macros as per http://bit.ly/2uS8BrU
        case _                                               => inspect(tree)
      }
    }

    protected def isArray(tree: Tree): Boolean = tree.tpe.typeSymbol.fullName == "scala.Array"
    protected def isTraversable(tree: Tree): Boolean = tree.tpe <:< typeOf[Traversable[Any]]
    protected def isSeq(t: Tree): Boolean = t.tpe <:< typeOf[Seq[Any]]
    protected def isIndexedSeq(t: Tree): Boolean = t.tpe <:< typeOf[IndexedSeq[Any]]
    protected def isSet(t: Tree, allowMutableSet: Boolean = true): Boolean = {
      t.tpe.widen.baseClasses.exists { c =>
        (allowMutableSet && c.fullName == "scala.collection.mutable.Set") || c.fullName == "scala.collection.immutable.Set"
      }
    }
    protected def isList(t: Tree): Boolean = t.tpe <:< typeOf[scala.collection.immutable.List[Any]]
    protected def isMap(tree: Tree): Boolean =
      tree.tpe.baseClasses.exists {
        _.fullName == "scala.collection.Map"
      }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy