@@ -130,7 +130,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
130130
131131 override def toText (tp : Type ): Text = controlled {
132132 def toTextTuple (args : List [Type ]): Text =
133- " (" ~ Text (args.map(argText), " , " ) ~ " )"
133+ " (" ~ argsText (args) ~ " )"
134134
135135 def toTextFunction (args : List [Type ], isImplicit : Boolean , isErased : Boolean ): Text =
136136 changePrec(GlobalPrec ) {
@@ -155,17 +155,24 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
155155 case _ => false
156156 }
157157
158- def toTextInfixType (op : Type , args : List [Type ]): Text = {
159- /* SLS 3.2.8: all infix types have the same precedence.
160- * In A op B op' C, op and op' need the same associativity.
161- * Therefore, if op is left associative, anything on its right
162- * needs to be parenthesized if it's an infix type, and vice versa. */
163- val l :: r :: Nil = args
164- val isRightAssoc = op.typeSymbol.name.endsWith(" :" )
165- val leftArg = if (isRightAssoc && isInfixType(l)) " (" ~ argText(l) ~ " )" else argText(l)
166- val rightArg = if (! isRightAssoc && isInfixType(r)) " (" ~ argText(r) ~ " )" else argText(r)
167-
168- leftArg ~ " " ~ simpleNameString(op.classSymbol) ~ " " ~ rightArg
158+ def tyconName (tp : Type ): Name = tp.typeSymbol.name
159+ def checkAssocMismatch (tp : Type , isRightAssoc : Boolean ) = tp match {
160+ case AppliedType (tycon, _) => isInfixType(tp) && tyconName(tycon).endsWith(" :" ) != isRightAssoc
161+ case AndType (_, _) => isRightAssoc
162+ case OrType (_, _) => isRightAssoc
163+ case _ => false
164+ }
165+
166+ def toTextInfixType (opName : Name , l : Type , r : Type )(op : => Text ): Text = {
167+ val isRightAssoc = opName.endsWith(" :" )
168+ val opPrec = parsing.precedence(opName)
169+
170+ changePrec(opPrec) {
171+ val leftPrec = if (isRightAssoc || checkAssocMismatch(l, isRightAssoc)) opPrec + 1 else opPrec
172+ val rightPrec = if (! isRightAssoc || checkAssocMismatch(r, isRightAssoc)) opPrec + 1 else opPrec
173+
174+ atPrec(leftPrec) { argText(l) } ~ " " ~ op ~ " " ~ atPrec(rightPrec) { argText(r) }
175+ }
169176 }
170177
171178 homogenize(tp) match {
@@ -174,7 +181,20 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
174181 if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ " *"
175182 if (defn.isFunctionClass(cls)) return toTextFunction(args, cls.name.isImplicitFunction, cls.name.isErasedFunction)
176183 if (defn.isTupleClass(cls)) return toTextTuple(args)
177- if (isInfixType(tp)) return toTextInfixType(tycon, args)
184+ if (isInfixType(tp)) {
185+ val l :: r :: Nil = args
186+ val opName = tyconName(tycon)
187+
188+ return toTextInfixType(tyconName(tycon), l, r) { simpleNameString(tycon.typeSymbol) }
189+ }
190+
191+ // Since RefinedPrinter, unlike PlainPrinter, can output right-associative type-operators, we must override handling
192+ // of AndType and OrType to account for associativity
193+ case AndType (tp1, tp2) =>
194+ return toTextInfixType(tpnme.raw.AMP , tp1, tp2) { toText(tpnme.raw.AMP ) }
195+ case OrType (tp1, tp2) =>
196+ return toTextInfixType(tpnme.raw.BAR , tp1, tp2) { toText(tpnme.raw.BAR ) }
197+
178198 case EtaExpansion (tycon) =>
179199 return toText(tycon)
180200 case tp : RefinedType if defn.isFunctionType(tp) =>
@@ -201,7 +221,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
201221 // (they don't need to because we keep the original type tree with
202222 // the original annotation anyway. Therefore, there will always be
203223 // one version of the annotation tree that has the correct positions).
204- withoutPos(super .toText(tp))
224+ return withoutPos(super .toText(tp))
205225 case tp : SelectionProto =>
206226 return " ?{ " ~ toText(tp.name) ~
207227 (Str (" " ) provided ! tp.name.toSimpleName.last.isLetterOrDigit) ~
@@ -375,9 +395,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
375395 case SingletonTypeTree (ref) =>
376396 toTextLocal(ref) ~ " ." ~ keywordStr(" type" )
377397 case AndTypeTree (l, r) =>
378- changePrec(AndPrec ) { toText(l) ~ " & " ~ toText(r) }
398+ changePrec(AndTypePrec ) { toText(l) ~ " & " ~ atPrec( AndTypePrec + 1 ) { toText(r) } }
379399 case OrTypeTree (l, r) =>
380- changePrec(OrPrec ) { toText(l) ~ " | " ~ toText(r) }
400+ changePrec(OrTypePrec ) { toText(l) ~ " | " ~ atPrec( OrTypePrec + 1 ) { toText(r) } }
381401 case RefinedTypeTree (tpt, refines) =>
382402 toTextLocal(tpt) ~ " " ~ blockText(refines)
383403 case AppliedTypeTree (tpt, args) =>
0 commit comments