@@ -124,6 +124,28 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
124124 toTextTuple(args.init)
125125 (" implicit " provided isImplicit) ~ argStr ~ " => " ~ argText(args.last)
126126 }
127+
128+ def isInfixType (tp : Type ): Boolean = tp match {
129+ case AppliedType (tycon, args) =>
130+ args.length == 2 &&
131+ ! Character .isUnicodeIdentifierStart(tycon.typeSymbol.name.toString.head)
132+ // TODO: Once we use the 2.12 stdlib, also check the @showAsInfix annotation
133+ case _ =>
134+ false
135+ }
136+ def toTextInfixType (op : Type , args : List [Type ]): Text = {
137+ /* SLS 3.2.8: all infix types have the same precedence.
138+ * In A op B op' C, op and op' need the same associativity.
139+ * Therefore, if op is left associative, anything on its right
140+ * needs to be parenthesized if it's an infix type, and vice versa. */
141+ val l :: r :: Nil = args
142+ val isRightAssoc = op.typeSymbol.name.endsWith(" :" )
143+ val leftArg = if (isRightAssoc && isInfixType(l)) " (" ~ toText(l) ~ " )" else toText(l)
144+ val rightArg = if (! isRightAssoc && isInfixType(r)) " (" ~ toText(r) ~ " )" else toText(r)
145+
146+ leftArg ~ " " ~ toTextLocal(op) ~ " " ~ rightArg
147+ }
148+
127149 homogenize(tp) match {
128150 case x : ConstantType if homogenizedView =>
129151 return toText(x.widen)
@@ -132,6 +154,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
132154 if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ " *"
133155 if (defn.isFunctionClass(cls)) return toTextFunction(args, cls.name.isImplicitFunction)
134156 if (defn.isTupleClass(cls)) return toTextTuple(args)
157+ if (isInfixType(tp)) return toTextInfixType(tycon, args)
135158 return (toTextLocal(tycon) ~ " [" ~ Text (args map argText, " , " ) ~ " ]" ).close
136159 case tp : TypeRef =>
137160 val hideType = ! ctx.settings.debugAlias.value && (tp.symbol.isAliasPreferred)
0 commit comments