Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StyleMap: refactor checking basic/complex literals #4019

Merged
merged 2 commits into from
May 19, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 53 additions & 48 deletions scalafmt-core/shared/src/main/scala/org/scalafmt/util/StyleMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ import org.scalameta.FileLine
import org.scalameta.logger
import scala.meta._
import scala.meta.tokens.Token
import scala.meta.tokens.Token.Comment
import scala.meta.tokens.Token.LeftParen
import scala.meta.tokens.Token.RightParen

import scala.annotation.tailrec
import scala.collection.mutable
Expand All @@ -30,20 +27,20 @@ class StyleMap(tokens: FormatTokens, val init: ScalafmtConfig) {
styleBuilder += init
val disableBinPack = mutable.Map.empty[Token, BinPack.Site]
def warn(err: String)(implicit fileLine: FileLine): Unit = logger.elem(err)
tokens.arr.foreach { tok =>
tokens.arr.foreach { ft =>
def changeStyle(style: ScalafmtConfig): Option[ScalafmtConfig] = {
val changing = curr != style
if (!changing) None
else {
startBuilder += tok.left.start
startBuilder += ft.left.start
styleBuilder += style
val prev = curr
curr = style
Some(prev)
}
}
tok.left match {
case Comment(c) if prefix.matcher(c).find() =>
ft.left match {
case Token.Comment(c) if prefix.matcher(c).find() =>
val configured = ScalafmtConfig
.fromHoconString(c, init, Some("scalafmt"))
// TODO(olafur) report error via callback
Expand All @@ -54,28 +51,24 @@ class StyleMap(tokens: FormatTokens, val init: ScalafmtConfig) {
}
changeStyle(style)
}
case open @ LeftParen()
case tok: Token.LeftParen
if curr.binPack.literalArgumentLists &&
opensLiteralArgumentList(tok)(curr) =>
forcedBinPack += tok.meta.leftOwner
opensLiteralArgumentList(ft)(curr) =>
forcedBinPack += ft.meta.leftOwner
changeStyle(setBinPack(curr, callSite = BinPack.Site.Always))
.foreach { x =>
tokens.matchingOpt(open)
tokens.matchingOpt(tok)
.foreach(disableBinPack.update(_, x.binPack.callSite))
}
case close @ RightParen() => disableBinPack.remove(close).foreach { x =>
changeStyle(setBinPack(curr, callSite = x))
}
case tok: Token.RightParen => disableBinPack.remove(tok)
.foreach(x => changeStyle(setBinPack(curr, callSite = x)))
case _ =>
}
}
(startBuilder.result(), styleBuilder.result())
}

@tailrec
private def isBasicLiteral(
tree: Tree,
)(implicit style: ScalafmtConfig): Boolean = tree match {
private def isBasicLiteral(tree: Tree): Boolean = tree match {
case lit: Lit =>
val strName = tree match {
case t: Lit.Int
Expand All @@ -86,40 +79,43 @@ class StyleMap(tokens: FormatTokens, val init: ScalafmtConfig) {
}
literalR.matches(strName)
case x: Name => literalR.matches(x.productPrefix)
case _ if !style.binPack.literalsIncludeSimpleExpr => false
case t: Term.Select => isBasicLiteral(t.qual)
case t: Term.Assign => isBasicLiteral(t.rhs)
case _ => tree.children match {
case _ => false
}

@tailrec
private def isSimpleLiteral(tree: Tree): Boolean = tree match {
case t: Term.Select => isSimpleLiteral(t.qual)
case t: Term.Assign => isSimpleLiteral(t.rhs)
case _ => isBasicLiteral(tree) ||
(tree.children match {
case Nil => true
case one :: Nil => isBasicLiteral(one)
case one :: Nil => isSimpleLiteral(one)
case _ => false
}
})
}

@tailrec
private def isLiteral(tree: Tree)(implicit style: ScalafmtConfig): Boolean =
isBasicLiteral(tree) ||
style.binPack.literalsIncludeSimpleExpr &&
(tree match {
case t: Term.Assign => isLiteral(t.rhs)
case t: Term.Apply => isBasicLiteral(t.fun) &&
(t.argClause match {
case Term.ArgClause(Nil, None) => true
case Term.ArgClause(arg :: Nil, None) => isLiteral(arg)
case _ => false
})
case Term.New(t) => isBasicLiteral(t.name) &&
(t.argClauses match {
case Nil | Term.ArgClause(Nil, None) :: Nil => true
case Term.ArgClause(arg :: Nil, None) :: Nil => isLiteral(arg)
case _ => false
})
case _ => tree.children match {
case Nil => true
case one :: Nil => isLiteral(one)
case _ => false
}
private def isComplexLiteral(tree: Tree): Boolean = tree match {
case t: Term.Assign => isComplexLiteral(t.rhs)
case t: Term.Apply => isSimpleLiteral(t.fun) &&
(t.argClause match {
case Term.ArgClause(Nil, None) => true
case Term.ArgClause(arg :: Nil, None) => isComplexLiteral(arg)
case _ => false
})
case Term.New(t) => isSimpleLiteral(t.name) &&
(t.argClauses match {
case Nil | Term.ArgClause(Nil, None) :: Nil => true
case Term.ArgClause(arg :: Nil, None) :: Nil => isComplexLiteral(arg)
case _ => false
})
case _ => isSimpleLiteral(tree) ||
(tree.children match {
case Nil => true
case one :: Nil => isComplexLiteral(one)
case _ => false
})
}

def opensLiteralArgumentList(
ft: FormatToken,
Expand All @@ -128,8 +124,17 @@ class StyleMap(tokens: FormatTokens, val init: ScalafmtConfig) {
case Member.SyntaxValuesClause(v) => Some(v)
case _ => None
}).exists { args =>
args.lengthCompare(style.binPack.literalsMinArgCount) >= 0 &&
args.forall(isLiteral)
implicit val pred: Tree => Boolean =
if (style.binPack.literalsIncludeSimpleExpr) isComplexLiteral
else isBasicLiteral
@tailrec
def iter(rest: List[Tree], cnt: Int)(implicit
pred: Tree => Boolean,
): Boolean = rest match {
case head :: tail => pred(head) && iter(tail, cnt + 1)
case Nil => cnt >= style.binPack.literalsMinArgCount
}
iter(args, 0)
}

@inline
Expand Down
Loading