Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Prepare to migrate to scalameta #195

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
75 changes: 44 additions & 31 deletions src/main/scala/org/scalastyle/Checker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,14 @@ case class Lines(lines: Array[Line], lastChar: Char) {
None
}

def toLine(position: Int): Option[Int] =
findLineAndIndex(position) map { case (_, index) => index }

def toLineColumn(position: Int): Option[LineColumn] =
findLineAndIndex(position) map { case (line, index) => LineColumn(index, position - line.start) }

def toFullLineTuple(position: Int): Option[(LineColumn, LineColumn)] =
findLineAndIndex(position) map { case (line, index) => (LineColumn(index, 0), LineColumn(index + 1, 0)) }
def toFullLineTuple(position: Int): Option[(Int, Int)] =
findLineAndIndex(position) map { case (_, index) => (index, index + 1) }

}

Expand All @@ -71,9 +74,10 @@ class ScalastyleChecker[T <: FileSpec](classLoader: Option[ClassLoader] = None)
private[this] def privateCheckFiles(
configuration: ScalastyleConfiguration,
files: Iterable[T]
): Seq[Message[T]] = {
val checks = configuration.checks.filter(_.enabled)
): List[Message[T]] = {
val checkerUtils = new CheckerUtils(classLoader)
val checks = checkerUtils.createChecks(configuration.checks.filter(_.enabled))

StartWork() :: files
.flatMap(file =>
StartFile(file) :: checkerUtils.verifyFile(configuration, checks, file) :::
Expand Down Expand Up @@ -105,9 +109,10 @@ object Checker {
)
}

def isObject(s: String): Boolean =
s == "java.lang.Object" || s == "Any" || s == "scala.Any" || s == "Object"
private val ObjectNames = Set("Any", "Object", "scala.Any", "java.lang.Object")
def isObject(s: String): Boolean = ObjectNames.contains(s)
def isNotObject(s: String): Boolean = !isObject(s)

}

