summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan@lightbend.com>2017-02-16 12:26:51 -0800
committerGitHub <noreply@github.com>2017-02-16 12:26:51 -0800
commite2be7c498dd849a007ab3e2a923fe48c8ae74b94 (patch)
treefad4a611d315302499dee4d1ad033c977b41d0b5
parentc10d25135361e309ddfd71c6806e041e994dc6fd (diff)
parentfab1db5a3854ae737e1d749eb08be9baf41199f5 (diff)
downloadscala-e2be7c498dd849a007ab3e2a923fe48c8ae74b94.tar.gz
scala-e2be7c498dd849a007ab3e2a923fe48c8ae74b94.tar.bz2
scala-e2be7c498dd849a007ab3e2a923fe48c8ae74b94.zip
Merge pull request #5589 from allisonhb/feature/si-4700
SI-4700 The thrilling continuation to the infix type printing saga.
-rw-r--r--src/library/scala/annotation/showAsInfix.scala27
-rw-r--r--src/reflect/scala/reflect/internal/AnnotationInfos.scala5
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala30
-rw-r--r--src/reflect/scala/reflect/runtime/JavaUniverseForce.scala1
-rw-r--r--test/files/run/t4700.check44
-rw-r--r--test/files/run/t4700.scala22
7 files changed, 129 insertions, 2 deletions
diff --git a/src/library/scala/annotation/showAsInfix.scala b/src/library/scala/annotation/showAsInfix.scala
new file mode 100644
index 0000000000..6c25e08efa
--- /dev/null
+++ b/src/library/scala/annotation/showAsInfix.scala
@@ -0,0 +1,27 @@
+package scala.annotation
+
+/**
+ * This annotation configures how Scala prints two-parameter generic types.
+ *
+ * By default, types with symbolic names are printed infix; while types without
+ * them are printed using the regular generic type syntax.
+ *
+ * Example of usage:
+ {{{
+ scala> class Map[T, U]
+ defined class Map
+
+ scala> def foo: Int Map Int = ???
+ foo: Map[Int,Int]
+
+ scala> @showAsInfix class Map[T, U]
+ defined class Map
+
+ scala> def foo: Int Map Int = ???
+ foo: Int Map Int
+ }}}
+ *
+ * @param enabled whether to show this type as an infix type operator.
+ * @since 2.12.2
+ */
+class showAsInfix(enabled: Boolean = true) extends annotation.StaticAnnotation \ No newline at end of file
diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala
index a6e584424b..8ba3e62ac2 100644
--- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala
+++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala
@@ -316,8 +316,9 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
/** Check whether any of the arguments mention a symbol */
def refsSymbol(sym: Symbol) = hasArgWhich(_.symbol == sym)
- def stringArg(index: Int) = constantAtIndex(index) map (_.stringValue)
- def intArg(index: Int) = constantAtIndex(index) map (_.intValue)
+ def stringArg(index: Int) = constantAtIndex(index) map (_.stringValue)
+ def intArg(index: Int) = constantAtIndex(index) map (_.intValue)
+ def booleanArg(index: Int) = constantAtIndex(index) map (_.booleanValue)
def symbolArg(index: Int) = argAtIndex(index) collect {
case Apply(fun, Literal(str) :: Nil) if fun.symbol == definitions.Symbol_apply =>
newTermName(str.stringValue)
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index 19460af27f..7f74deb823 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -1421,6 +1421,8 @@ trait Definitions extends api.StandardDefinitions {
case _ => false
}
+ lazy val ShowAsInfixAnnotationClass = rootMirror.getClassIfDefined("scala.annotation.showAsInfix")
+
// todo: reconcile with javaSignature!!!
def signature(tp: Type): String = {
def erasure(tp: Type): Type = tp match {
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index ad7e3ffe8f..8dc886bfe3 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -309,6 +309,9 @@ trait Types
/** Is this type completed (i.e. not a lazy type)? */
def isComplete: Boolean = true
+ /** Should this be printed as an infix type (@showAsInfix class &&[T, U])? */
+ def isShowAsInfixType: Boolean = false
+
/** If this is a lazy type, assign a new type to `sym`. */
def complete(sym: Symbol) {}
@@ -2102,6 +2105,15 @@ trait Types
trivial = fromBoolean(!sym.isTypeParameter && pre.isTrivial && areTrivialTypes(args))
toBoolean(trivial)
}
+
+ /* It only makes sense to show 2-ary type constructors infix.
+ * By default we do only if it's a symbolic name. */
+ override def isShowAsInfixType: Boolean =
+ hasLength(args, 2) &&
+ sym.getAnnotation(ShowAsInfixAnnotationClass)
+ .map(_ booleanArg 0 getOrElse true)
+ .getOrElse(!Character.isUnicodeIdentifierStart(sym.decodedName.head))
+
private[Types] def invalidateTypeRefCaches(): Unit = {
parentsCache = null
parentsPeriod = NoPeriod
@@ -2327,6 +2339,22 @@ trait Types
case arg :: Nil => s"($arg,)"
case _ => args.mkString("(", ", ", ")")
}
+ private def infixTypeString: String = {
+ /* SLS 3.2.8: all infix types have the same precedence.
+ * In A op B op' C, op and op' need the same associativity.
+ * Therefore, if op is left associative, anything on its right
+ * needs to be parenthesized if it's an infix type, and vice versa. */
+ // we should only get here after `isShowInfixType` says we have 2 args
+ val l :: r :: Nil = args
+
+ val isRightAssoc = typeSymbol.decodedName endsWith ":"
+
+ val lstr = if (isRightAssoc && l.isShowAsInfixType) s"($l)" else l.toString
+
+ val rstr = if (!isRightAssoc && r.isShowAsInfixType) s"($r)" else r.toString
+
+ s"$lstr ${sym.decodedName} $rstr"
+ }
private def customToString = sym match {
case RepeatedParamClass | JavaRepeatedParamClass => args.head + "*"
case ByNameParamClass => "=> " + args.head
@@ -2350,6 +2378,8 @@ trait Types
xs.init.mkString("(", ", ", ")") + " => " + xs.last
}
}
+ else if (isShowAsInfixType)
+ infixTypeString
else if (isTupleTypeDirect(this))
tupleTypeString
else if (sym.isAliasType && prefixChain.exists(_.termSymbol.isSynthetic) && (this ne dealias))
diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
index 95d6662d14..9138ed3f02 100644
--- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
+++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
@@ -429,6 +429,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
definitions.hijackedCoreClasses
definitions.symbolsNotPresentInBytecode
definitions.isPossibleSyntheticParent
+ definitions.ShowAsInfixAnnotationClass
definitions.abbrvTag
definitions.numericWeight
definitions.boxedModule
diff --git a/test/files/run/t4700.check b/test/files/run/t4700.check
new file mode 100644
index 0000000000..ae854b959d
--- /dev/null
+++ b/test/files/run/t4700.check
@@ -0,0 +1,44 @@
+
+scala> import scala.annotation.showAsInfix
+import scala.annotation.showAsInfix
+
+scala> class &&[T,U]
+defined class $amp$amp
+
+scala> def foo: Int && Boolean = ???
+foo: Int && Boolean
+
+scala> def foo: Int && Boolean && String = ???
+foo: Int && Boolean && String
+
+scala> def foo: Int && (Boolean && String) = ???
+foo: Int && (Boolean && String)
+
+scala> @showAsInfix type Mappy[T, U] = Map[T, U]
+defined type alias Mappy
+
+scala> def foo: Int Mappy (Boolean && String) = ???
+foo: Int Mappy (Boolean && String)
+
+scala> @showAsInfix(false) class ||[T,U]
+defined class $bar$bar
+
+scala> def foo: Int || Boolean = ???
+foo: ||[Int,Boolean]
+
+scala> class &:[L, R]
+defined class $amp$colon
+
+scala> def foo: Int &: String = ???
+foo: Int &: String
+
+scala> def foo: Int &: Boolean &: String = ???
+foo: Int &: Boolean &: String
+
+scala> def foo: (Int && String) &: Boolean = ???
+foo: (Int && String) &: Boolean
+
+scala> def foo: Int && (Boolean &: String) = ???
+foo: Int && (Boolean &: String)
+
+scala> :quit
diff --git a/test/files/run/t4700.scala b/test/files/run/t4700.scala
new file mode 100644
index 0000000000..7c02676e89
--- /dev/null
+++ b/test/files/run/t4700.scala
@@ -0,0 +1,22 @@
+import scala.tools.nsc.interpreter._
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ def code = """
+ |import scala.annotation.showAsInfix
+ |class &&[T,U]
+ |def foo: Int && Boolean = ???
+ |def foo: Int && Boolean && String = ???
+ |def foo: Int && (Boolean && String) = ???
+ |@showAsInfix type Mappy[T, U] = Map[T, U]
+ |def foo: Int Mappy (Boolean && String) = ???
+ |@showAsInfix(false) class ||[T,U]
+ |def foo: Int || Boolean = ???
+ |class &:[L, R]
+ |def foo: Int &: String = ???
+ |def foo: Int &: Boolean &: String = ???
+ |def foo: (Int && String) &: Boolean = ???
+ |def foo: Int && (Boolean &: String) = ???
+ |""".stripMargin
+}
+