@@ -155,17 +155,24 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
155
155
case _ => false
156
156
}
157
157
158
- def toTextInfixType (op : Type , args : List [Type ]): Text = changePrec(InfixPrec ) {
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) atPrec(InfixPrec + 1 ) { argText(l) } else argText(l)
166
- val rightArg = if (! isRightAssoc) atPrec(InfixPrec + 1 ) { 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, true )
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
+ }
169
176
}
170
177
171
178
homogenize(tp) match {
@@ -174,7 +181,20 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
174
181
if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ " *"
175
182
if (defn.isFunctionClass(cls)) return toTextFunction(args, cls.name.isImplicitFunction, cls.name.isErasedFunction)
176
183
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.classSymbol) }
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
+
178
198
case EtaExpansion (tycon) =>
179
199
return toText(tycon)
180
200
case tp : RefinedType if defn.isFunctionType(tp) =>
0 commit comments