class CheckerUtils(classLoader: Option[ClassLoader] = None) {
Expand All @@ -125,47 +130,49 @@ class CheckerUtils(classLoader: Option[ClassLoader] = None) {

def verifySource[T <: FileSpec](
configuration: ScalastyleConfiguration,
classes: List[ConfigurationChecker],
checks: => List[Checker[_]],
file: T,
source: String
): List[Message[T]] = {
if (source.isEmpty)
): List[Message[T]] =
if (source.isEmpty) {
Nil
else {
val lines = Checker.parseLines(source)
} else {
val scalariformAst = parseScalariform(source)

val commentFilters =
if (configuration.commentFilter)
CommentFilter.findCommentFilters(scalariformAst.comments, lines)
else
Nil

classes
.flatMap(cc => newInstance(cc.className, cc.level, cc.parameters, cc.customMessage, cc.customId))
.flatMap(c =>
c match {
case c: FileChecker => c.verify(file, c.level, lines, lines)
case c: ScalariformChecker => c.verify(file, c.level, scalariformAst.ast, lines)
case c: CombinedChecker => c.verify(file, c.level, CombinedAst(scalariformAst.ast, lines), lines)
case _ => Nil
}
)
.filter(m => CommentFilter.filterApplies(m, commentFilters))
verifyAst(configuration, checks, file, scalariformAst, source)
}

private def verifyAst[T <: FileSpec](
configuration: ScalastyleConfiguration,
checks: List[Checker[_]],
file: T,
ast: ScalariformAst,
source: String
): List[Message[T]] = {
val lines = Checker.parseLines(source)
val commentFilters =
if (configuration.commentFilter) CommentFilter.findCommentFilters(ast.comments, lines) else Nil

checks
.flatMap {
case c: FileChecker => c.verify(file, c.level, lines, lines)
case c: ScalariformChecker => c.verify(file, c.level, ast.ast, lines)
case c: CombinedChecker => c.verify(file, c.level, CombinedAst(ast.ast, lines), lines)
case _ => Nil
}
.filter(m => CommentFilter.filterApplies(m, commentFilters))
}

def verifyFile[T <: FileSpec](
configuration: ScalastyleConfiguration,
classes: List[ConfigurationChecker],
checks: List[Checker[_]],
file: T
): List[Message[T]] = {
try {
val s = file match {
case fs: RealFileSpec => readFile(fs.name, fs.encoding)
case ss: SourceSpec => ss.contents
}
verifySource(configuration, classes, file, s)
verifySource(configuration, checks, file, s)
} catch {
case e: Exception =>
List(
Expand Down Expand Up @@ -213,6 +220,12 @@ class CheckerUtils(classLoader: Option[ClassLoader] = None) {
}
}

def createChecks(configuration: ScalastyleConfiguration): List[Checker[_]] =
createChecks(configuration.checks.filter(_.enabled))

def createChecks(classes: List[ConfigurationChecker]): List[Checker[_]] =
classes.flatMap(cc => newInstance(cc.className, cc.level, cc.parameters, cc.customMessage, cc.customId))

private def newInstance(
name: String,
level: Level,
Expand Down
12 changes: 6 additions & 6 deletions src/main/scala/org/scalastyle/CommentFilter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import scala.collection.mutable.ListBuffer

import _root_.scalariform.lexer.Comment

case class CommentFilter(id: Option[String], start: Option[LineColumn], end: Option[LineColumn])
case class CommentFilter(id: Option[String], start: Option[Int], end: Option[Int])
case class CommentInter(id: Option[String], position: Int, off: Boolean)

object CommentFilter {
Expand Down Expand Up @@ -63,18 +63,18 @@ object CommentFilter {

val list = ListBuffer[CommentFilter]()
val inMap = new mutable.HashMap[Option[String], Boolean]()
val start = new mutable.HashMap[Option[String], Option[LineColumn]]()
val start = new mutable.HashMap[Option[String], Option[Int]]()

it.foreach { ci =>
(inMap.getOrElse(ci.id, false), ci.off) match {
case (true, false) => // off then on, add a new CommentFilter
list += CommentFilter(ci.id, start.getOrElse(ci.id, None), lines.toLineColumn(ci.position))
list += CommentFilter(ci.id, start.getOrElse(ci.id, None), lines.toLine(ci.position))
inMap.put(ci.id, false)
start.remove(ci.id)
case (true, true) => // off then off, do nothing
case (false, false) => // on then on, do nothing
case (false, true) => // on then off, reset start
start.put(ci.id, lines.toLineColumn(ci.position))
start.put(ci.id, lines.toLine(ci.position))
inMap.put(ci.id, true)
}
}
Expand Down Expand Up @@ -102,8 +102,8 @@ object CommentFilter {
else {
val m = se.lineNumber.get
(cf.start, cf.end) match {
case (Some(s), Some(e)) => m >= s.line && m < e.line
case (Some(s), None) => m >= s.line
case (Some(s), Some(e)) => m >= s && m < e
case (Some(s), None) => m >= s
case (None, Some(_)) => false // we just have an :off, filter doesn't apply
case (None, None) => false // this isn't possible, say it doesn't apply
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,18 +101,18 @@ abstract class AbstractMethodChecker extends ScalariformChecker {
val it = for {
t <- localvisit(ast.immediateChildren.head)
f <- traverse(t)
if matches(f)
} yield PositionError(f.position.get, params(f))
p <- matches(f)
} yield PositionError(p, params(f))

it.toList
it
}

private def traverse(t: BaseClazz[AstNode]): ListType = {
val l = t.subs.flatMap(traverse)
if (matches(t)) t :: l else l
if (matches(t).isDefined) t :: l else l
}

def matches(t: BaseClazz[AstNode]): Boolean
def matches(t: BaseClazz[AstNode]): Option[Int]

protected def getParams(p: ParamClauses): List[Param] =
p.paramClausesAndNewlines
Expand All @@ -132,10 +132,8 @@ abstract class AbstractMethodChecker extends ScalariformChecker {
protected def getParamTypes(pc: ParamClauses): List[String] =
getParams(pc).map(p => typename(p.paramTypeOpt.get._2))

protected def matchFunDefOrDcl(t: BaseClazz[AstNode], fn: FunDefOrDcl => Boolean): Boolean =
t match {
case f: FunDefOrDclClazz => fn(f.t); case _ => false
}
protected def matchFunDefOrDcl(t: BaseClazz[AstNode], fn: FunDefOrDcl => Boolean): Option[Int] =
t.subs.collectFirst { case f: FunDefOrDclClazz if fn(f.t) => f.t.nameToken.offset }

protected def methodMatch(name: String, paramTypesMatch: List[String] => Boolean)(t: FunDefOrDcl): Boolean =
t.nameToken.text == name && paramTypesMatch(getParamTypes(t.paramClauses))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,16 @@
package org.scalastyle.scalariform

import org.scalastyle.Checker

import scalariform.parser.AstNode
import scalariform.parser.FunDefOrDcl

class CovariantEqualsChecker extends AbstractMethodChecker {
val errorKey = "covariant.equals"

def matches(t: BaseClazz[AstNode]): Boolean = {
val equalsObject = t.subs.exists(matchFunDefOrDcl(_, isEqualsObject))
val equalsOther = t.subs.exists(matchFunDefOrDcl(_, isEqualsOther))

!equalsObject && equalsOther
def matches(t: BaseClazz[AstNode]): Option[Int] = {
val equalsObject = matchFunDefOrDcl(t, isEqualsObject).isEmpty
if (equalsObject) matchFunDefOrDcl(t, isEqualsOther) else None
}

private def isEqualsOther(t: FunDefOrDcl): Boolean =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package org.scalastyle.scalariform

import _root_.scalariform.lexer.Token
import _root_.scalariform.lexer.Tokens.CASE
import _root_.scalariform.lexer.Tokens.DO
import _root_.scalariform.lexer.Tokens.FOR
import _root_.scalariform.lexer.Tokens.IF
import _root_.scalariform.lexer.Tokens.MATCH
Expand All @@ -39,7 +38,7 @@ class CyclomaticComplexityChecker extends CombinedChecker {
val DefaultCountCases = true
private lazy val maximum = getInt("maximum", DefaultMaximum)
private lazy val countCases = getBoolean("countCases", DefaultCountCases)
private val defaultTokens = Set(IF, WHILE, DO, FOR)
private val defaultTokens = Set(IF, WHILE, FOR)

case class FunDefOrDclClazz(t: FunDefOrDcl, position: Option[Int], subs: List[FunDefOrDclClazz])
extends Clazz[FunDefOrDcl]()
Expand All @@ -50,7 +49,7 @@ class CyclomaticComplexityChecker extends CombinedChecker {
f <- traverse(t)
value = matches(f, ast.lines, maximum)
if value > maximum
} yield PositionError(t.position.get, List("" + value, "" + maximum))
} yield PositionError(f.position.get, List("" + value, "" + maximum))

it
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class EmptyInterpolatedStringChecker extends ScalariformChecker {
List(left, right) <- ast.tokens.sliding(2)
if left.tokenType == INTERPOLATION_ID && typesSupportingVariables.contains(left.text) &&
interpolationRegex.findFirstIn(right.text).isEmpty
} yield PositionError(right.offset)
} yield PositionError(left.offset)

it.toList
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@ import scalariform.parser.FunDefOrDcl
class EqualsHashCodeChecker extends AbstractMethodChecker {
val errorKey = "equals.hash.code"

def matches(t: BaseClazz[AstNode]): Boolean = {
val hc = t.subs.exists(matchFunDefOrDcl(_, isHashCode))
val eq = t.subs.exists(matchFunDefOrDcl(_, isEqualsObject))
def matches(t: BaseClazz[AstNode]): Option[Int] = {
val hc = matchFunDefOrDcl(t, isHashCode)
val eq = matchFunDefOrDcl(t, isEqualsObject)

(hc && !eq) || (!hc && eq)
if (hc.isDefined) {
if (eq.isDefined) None else hc
} else {
if (eq.isDefined) eq else None
}
}

private def isHashCode(t: FunDefOrDcl): Boolean = methodMatch("hashCode", noParameter() _)(t)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ class ForBraceChecker extends CombinedChecker {
private def validSingleLine(t: ForExpr, lines: Lines) = {
singleLineAllowed && {
val singleLine = for {
start <- lines.toLineColumn(t.forToken.offset)
end <- lines.toLineColumn(t.tokens.last.offset)
if start.line == end.line
start <- lines.toLine(t.forToken.offset)
end <- lines.toLine(t.tokens.last.offset)
if start == end
} yield ()

singleLine.isDefined
Expand Down
11 changes: 5 additions & 6 deletions src/main/scala/org/scalastyle/scalariform/IfBraceChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import _root_.scalariform.parser.Expr
import _root_.scalariform.parser.IfExpr
import org.scalastyle.CombinedAst
import org.scalastyle.CombinedChecker
import org.scalastyle.LineColumn
import org.scalastyle.Lines
import org.scalastyle.PositionError
import org.scalastyle.ScalastyleError
Expand Down Expand Up @@ -69,11 +68,11 @@ class IfBraceChecker extends CombinedChecker {
singleLineAllowed: Boolean,
doubleLineAllowed: Boolean
): Boolean = {
val ifLine = lines.toLineColumn(t.t.ifToken.offset)
val ifLine = lines.toLine(t.t.ifToken.offset)
val ifBodyLine = firstLineOfGeneralTokens(t.t.body, lines)

val (elseLine, elseBodyLine) = t.t.elseClause match {
case Some(e) => (lines.toLineColumn(e.elseToken.offset), firstLineOfGeneralTokens(e.elseBody, lines))
case Some(e) => (lines.toLine(e.elseToken.offset), firstLineOfGeneralTokens(e.elseBody, lines))
case None => (None, None)
}

Expand All @@ -90,9 +89,9 @@ class IfBraceChecker extends CombinedChecker {
}
}

private[this] def sameLine(l1: Option[LineColumn], l2: Option[LineColumn]) =
private[this] def sameLine(l1: Option[Int], l2: Option[Int]) =
(l1, l2) match {
case (Some(x), Some(y)) => x.line == y.line
case (Some(x), Some(y)) => x == y
case _ => true
}

Expand All @@ -102,7 +101,7 @@ class IfBraceChecker extends CombinedChecker {
body.contents.head match {
case e: BlockExpr => None
case e: IfExpr => None
case e: Any => lines.toLineColumn(e.tokens.head.offset)
case e: Any => lines.toLine(e.tokens.head.offset)
}
} else
None
Expand Down
16 changes: 8 additions & 8 deletions src/main/scala/org/scalastyle/scalariform/ImportsChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -213,13 +213,13 @@ class ImportOrderChecker extends ScalariformChecker {
val CompilationUnit(statements, _) = ast
statements.immediateChildren.flatMap { n =>
val result = n match {
case ImportClause(_, BlockImportExpr(prefix, selectors), _, _) =>
case ImportClause(_, e @ BlockImportExpr(prefix, selectors), _, _) =>
val text = exprToText(prefix.contents, if (lexicographic) selectors.tokens else Nil)
checkImport(text, n.firstToken.offset) ++ checkSelectors(selectors)
checkImport(text, e.firstToken.offset) ++ checkSelectors(selectors)

case ImportClause(_, Expr(contents), _, _) =>
case ImportClause(_, e @ Expr(contents), _, _) =>
val text = exprToText(contents)
checkImport(text, n.firstToken.offset)
checkImport(text, e.firstToken.offset)

case _ =>
Nil
Expand Down Expand Up @@ -281,13 +281,13 @@ class ImportOrderChecker extends ScalariformChecker {
val ImportSelectors(_, first, others, _) = selectors

val errors = new ListBuffer[ScalastyleError]()
val names = Seq(first.contents.head.tokens.head.text) ++
others.map(_._2.contents.head.tokens.head.text)
val names = Seq(first.contents.head.tokens.head) ++
others.map(_._2.contents.head.tokens.head)

if (names.size > 1) {
names.sliding(2).foreach { case Seq(left, right) =>
if (compareNames(left, right, isImport = false) > 0)
errors += newError(selectors.firstToken.offset, "wrongOrderInSelector", right, left)
if (compareNames(left.text, right.text, isImport = false) > 0)
errors += newError(left.offset, "wrongOrderInSelector", right.text, left.text)
}
}

Expand Down
Loading