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

Discourage multi-argument infix syntax: lint applications (x op (a, b)), also lint operator-name definitions #8951

Merged
merged 6 commits into from May 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions build.sbt
Expand Up @@ -374,6 +374,7 @@ lazy val commonSettings = instanceSettings ++ clearSourceAndResourceDirectories
connectInput in run := true,
//scalacOptions in Compile += "-Xlint:-deprecation,-inaccessible,-nonlocal-return,-valpattern,_",
//scalacOptions in Compile ++= Seq("-Xmaxerrs", "5", "-Xmaxwarns", "5"),
scalacOptions in Compile += "-Wconf:cat=unchecked&msg=The outer reference in this type test cannot be checked at run time.:ws",
scalacOptions in Compile in doc ++= Seq(
"-doc-footer", "epfl",
"-diagrams",
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/reflect/macros/compiler/Errors.scala
Expand Up @@ -100,7 +100,7 @@ trait Errors extends Traces {

private def showMeth(pss: List[List[Symbol]], restpe: Type, abbreviate: Boolean, untype: Boolean) = {
def preprocess(tpe: Type) = if (untype) untypeMetalevel(tpe) else tpe
var pssPart = (pss map (ps => ps map (p => p.defStringSeenAs(preprocess(p.info))) mkString ("(", ", ", ")"))).mkString
var pssPart = pss.map(_.map(p => p.defStringSeenAs(preprocess(p.info))).mkString("(", ", ", ")")).mkString
if (abbreviate) pssPart = abbreviateCoreAliases(pssPart)
var retPart = preprocess(restpe).toString
if (abbreviate || macroDdef.tpt.tpe == null) retPart = abbreviateCoreAliases(retPart)
Expand Down
1 change: 1 addition & 0 deletions src/compiler/scala/reflect/reify/utils/SymbolTables.scala
Expand Up @@ -58,6 +58,7 @@ trait SymbolTables {
case None => EmptyTree
}

@deprecated("use add instead", since="2.13.3")
def +(sym: Symbol, name: TermName, reification: Tree): SymbolTable = add(sym, name, reification)
def +(symDef: Tree): SymbolTable = add(symDef)
def ++(symDefs: IterableOnce[Tree]): SymbolTable = symDefs.iterator.foldLeft(this)((symtab, symDef) => symtab.add(symDef))
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/scala/tools/nsc/Reporting.scala
Expand Up @@ -229,7 +229,7 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio
)
reportedFeature += featureTrait

val msg = s"$featureDesc $req be enabled\nby making the implicit value $fqname visible.$explain" replace ("#", construct)
val msg = s"$featureDesc $req be enabled\nby making the implicit value $fqname visible.$explain".replace("#", construct)
// maybe pos.source.file.file.getParentFile.getName or Path(source.file.file).parent.name
def parentFileName(source: internal.util.SourceFile) =
Option(java.nio.file.Paths.get(source.path).getParent).map(_.getFileName.toString)
Expand Down Expand Up @@ -382,6 +382,7 @@ object Reporting {
object LintBynameImplicit extends Lint; add(LintBynameImplicit)
object LintRecurseWithDefault extends Lint; add(LintRecurseWithDefault)
object LintUnitSpecialization extends Lint; add(LintUnitSpecialization)
object LintMultiargInfix extends Lint; add(LintMultiargInfix)

sealed trait Feature extends WarningCategory { override def summaryCategory: WarningCategory = Feature }
object Feature extends Feature { override def includes(o: WarningCategory): Boolean = o.isInstanceOf[Feature] }; add(Feature)
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/scala/tools/nsc/ast/TreeDSL.scala
Expand Up @@ -13,6 +13,7 @@
package scala.tools.nsc
package ast

import scala.annotation.unused
import scala.language.implicitConversions

/** A DSL for generating scala code. The goal is that the
Expand Down Expand Up @@ -82,9 +83,13 @@ trait TreeDSL {
def INT_- (other: Tree) = fn(target, getMember(IntClass, nme.MINUS), other)

// generic operations on ByteClass, IntClass, LongClass
@unused("avoid warning for multiple parameters")
def GEN_| (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.OR), other)
@unused("avoid warning for multiple parameters")
def GEN_& (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.AND), other)
@unused("avoid warning for multiple parameters")
def GEN_== (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.EQ), other)
@unused("avoid warning for multiple parameters")
def GEN_!= (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.NE), other)

/** Apply, Select, Match **/
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/ast/TreeGen.scala
Expand Up @@ -364,7 +364,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
}

def expandFunction(localTyper: analyzer.Typer)(fun: Function, inConstructorFlag: Long): Tree = {
val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag)
val anonClass = fun.symbol.owner.newAnonymousFunctionClass(fun.pos, inConstructorFlag)
val parents = if (isFunctionType(fun.tpe)) {
anonClass addAnnotation SerialVersionUIDAnnotation
addSerializable(abstractFunctionType(fun.vparams.map(_.symbol.tpe), fun.body.tpe.deconst))
Expand Down
58 changes: 30 additions & 28 deletions src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
Expand Up @@ -16,12 +16,11 @@
package scala.tools.nsc
package ast.parser

import scala.collection.mutable
import mutable.ListBuffer
import scala.reflect.internal.{Precedence, ModifierFlags => Flags}
import scala.annotation.tailrec
import scala.collection.mutable, mutable.ListBuffer
import scala.reflect.internal.{ModifierFlags => Flags, Precedence}
import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Position, SourceFile}
import Tokens._
import scala.annotation.tailrec
import scala.tools.nsc.Reporting.WarningCategory

/** Historical note: JavaParsers started life as a direct copy of Parsers
Expand Down Expand Up @@ -868,11 +867,18 @@ self =>
}
}
}
def mkNamed(args: List[Tree]) = if (isExpr) args map treeInfo.assignmentToMaybeNamedArg else args
def mkNamed(args: List[Tree]) = if (isExpr) args.map(treeInfo.assignmentToMaybeNamedArg) else args
var isMultiarg = false
val arguments = right match {
case Parens(Nil) => literalUnit :: Nil
case Parens(args) => mkNamed(args)
case _ => right :: Nil
case Parens(Nil) => literalUnit :: Nil
case Parens(args @ (_ :: Nil)) => mkNamed(args)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inner parens are extraneous.

case Parens(args) => isMultiarg = true ; mkNamed(args)
case _ => right :: Nil
}
def mkApply(fun: Tree, args: List[Tree]) = {
val apply = Apply(fun, args)
if (isMultiarg) apply.updateAttachment(MultiargInfixAttachment)
apply
}
if (isExpr) {
if (rightAssoc) {
Expand All @@ -881,15 +887,12 @@ self =>
val liftedArg = atPos(left.pos) {
ValDef(Modifiers(FINAL | SYNTHETIC | ARTIFACT), x, TypeTree(), stripParens(left))
}
Block(
liftedArg :: Nil,
Apply(mkSelection(right), List(Ident(x) setPos left.pos.focus)))
} else {
Apply(mkSelection(left), arguments)
}
} else {
Apply(Ident(op.encode), stripParens(left) :: arguments)
}
val apply = mkApply(mkSelection(right), List(Ident(x) setPos left.pos.focus))
Block(liftedArg :: Nil, apply)
} else
mkApply(mkSelection(left), arguments)
} else
mkApply(Ident(op.encode), stripParens(left) :: arguments)
}

/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
Expand Down Expand Up @@ -2184,7 +2187,7 @@ self =>
private def addMod(mods: Modifiers, mod: Long, pos: Position): Modifiers = {
if (mods hasFlag mod) syntaxError(in.offset, "repeated modifier", skipIt = false)
in.nextToken()
(mods | mod) withPosition (mod, pos)
(mods | mod).withPosition(mod, pos)
}

private def tokenRange(token: TokenData) =
Expand Down Expand Up @@ -2352,13 +2355,12 @@ self =>
warning(in.offset, s"$ttl parameter sections are effectively implicit", WarningCategory.WFlagExtraImplicit)
}
val result = vds.toList
if (owner == nme.CONSTRUCTOR && (result.isEmpty || (result.head take 1 exists (_.mods.isImplicit)))) {
if (owner == nme.CONSTRUCTOR && (result.isEmpty || result.head.take(1).exists(_.mods.isImplicit)))
in.token match {
case LBRACKET => syntaxError(in.offset, "no type parameters allowed here", skipIt = false)
case EOF => incompleteInputError("auxiliary constructor needs non-implicit parameter list")
case _ => syntaxError(start, "auxiliary constructor needs non-implicit parameter list", skipIt = false)
}
}
addEvidenceParams(owner, result, contextBounds)
}

Expand Down Expand Up @@ -2394,7 +2396,7 @@ self =>
if (mods.isLazy) syntaxError("lazy modifier not allowed here. Use call-by-name parameters instead", skipIt = false)
in.token match {
case v @ (VAL | VAR) =>
mods = mods withPosition (in.token.toLong, tokenRange(in))
mods = mods.withPosition(in.token.toLong, tokenRange(in))
if (v == VAR) mods |= Flags.MUTABLE
in.nextToken()
case _ =>
Expand Down Expand Up @@ -2629,13 +2631,13 @@ self =>
syntaxError("lazy not allowed here. Only vals can be lazy", skipIt = false)
in.token match {
case VAL =>
patDefOrDcl(pos, mods withPosition(VAL, tokenRange(in)))
patDefOrDcl(pos, mods.withPosition(VAL, tokenRange(in)))
case VAR =>
patDefOrDcl(pos, (mods | Flags.MUTABLE) withPosition (VAR, tokenRange(in)))
patDefOrDcl(pos, (mods | Flags.MUTABLE).withPosition(VAR, tokenRange(in)))
case DEF =>
List(funDefOrDcl(pos, mods withPosition(DEF, tokenRange(in))))
List(funDefOrDcl(pos, mods.withPosition(DEF, tokenRange(in))))
case TYPE =>
List(typeDefOrDcl(pos, mods withPosition(TYPE, tokenRange(in))))
List(typeDefOrDcl(pos, mods.withPosition(TYPE, tokenRange(in))))
case _ =>
List(tmplDef(pos, mods))
}
Expand Down Expand Up @@ -2888,15 +2890,15 @@ self =>
if (mods.isLazy) syntaxError("classes cannot be lazy", skipIt = false)
in.token match {
case TRAIT =>
classDef(pos, (mods | Flags.TRAIT | Flags.ABSTRACT) withPosition (Flags.TRAIT, tokenRange(in)))
classDef(pos, (mods | Flags.TRAIT | Flags.ABSTRACT).withPosition(Flags.TRAIT, tokenRange(in)))
case CLASS =>
classDef(pos, mods)
case CASECLASS =>
classDef(pos, (mods | Flags.CASE) withPosition (Flags.CASE, tokenRange(in.prev /*scanner skips on 'case' to 'class', thus take prev*/)))
classDef(pos, (mods | Flags.CASE).withPosition(Flags.CASE, tokenRange(in.prev /*scanner skips on 'case' to 'class', thus take prev*/)))
case OBJECT =>
objectDef(pos, mods)
case CASEOBJECT =>
objectDef(pos, (mods | Flags.CASE) withPosition (Flags.CASE, tokenRange(in.prev /*scanner skips on 'case' to 'object', thus take prev*/)))
objectDef(pos, (mods | Flags.CASE).withPosition(Flags.CASE, tokenRange(in.prev /*scanner skips on 'case' to 'object', thus take prev*/)))
case _ =>
syntaxErrorOrIncompleteAnd("expected start of definition", skipIt = true)(
// assume a class definition so as to have somewhere to stash the annotations
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/scala/tools/nsc/settings/Warnings.scala
Expand Up @@ -192,6 +192,7 @@ trait Warnings {
val ByNameImplicit = LintWarning("byname-implicit", "Block adapted by implicit with by-name parameter.")
val RecurseWithDefault = LintWarning("recurse-with-default", "Recursive call used default argument.")
val UnitSpecialization = LintWarning("unit-special", "Warn for specialization of Unit in parameter position.")
val MultiargInfix = LintWarning("multiarg-infix", "Infix operator was defined or used with multiarg operand.")

def allLintWarnings = values.toSeq.asInstanceOf[Seq[LintWarning]]
}
Expand Down Expand Up @@ -223,6 +224,7 @@ trait Warnings {
def warnByNameImplicit = lint contains ByNameImplicit
def warnRecurseWithDefault = lint contains RecurseWithDefault
def unitSpecialization = lint contains UnitSpecialization
def multiargInfix = lint contains MultiargInfix

// The Xlint warning group.
val lint = MultiChoiceSetting(
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala
Expand Up @@ -147,7 +147,7 @@ trait SymbolTrackers {
else {
indicatorString + indent + symString(root) + (
if (children.isEmpty) ""
else children map (c => c.indentString(indent + " ")) mkString ("\n", "\n", "")
else children.map(_.indentString(indent + " ")).mkString("\n", "\n", "")
)
}
}
Expand Down
14 changes: 4 additions & 10 deletions src/compiler/scala/tools/nsc/transform/Constructors.scala
Expand Up @@ -272,16 +272,10 @@ abstract class Constructors extends Statics with Transform with TypingTransforme

closureClass setInfoAndEnter new ClassInfoType(closureParents, newScope, closureClass)

val outerField: TermSymbol = (
closureClass
newValue(nme.OUTER, impl.pos, PrivateLocal | PARAMACCESSOR)
setInfoAndEnter clazz.tpe
)
val applyMethod: MethodSymbol = (
closureClass
newMethod(nme.apply, impl.pos, FINAL)
setInfoAndEnter MethodType(Nil, ObjectTpe)
)
val outerField: TermSymbol =
closureClass.newValue(nme.OUTER, impl.pos, PrivateLocal | PARAMACCESSOR) setInfoAndEnter clazz.tpe
val applyMethod: MethodSymbol =
closureClass.newMethod(nme.apply, impl.pos, FINAL) setInfoAndEnter MethodType(Nil, ObjectTpe)
val outerFieldDef = ValDef(outerField)
val closureClassTyper = localTyper.atOwner(closureClass)
val applyMethodTyper = closureClassTyper.atOwner(applyMethod)
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
Expand Up @@ -138,14 +138,14 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
val resultType = MethodType(List(thisParam), dropNullaryMethod(methodResult))
val selfParamType = singleType(currentOwner.companionModule.thisType, thisParam)

def fixres(tp: Type) = tp substThisAndSym (clazz, selfParamType, clazz.typeParams, tparamsFromClass)
def fixtparam(tp: Type) = tp substSym (clazz.typeParams, tparamsFromClass)
def fixres(tp: Type) = tp.substThisAndSym(clazz, selfParamType, clazz.typeParams, tparamsFromClass)
def fixtparam(tp: Type) = tp.substSym(clazz.typeParams, tparamsFromClass)

// We can't substitute symbols on the entire polytype because we
// need to modify the bounds of the cloned type parameters, but we
// don't want to substitute for the cloned type parameters themselves.
val tparams = tparamsFromMethod ::: tparamsFromClass
tparams foreach (_ modifyInfo fixtparam)
tparams.foreach(_ modifyInfo fixtparam)
GenPolyType(tparams, fixres(resultType))

// For reference, calling fix on the GenPolyType plays out like this:
Expand Down
Expand Up @@ -804,7 +804,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
def overrideIn(clazz: Symbol, sym: Symbol) = {
val newFlags = (sym.flags | OVERRIDE | SPECIALIZED) & ~(DEFERRED | CASEACCESSOR | PARAMACCESSOR)
val sym1 = sym.cloneSymbol(clazz, newFlags)
sym1 modifyInfo (_ asSeenFrom (clazz.tpe, sym1.owner))
sym1.modifyInfo(_.asSeenFrom(clazz.tpe, sym1.owner))
}
val specVal = specializedOverload(sClass, m, env)

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/transform/TailCalls.scala
Expand Up @@ -174,7 +174,7 @@ abstract class TailCalls extends Transform {
val thisParam = method.newSyntheticValueParam(currentClass.typeOfThis)
label setInfo MethodType(thisParam :: method.tpe.params, method.tpe_*.finalResultType)
if (isEligible)
label substInfo (method.tpe.typeParams, tparams)
label.substInfo(method.tpe.typeParams, tparams)

label
}
Expand Down
10 changes: 5 additions & 5 deletions src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
Expand Up @@ -517,7 +517,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {

def resetUniques() = {_nextId = 0; uniques.clear()}
private val uniques = new mutable.HashMap[Tree, Var]
def apply(x: Tree): Var = uniques getOrElseUpdate(x, new Var(x, x.tpe))
def apply(x: Tree): Var = uniques.getOrElseUpdate(x, new Var(x, x.tpe))
def unapply(v: Var) = Some(v.path)
}
class Var(val path: Tree, staticTp: Type) extends AbsVar {
Expand Down Expand Up @@ -575,11 +575,11 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {

// populate equalitySyms
// don't care about the result, but want only one fresh symbol per distinct constant c
def registerEquality(c: Const): Unit = {ensureCanModify(); symForEqualsTo getOrElseUpdate(c, Sym(this, c))}
def registerEquality(c: Const): Unit = { ensureCanModify() ; symForEqualsTo.getOrElseUpdate(c, Sym(this, c)) }

// return the symbol that represents this variable being equal to the constant `c`, if it exists, otherwise False (for robustness)
// (registerEquality(c) must have been called prior, either when constructing the domain or from outside)
def propForEqualsTo(c: Const): Prop = {observed(); symForEqualsTo.getOrElse(c, False)}
def propForEqualsTo(c: Const): Prop = { observed() ; symForEqualsTo.getOrElse(c, False) }

// [implementation NOTE: don't access until all potential equalities have been registered using registerEquality]p
/** the information needed to construct the boolean proposition that encodes the equality proposition (V = C)
Expand Down Expand Up @@ -709,8 +709,8 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {
// don't call until all equalities have been registered and registerNull has been called (if needed)
def describe = {
def domain_s = domain match {
case Some(d) => d mkString (" ::= ", " | ", "// "+ symForEqualsTo.keys)
case _ => symForEqualsTo.keys mkString (" ::= ", " | ", " | ...")
case Some(d) => d.mkString(" ::= ", " | ", "// "+ symForEqualsTo.keys)
case _ => symForEqualsTo.keys.mkString(" ::= ", " | ", " | ...")
}
s"$this: ${staticTp}${domain_s} // = $path"
}
Expand Down
Expand Up @@ -234,14 +234,14 @@ trait MatchApproximation extends TreeAndTypeAnalysis with ScalaLogic with MatchT
private[this] val uniqueTypeProps = new mutable.HashMap[(Tree, Type), Eq]

def uniqueEqualityProp(testedPath: Tree, rhs: Tree): Prop =
uniqueEqualityProps getOrElseUpdate((testedPath, rhs), Eq(Var(testedPath), ValueConst(rhs)))
uniqueEqualityProps.getOrElseUpdate((testedPath, rhs), Eq(Var(testedPath), ValueConst(rhs)))

// overridden in TreeMakersToPropsIgnoreNullChecks
def uniqueNonNullProp (testedPath: Tree): Prop =
uniqueNonNullProps getOrElseUpdate(testedPath, Not(Eq(Var(testedPath), NullConst)))
uniqueNonNullProps.getOrElseUpdate(testedPath, Not(Eq(Var(testedPath), NullConst)))

def uniqueTypeProp(testedPath: Tree, pt: Type): Prop =
uniqueTypeProps getOrElseUpdate((testedPath, pt), Eq(Var(testedPath), TypeConst(checkableType(pt))))
uniqueTypeProps.getOrElseUpdate((testedPath, pt), Eq(Var(testedPath), TypeConst(checkableType(pt))))

// a variable in this set should never be replaced by a tree that "does not consist of a selection on a variable in this set" (intuitively)
private val pointsToBound = mutable.HashSet(root)
Expand Down Expand Up @@ -646,7 +646,7 @@ trait MatchAnalysis extends MatchApproximation {

def varAssignmentString(varAssignment: Map[Var, (Seq[Const], Seq[Const])]) =
varAssignment.toSeq.sortBy(_._1.toString).map { case (v, (trues, falses)) =>
val assignment = "== "+ (trues mkString("(", ", ", ")")) +" != ("+ (falses mkString(", ")) +")"
val assignment = "== "+ trues.mkString("(", ", ", ")") +" != ("+ falses.mkString(", ") +")"
v +"(="+ v.path +": "+ v.staticTpCheckable +") "+ assignment
}.mkString("\n")

Expand Down
Expand Up @@ -294,7 +294,7 @@ trait Interface extends ast.TreeDSL {
}
new Substitution(newFrom.prependToList(other.from), newTo.prependToList(other.to.mapConserve(apply)))
}
override def toString = (from.map(_.name) zip to) mkString("Substitution(", ", ", ")")
override def toString = from.map(_.name).zip(to).mkString("Substitution(", ", ", ")")
}

object EmptySubstitution extends Substitution(Nil, Nil) {
Expand Down
Expand Up @@ -188,7 +188,7 @@ trait NamesDefaults { self: Analyzer =>
blockTyper.context.scope enter sym
val vd = atPos(sym.pos)(ValDef(sym, qual) setType NoType)
// it stays in Vegas: scala/bug#5720, scala/bug#5727
qual changeOwner (blockTyper.context.owner, sym)
qual.changeOwner(blockTyper.context.owner, sym)

val newQual = atPos(qual.pos.focus)(blockTyper.typedQualifier(Ident(sym.name)))
val baseFunTransformed = atPos(baseFun.pos.makeTransparent) {
Expand Down