Skip to content

Better positions for infix operations #1941

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

Merged
merged 2 commits into from
Feb 8, 2017
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ object desugar {
for (i <- 0 until arity if nme.selectorName(i) `ne` caseParams(i).name)
yield syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeIdent), caseParams(i).name))
def isRepeated(tree: Tree): Boolean = tree match {
case PostfixOp(_, nme.raw.STAR) => true
case PostfixOp(_, Ident(nme.raw.STAR)) => true
case ByNameTypeTree(tree1) => isRepeated(tree1)
case _ => false
}
Expand Down Expand Up @@ -739,23 +739,25 @@ object desugar {

/** Translate infix operation expression left op right
*/
def makeBinop(left: Tree, op: Name, right: Tree): Tree = {
def makeBinop(left: Tree, op: Ident, right: Tree): Tree = {
def assignToNamedArg(arg: Tree) = arg match {
case Assign(Ident(name), rhs) => cpy.NamedArg(arg)(name, rhs)
case _ => arg
}
if (isLeftAssoc(op)) {
if (isLeftAssoc(op.name)) {
val args: List[Tree] = right match {
case Parens(arg) => assignToNamedArg(arg) :: Nil
case Tuple(args) => args mapConserve assignToNamedArg
case _ => right :: Nil
}
Apply(Select(left, op), args)
val selectPos = Position(left.pos.start, op.pos.end, op.pos.start)
Apply(Select(left, op.name).withPos(selectPos), args)
} else {
val x = ctx.freshName().toTermName
val selectPos = Position(op.pos.start, right.pos.end, op.pos.start)
new InfixOpBlock(
ValDef(x, TypeTree(), left).withMods(synthetic),
Apply(Select(right, op), Ident(x)))
Apply(Select(right, op.name).withPos(selectPos), Ident(x).withPos(left.pos)))
}
}

Expand Down Expand Up @@ -956,25 +958,25 @@ object desugar {
Apply(Select(Apply(Ident(nme.StringContext), strs), id), elems)
case InfixOp(l, op, r) =>
if (ctx.mode is Mode.Type)
if (op == tpnme.raw.AMP) AndTypeTree(l, r) // l & r
else if (op == tpnme.raw.BAR) OrTypeTree(l, r) // l | r
else AppliedTypeTree(Ident(op), l :: r :: Nil) // op[l, r]
if (!op.isBackquoted && op.name == tpnme.raw.AMP) AndTypeTree(l, r) // l & r
else if (!op.isBackquoted && op.name == tpnme.raw.BAR) OrTypeTree(l, r) // l | r
else AppliedTypeTree(op, l :: r :: Nil) // op[l, r]
else if (ctx.mode is Mode.Pattern)
Apply(Ident(op), l :: r :: Nil) // op(l, r)
Apply(op, l :: r :: Nil) // op(l, r)
else // l.op(r), or val x = r; l.op(x), plus handle named args specially
makeBinop(l, op, r)
case PostfixOp(t, op) =>
if ((ctx.mode is Mode.Type) && op == nme.raw.STAR) {
if ((ctx.mode is Mode.Type) && !op.isBackquoted && op.name == nme.raw.STAR) {
val seqType = if (ctx.compilationUnit.isJava) defn.ArrayType else defn.SeqType
Annotated(
AppliedTypeTree(ref(seqType), t),
New(ref(defn.RepeatedAnnotType), Nil :: Nil))
} else {
assert(ctx.mode.isExpr || ctx.reporter.hasErrors, ctx.mode)
Select(t, op)
Select(t, op.name)
}
case PrefixOp(op, t) =>
Select(t, nme.UNARY_PREFIX ++ op)
Select(t, nme.UNARY_PREFIX ++ op.name)
case Parens(t) =>
t
case Tuple(ts) =>
Expand Down
5 changes: 5 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,15 @@ object Trees {
extends RefTree[T] {
type ThisTree[-T >: Untyped] = Ident[T]
def qualifier: Tree[T] = genericEmptyTree

/** Is this a `BackquotedIdent` ? */
def isBackquoted: Boolean = false
}

class BackquotedIdent[-T >: Untyped] private[ast] (name: Name)
extends Ident[T](name) {
override def isBackquoted: Boolean = true

override def toString = s"BackquotedIdent($name)"
}

Expand Down
20 changes: 10 additions & 10 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
// ----- Tree cases that exist in untyped form only ------------------

trait OpTree extends Tree {
def op: Name
override def isTerm = op.isTermName
override def isType = op.isTypeName
def op: Ident
override def isTerm = op.name.isTermName
override def isType = op.name.isTypeName
}

/** A typed subtree of an untyped tree needs to be wrapped in a TypedSlice
Expand Down Expand Up @@ -66,9 +66,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
*/
class WildcardFunction(placeholderParams: List[ValDef], body: Tree) extends Function(placeholderParams, body)

case class InfixOp(left: Tree, op: Name, right: Tree) extends OpTree
case class PostfixOp(od: Tree, op: Name) extends OpTree
case class PrefixOp(op: Name, od: Tree) extends OpTree
case class InfixOp(left: Tree, op: Ident, right: Tree) extends OpTree
case class PostfixOp(od: Tree, op: Ident) extends OpTree
case class PrefixOp(op: Ident, od: Tree) extends OpTree
case class Parens(t: Tree) extends ProxyTree {
def forwardTo = t
}
Expand Down Expand Up @@ -357,7 +357,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
* parameter, the reference will be a repeated argument.
*/
def refOfDef(tree: MemberDef)(implicit ctx: Context) = tree match {
case ValDef(_, PostfixOp(_, nme.raw.STAR), _) => repeated(Ident(tree.name))
case ValDef(_, PostfixOp(_, Ident(nme.raw.STAR)), _) => repeated(Ident(tree.name))
case _ => Ident(tree.name)
}

Expand Down Expand Up @@ -409,15 +409,15 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case tree: Function if (args eq tree.args) && (body eq tree.body) => tree
case _ => untpd.Function(args, body).withPos(tree.pos)
}
def InfixOp(tree: Tree)(left: Tree, op: Name, right: Tree) = tree match {
def InfixOp(tree: Tree)(left: Tree, op: Ident, right: Tree) = tree match {
case tree: InfixOp if (left eq tree.left) && (op eq tree.op) && (right eq tree.right) => tree
case _ => untpd.InfixOp(left, op, right).withPos(tree.pos)
}
def PostfixOp(tree: Tree)(od: Tree, op: Name) = tree match {
def PostfixOp(tree: Tree)(od: Tree, op: Ident) = tree match {
case tree: PostfixOp if (od eq tree.od) && (op eq tree.op) => tree
case _ => untpd.PostfixOp(od, op).withPos(tree.pos)
}
def PrefixOp(tree: Tree)(op: Name, od: Tree) = tree match {
def PrefixOp(tree: Tree)(op: Ident, od: Tree) = tree match {
case tree: PrefixOp if (op eq tree.op) && (od eq tree.od) => tree
case _ => untpd.PrefixOp(op, od).withPos(tree.pos)
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ object JavaParsers {
if (in.token == DOTDOTDOT) {
in.nextToken()
t = atPos(t.pos.start) {
PostfixOp(t, nme.raw.STAR)
PostfixOp(t, Ident(nme.raw.STAR))
}
}
atPos(start, in.offset) {
Expand Down
26 changes: 12 additions & 14 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ object Parsers {
import reporting.diagnostic.Message
import reporting.diagnostic.messages._

case class OpInfo(operand: Tree, operator: Name, offset: Offset)
case class OpInfo(operand: Tree, operator: Ident, offset: Offset)

class ParensCounters {
private var parCounts = new Array[Int](lastParen - firstParen)
Expand Down Expand Up @@ -414,18 +414,17 @@ object Parsers {
"left- and right-associative operators with same precedence may not be mixed", offset)

def reduceStack(base: List[OpInfo], top: Tree, prec: Int, leftAssoc: Boolean): Tree = {
if (opStack != base && precedence(opStack.head.operator) == prec)
checkAssoc(opStack.head.offset, opStack.head.operator, leftAssoc)
if (opStack != base && precedence(opStack.head.operator.name) == prec)
checkAssoc(opStack.head.offset, opStack.head.operator.name, leftAssoc)
def recur(top: Tree): Tree = {
if (opStack == base) top
else {
val opInfo = opStack.head
val opPrec = precedence(opInfo.operator)
val opPrec = precedence(opInfo.operator.name)
if (prec < opPrec || leftAssoc && prec == opPrec) {
opStack = opStack.tail
recur {
val opPos = Position(opInfo.offset, opInfo.offset + opInfo.operator.length, opInfo.offset)
atPos(opPos union opInfo.operand.pos union top.pos) {
atPos(opInfo.operator.pos union opInfo.operand.pos union top.pos) {
InfixOp(opInfo.operand, opInfo.operator, top)
}
}
Expand All @@ -449,10 +448,9 @@ object Parsers {
val base = opStack
var top = first
while (isIdent && in.name != notAnOperator) {
val op = if (isType) in.name.toTypeName else in.name
top = reduceStack(base, top, precedence(op), isLeftAssoc(op))
val op = if (isType) typeIdent() else termIdent()
top = reduceStack(base, top, precedence(op.name), isLeftAssoc(op.name))
opStack = OpInfo(top, op, in.offset) :: opStack
ident()
newLineOptWhenFollowing(canStartOperand)
if (maybePostfix && !canStartOperand(in.token)) {
val topInfo = opStack.head
Expand Down Expand Up @@ -870,7 +868,7 @@ object Parsers {
val t = toplevelTyp()
if (isIdent(nme.raw.STAR)) {
in.nextToken()
atPos(startOffset(t)) { PostfixOp(t, nme.raw.STAR) }
atPos(startOffset(t)) { PostfixOp(t, Ident(nme.raw.STAR)) }
} else t
}

Expand Down Expand Up @@ -1189,11 +1187,11 @@ object Parsers {
val prefixExpr = () =>
if (isIdent && nme.raw.isUnary(in.name)) {
val start = in.offset
val name = ident()
if (name == nme.raw.MINUS && isNumericLit)
val op = termIdent()
if (op.name == nme.raw.MINUS && isNumericLit)
simpleExprRest(literal(start), canApply = true)
else
atPos(start) { PrefixOp(name, simpleExpr()) }
atPos(start) { PrefixOp(op, simpleExpr()) }
}
else simpleExpr()

Expand Down Expand Up @@ -1260,7 +1258,7 @@ object Parsers {
val app = atPos(startOffset(t), in.offset) { Apply(t, argumentExprs()) }
simpleExprRest(app, canApply = true)
case USCORE =>
atPos(startOffset(t), in.skipToken()) { PostfixOp(t, nme.WILDCARD) }
atPos(startOffset(t), in.skipToken()) { PostfixOp(t, Ident(nme.WILDCARD)) }
case _ =>
t
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
argsText ~ " => " ~ toText(body)
}
case InfixOp(l, op, r) =>
val opPrec = parsing.precedence(op)
val opPrec = parsing.precedence(op.name)
changePrec(opPrec) { toText(l) ~ " " ~ toText(op) ~ " " ~ toText(r) }
case PostfixOp(l, op) =>
changePrec(InfixPrec) { toText(l) ~ " " ~ toText(op) }
Expand Down
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1077,8 +1077,6 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {

trait MatchTranslator extends TreeMakers with ScalacPatternExpanders {

def isBackquoted(x: Ident) = x.isInstanceOf[BackquotedIdent]

def isVarPattern(pat: Tree): Boolean = pat match {
case x: BackquotedIdent => false
case x: Ident => x.name.isVariableName
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ object EtaExpansion {
ids = ids.init :+ repeated(ids.last)
var body: Tree = Apply(lifted, ids)
mt.resultType match {
case rt: MethodType if !rt.isImplicit => body = PostfixOp(body, nme.WILDCARD)
case rt: MethodType if !rt.isImplicit => body = PostfixOp(body, Ident(nme.WILDCARD))
case _ =>
}
val fn = untpd.Function(params, body)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1440,7 +1440,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit


def typedAsFunction(tree: untpd.PostfixOp, pt: Type)(implicit ctx: Context): Tree = {
val untpd.PostfixOp(qual, nme.WILDCARD) = tree
val untpd.PostfixOp(qual, Ident(nme.WILDCARD)) = tree
val pt1 = if (defn.isFunctionType(pt)) pt else AnyFunctionProto
var res = typed(qual, pt1)
if (pt1.eq(AnyFunctionProto) && !defn.isFunctionClass(res.tpe.classSymbol)) {
Expand Down Expand Up @@ -1541,7 +1541,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case tree: untpd.Annotated => typedAnnotated(tree, pt)
case tree: untpd.TypedSplice => typedTypedSplice(tree)
case tree: untpd.UnApply => typedUnApply(tree, pt)
case tree @ untpd.PostfixOp(qual, nme.WILDCARD) => typedAsFunction(tree, pt)
case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt)
case untpd.EmptyTree => tpd.EmptyTree
case _ => typedUnadapted(desugar(tree), pt)
}
Expand Down
4 changes: 4 additions & 0 deletions tests/pos/backquoted_type_operator.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
object Test {
type `&`[L,R] = L
val x: Int `&` String = 10
}
7 changes: 7 additions & 0 deletions tests/repl/errmsgs.check
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,11 @@ scala> class Foo() { def bar: Int = 1 }; val foo = new Foo(); foo.barr
4 |class Foo() { def bar: Int = 1 }; val foo = new Foo(); foo.barr
| ^^^^^^^^
| value `barr` is not a member of Foo(foo) - did you mean `foo.bar`?
scala> val x: List[Int] = "foo" :: List(1)
-- [E007] Type Mismatch Error: <console> ---------------------------------------
4 |val x: List[Int] = "foo" :: List(1)
| ^^^^^
| found: String($1$)
| required: Int
|
scala> :quit