From 35a46091478bb1c7ba1e3531214a4088b690eaae Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Tue, 9 Mar 2021 16:19:10 +0100 Subject: [PATCH 1/2] Add documentation for intrinsic types --- .../CommentsForIntrinsicsDefinitions.scala | 414 ++++++++++++++++++ .../dotty/tools/dotc/core/Definitions.scala | 4 + .../scaladoc/tasty/SyntheticSupport.scala | 16 +- .../tools/scaladoc/tasty/TastyParser.scala | 28 +- 4 files changed, 451 insertions(+), 11 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/core/CommentsForIntrinsicsDefinitions.scala diff --git a/compiler/src/dotty/tools/dotc/core/CommentsForIntrinsicsDefinitions.scala b/compiler/src/dotty/tools/dotc/core/CommentsForIntrinsicsDefinitions.scala new file mode 100644 index 000000000000..6813a924a4b7 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/core/CommentsForIntrinsicsDefinitions.scala @@ -0,0 +1,414 @@ +package dotty.tools +package dotc +package core + +import scala.annotation.{threadUnsafe => tu} +import Comments.Comment +import util.Spans.NoSpan + +@tu lazy val syntheticSymbolsToComment: Definitions => Map[Symbols.Symbol, Comment] = { (defn: Definitions) => + import defn._ + Map( + AnyClass -> + """/** Class `Any` is the root of the Scala class hierarchy. Every class in a Scala + | * execution environment inherits directly or indirectly from this class. + | * + | * Starting with Scala 2.10 it is possible to directly extend `Any` using ''universal traits''. + | * A ''universal trait'' is a trait that extends `Any`, only has `def`s as members, and does no initialization. + | * + | * The main use case for universal traits is to allow basic inheritance of methods for [[scala.AnyVal value classes]]. + | * For example, + | * + | * {{{ + | * trait Printable extends Any { + | * def print(): Unit = println(this) + | * } + | * class Wrapper(val underlying: Int) extends AnyVal with Printable + | * + | * val w = new Wrapper(3) + | * w.print() + | * }}} + | * + | * See the [[https://docs.scala-lang.org/overviews/core/value-classes.html Value Classes and Universal Traits]] for more + | * details on the interplay of universal traits and value classes. + | */ + """.stripMargin, + + Any_== -> + """/** Test two objects for equality. + | * The expression `x == that` is equivalent to `if (x eq null) that eq null else x.equals(that)`. + | * + | * @param that the object to compare against this object for equality. + | * @return `true` if the receiver object is equivalent to the argument; `false` otherwise. + | */ + """.stripMargin, + + Any_!= -> + """/** Test two objects for inequality. + | * + | * @param that the object to compare against this object for equality. + | * @return `true` if !(this == that), false otherwise. + | */ + """.stripMargin, + + Any_equals -> + """/** Compares the receiver object (`this`) with the argument object (`that`) for equivalence. + | * + | * Any implementation of this method should be an [[https://en.wikipedia.org/wiki/Equivalence_relation equivalence relation]]: + | * + | * - It is reflexive: for any instance `x` of type `Any`, `x.equals(x)` should return `true`. + | * - It is symmetric: for any instances `x` and `y` of type `Any`, `x.equals(y)` should return `true` if and + | * only if `y.equals(x)` returns `true`. + | * - It is transitive: for any instances `x`, `y`, and `z` of type `Any` if `x.equals(y)` returns `true` and + | * `y.equals(z)` returns `true`, then `x.equals(z)` should return `true`. + | * + | * If you override this method, you should verify that your implementation remains an equivalence relation. + | * Additionally, when overriding this method it is usually necessary to override `hashCode` to ensure that + | * objects which are "equal" (`o1.equals(o2)` returns `true`) hash to the same [[scala.Int]]. + | * (`o1.hashCode.equals(o2.hashCode)`). + | * + | * @param that the object to compare against this object for equality. + | * @return `true` if the receiver object is equivalent to the argument; `false` otherwise. + | */ + """.stripMargin, + + Any_hashCode -> + """/** Calculate a hash code value for the object. + | * + | * The default hashing algorithm is platform dependent. + | * + | * Note that it is allowed for two objects to have identical hash codes (`o1.hashCode.equals(o2.hashCode)`) yet + | * not be equal (`o1.equals(o2)` returns `false`). A degenerate implementation could always return `0`. + | * However, it is required that if two objects are equal (`o1.equals(o2)` returns `true`) that they have + | * identical hash codes (`o1.hashCode.equals(o2.hashCode)`). Therefore, when overriding this method, be sure + | * to verify that the behavior is consistent with the `equals` method. + | * + | * @return the hash code value for this object. + | */ + """.stripMargin, + + Any_toString -> + """/** Returns a string representation of the object. + | * + | * The default representation is platform dependent. + | * + | * @return a string representation of the object. + | */ + """.stripMargin, + + Any_## -> + """/** Equivalent to `x.hashCode` except for boxed numeric types and `null`. + | * For numerics, it returns a hash value which is consistent + | * with value equality: if two value type instances compare + | * as true, then ## will produce the same hash value for each + | * of them. + | * For `null` returns a hashcode where `null.hashCode` throws a + | * `NullPointerException`. + | * + | * @return a hash value consistent with == + | */ + """.stripMargin, + + Any_isInstanceOf -> + """/** Test whether the dynamic type of the receiver object is `T0`. + | * + | * Note that the result of the test is modulo Scala's erasure semantics. + | * Therefore the expression `1.isInstanceOf[String]` will return `false`, while the + | * expression `List(1).isInstanceOf[List[String]]` will return `true`. + | * In the latter example, because the type argument is erased as part of compilation it is + | * not possible to check whether the contents of the list are of the specified type. + | * + | * @return `true` if the receiver object is an instance of erasure of type `T0`; `false` otherwise. + | */ + """.stripMargin, + + Any_asInstanceOf -> + """/** Cast the receiver object to be of type `T0`. + | * + | * Note that the success of a cast at runtime is modulo Scala's erasure semantics. + | * Therefore the expression `1.asInstanceOf[String]` will throw a `ClassCastException` at + | * runtime, while the expression `List(1).asInstanceOf[List[String]]` will not. + | * In the latter example, because the type argument is erased as part of compilation it is + | * not possible to check whether the contents of the list are of the requested type. + | * + | * @throws ClassCastException if the receiver object is not an instance of the erasure of type `T0`. + | * @return the receiver object. + | */ + """.stripMargin, + + Any_getClass -> + """/** Returns the runtime class representation of the object. + | * + | * @return a class object corresponding to the runtime type of the receiver. + | */ + """.stripMargin, + + MatchableClass -> + """/** + | * + | * + | * + | * + | * + | * + | * + | */ + """.stripMargin, + + AnyRefAlias -> + """/** Class `AnyRef` is the root class of all ''reference types''. + | * All types except the value types descend from this class. + | * @template + | */ + """.stripMargin, + + Object_eq -> + """/** Tests whether the argument (`that`) is a reference to the receiver object (`this`). + | * + | * The `eq` method implements an [[https://en.wikipedia.org/wiki/Equivalence_relation equivalence relation]] on + | * non-null instances of `AnyRef`, and has three additional properties: + | * + | * - It is consistent: for any non-null instances `x` and `y` of type `AnyRef`, multiple invocations of + | * `x.eq(y)` consistently returns `true` or consistently returns `false`. + | * - For any non-null instance `x` of type `AnyRef`, `x.eq(null)` and `null.eq(x)` returns `false`. + | * - `null.eq(null)` returns `true`. + | * + | * When overriding the `equals` or `hashCode` methods, it is important to ensure that their behavior is + | * consistent with reference equality. Therefore, if two objects are references to each other (`o1 eq o2`), they + | * should be equal to each other (`o1 == o2`) and they should hash to the same value (`o1.hashCode == o2.hashCode`). + | * + | * @param that the object to compare against this object for reference equality. + | * @return `true` if the argument is a reference to the receiver object; `false` otherwise. + | */ + """.stripMargin, + + Object_ne -> + """/** Equivalent to `!(this eq that)`. + | * + | * @param that the object to compare against this object for reference equality. + | * @return `true` if the argument is not a reference to the receiver object; `false` otherwise. + | */ + """.stripMargin, + + Object_synchronized -> + """/** Executes the code in `body` with an exclusive lock on `this`. + | * + | * @param body the code to execute + | * @return the result of `body` + | */ + """.stripMargin, + + Object_clone -> + """/** Create a copy of the receiver object. + | * + | * The default implementation of the `clone` method is platform dependent. + | * + | * @note not specified by SLS as a member of AnyRef + | * @return a copy of the receiver object. + | */ + """.stripMargin, + + Object_finalize -> + """/** Called by the garbage collector on the receiver object when there + | * are no more references to the object. + | * + | * The details of when and if the `finalize` method is invoked, as + | * well as the interaction between `finalize` and non-local returns + | * and exceptions, are all platform dependent. + | * + | * @note not specified by SLS as a member of AnyRef + | */ + """.stripMargin, + + Object_notify -> + """/** Wakes up a single thread that is waiting on the receiver object's monitor. + | * + | * @note not specified by SLS as a member of AnyRef + | */ + """.stripMargin, + + Object_notifyAll -> + """/** Wakes up all threads that are waiting on the receiver object's monitor. + | * + | * @note not specified by SLS as a member of AnyRef + | */ + """.stripMargin, + + Object_wait -> + """/** See [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait--]]. + | * + | * @note not specified by SLS as a member of AnyRef + | */ + """.stripMargin, + + Object_waitL -> + """/** See [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait-long-]]. + | * + | * @param timeout the maximum time to wait in milliseconds. + | * @note not specified by SLS as a member of AnyRef + | */ + """.stripMargin, + + Object_waitLI -> + """/** See [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait-long-int-]] + | * + | * @param timeout the maximum time to wait in milliseconds. + | * @param nanos additional time, in nanoseconds range 0-999999. + | * @note not specified by SLS as a member of AnyRef + | */ + """.stripMargin, + + AnyKindClass -> + """/** + | * + | * + | * + | * + | * + | * + | * + | */ + """.stripMargin, + + andType -> + """/** + | * + | * + | * + | * + | * + | * + | * + | */ + """.stripMargin, + + orType -> + """/** + | * + | * + | * + | * + | * + | * + | * + | */ + """.stripMargin, + + AnyValClass -> + """/** `AnyVal` is the root class of all ''value types'', which describe values + | * not implemented as objects in the underlying host system. Value classes + | * are specified in Scala Language Specification, section 12.2. + | * + | * The standard implementation includes nine `AnyVal` subtypes: + | * + | * [[scala.Double]], [[scala.Float]], [[scala.Long]], [[scala.Int]], [[scala.Char]], + | * [[scala.Short]], and [[scala.Byte]] are the ''numeric value types''. + | * + | * [[scala.Unit]] and [[scala.Boolean]] are the ''non-numeric value types''. + | * + | * Other groupings: + | * + | * - The ''subrange types'' are [[scala.Byte]], [[scala.Short]], and [[scala.Char]]. + | * - The ''integer types'' include the subrange types as well as [[scala.Int]] and [[scala.Long]]. + | * - The ''floating point types'' are [[scala.Float]] and [[scala.Double]]. + | * + | * Prior to Scala 2.10, `AnyVal` was a sealed trait. Beginning with Scala 2.10, + | * however, it is possible to define a subclass of `AnyVal` called a ''user-defined value class'' + | * which is treated specially by the compiler. Properly-defined user value classes provide a way + | * to improve performance on user-defined types by avoiding object allocation at runtime, and by + | * replacing virtual method invocations with static method invocations. + | * + | * User-defined value classes which avoid object allocation... + | * + | * - must have a single `val` parameter that is the underlying runtime representation. + | * - can define `def`s, but no `val`s, `var`s, or nested `traits`s, `class`es or `object`s. + | * - typically extend no other trait apart from `AnyVal`. + | * - cannot be used in type tests or pattern matching. + | * - may not override `equals` or `hashCode` methods. + | * + | * A minimal example: + | * {{{ + | * class Wrapper(val underlying: Int) extends AnyVal { + | * def foo: Wrapper = new Wrapper(underlying * 19) + | * } + | * }}} + | * + | * It's important to note that user-defined value classes are limited, and in some circumstances, + | * still must allocate a value class instance at runtime. These limitations and circumstances are + | * explained in greater detail in the [[https://docs.scala-lang.org/overviews/core/value-classes.html Value Classes and Universal Traits]]. + | */ + """.stripMargin, + + NullClass -> + """/** `Null` is - together with [[scala.Nothing]] - at the bottom of the Scala type hierarchy. + | * + | * `Null` is the type of the `null` literal. It is a subtype of every type + | * except those of value classes. Value classes are subclasses of [[AnyVal]], which includes + | * primitive types such as [[Int]], [[Boolean]], and user-defined value classes. + | * + | * Since `Null` is not a subtype of value types, `null` is not a member of any such type. + | * For instance, it is not possible to assign `null` to a variable of type [[scala.Int]]. + | */ + """.stripMargin, + + NothingClass -> + """/** `Nothing` is - together with [[scala.Null]] - at the bottom of Scala's type hierarchy. + | * + | * `Nothing` is a subtype of every other type (including [[scala.Null]]); there exist + | * ''no instances'' of this type. Although type `Nothing` is uninhabited, it is + | * nevertheless useful in several ways. For instance, the Scala library defines a value + | * [[scala.collection.immutable.Nil]] of type `List[Nothing]`. Because lists are covariant in Scala, + | * this makes [[scala.collection.immutable.Nil]] an instance of `List[T]`, for any element of type `T`. + | * + | * Another usage for Nothing is the return type for methods which never return normally. + | * One example is method error in [[scala.sys]], which always throws an exception. + | */ + """.stripMargin, + + SingletonClass -> + """/** `Singleton` is used by the compiler as a supertype for singleton types. This includes literal types, + | * as they are also singleton types. + | * + | * {{{ + | * scala> object A { val x = 42 } + | * defined object A + | * + | * scala> implicitly[A.type <:< Singleton] + | * res12: A.type <:< Singleton = generalized constraint + | * + | * scala> implicitly[A.x.type <:< Singleton] + | * res13: A.x.type <:< Singleton = generalized constraint + | * + | * scala> implicitly[42 <:< Singleton] + | * res14: 42 <:< Singleton = generalized constraint + | * + | * scala> implicitly[Int <:< Singleton] + | * ^ + | * error: Cannot prove that Int <:< Singleton. + | * }}} + | * + | * `Singleton` has a special meaning when it appears as an upper bound on a formal type + | * parameter. Normally, type inference in Scala widens singleton types to the underlying + | * non-singleton type. When a type parameter has an explicit upper bound of `Singleton`, + | * the compiler infers a singleton type. + | * + | * {{{ + | * scala> def check42[T](x: T)(implicit ev: T =:= 42): T = x + | * check42: [T](x: T)(implicit ev: T =:= 42)T + | * + | * scala> val x1 = check42(42) + | * ^ + | * error: Cannot prove that Int =:= 42. + | * + | * scala> def singleCheck42[T <: Singleton](x: T)(implicit ev: T =:= 42): T = x + | * singleCheck42: [T <: Singleton](x: T)(implicit ev: T =:= 42)T + | * + | * scala> val x2 = singleCheck42(42) + | * x2: Int = 42 + | * }}} + | * + | * See also [[https://docs.scala-lang.org/sips/42.type.html SIP-23 about Literal-based Singleton Types]]. + | */ + """.stripMargin, + ).view.mapValues(Comment(NoSpan, _)).toMap +} diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 916a5132e48e..effa0b93338a 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -11,6 +11,7 @@ import collection.mutable import Denotations.SingleDenotation import util.{SimpleIdentityMap, SourceFile, NoSource} import typer.ImportInfo.RootRef +import Comments.CommentsContext import scala.annotation.tailrec @@ -1718,5 +1719,8 @@ class Definitions { isInitialized = true } + syntheticSymbolsToComment(this).foreach { (s, c) => + ctx.docCtx.get.addDocstring(s, Some(c)) + } } } diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala index 28440b066fa7..2d57da7b047d 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala @@ -83,10 +83,14 @@ trait SyntheticsSupport: import dotty.tools.dotc given dotc.core.Contexts.Context = qctx.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx val classdef = rdef.asInstanceOf[dotc.ast.tpd.TypeDef] - val ref = classdef.symbol.info.asInstanceOf[dotc.core.Types.ClassInfo].appliedRef - val baseTypes: List[(dotc.core.Symbols.Symbol, dotc.core.Types.Type)] = - ref.baseClasses.map(b => b -> ref.baseType(b)) - baseTypes.asInstanceOf[List[(Symbol, TypeRepr)]] + classdef.symbol.info match + case ci: dotc.core.Types.ClassInfo => + val ref = ci.appliedRef + val baseTypes: List[(dotc.core.Symbols.Symbol, dotc.core.Types.Type)] = + ref.baseClasses.map(b => b -> ref.baseType(b)) + baseTypes.asInstanceOf[List[(Symbol, TypeRepr)]] + case _ => + List.empty } def hackExists(using Quotes)(rpos: qctx.reflect.Position) = { @@ -98,7 +102,9 @@ trait SyntheticsSupport: pos.exists } - def getSupertypes(using Quotes)(c: ClassDef) = hackGetSupertypes(c).tail + def getSupertypes(using Quotes)(c: ClassDef) = hackGetSupertypes(c) match + case _ :: tail => tail + case _ => List.empty def typeForClass(c: ClassDef): TypeRepr = import qctx.reflect._ diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala index eadf63c70def..eb6ee395c897 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala @@ -116,6 +116,21 @@ case class ScaladocTastyInspector()(using ctx: DocContext) extends DocTastyInspe topLevels ++= parser.parseRootTree(treeRoot) } + val defn = ctx.compilerContext.definitions + + topLevels ++= Seq( + defn.AnyClass, + defn.MatchableClass, + // defn.AnyRefAlias, + defn.AnyKindClass, + // defn.andType, + // defn.orType, + defn.AnyValClass, + defn.NullClass, + defn.NothingClass, + defn.SingletonClass + ).map(s => "scala" -> parser.parseClasslike(s.asInstanceOf[parser.qctx.reflect.Symbol].tree.asInstanceOf[parser.qctx.reflect.ClassDef])) + def result(): (List[Member], Option[Comment]) = topLevels.clear() @@ -154,12 +169,12 @@ case class TastyParser( report.warning(throwableToString(e), tree.pos) None - def processSymbol[T](sym: Symbol)(op: => T): Option[T] = try Option(op) catch - case t: Throwable => - try report.warning(throwableToString(t), sym.tree.pos) catch - case _: Throwable => - report.warning(s"Failed to process ${sym.fullName}:\n${throwableToString(t)}") - None + // def processSymbol[T](sym: Symbol)(op: => T): Option[T] = try Option(op) catch + // case t: Throwable => + // try report.warning(throwableToString(t), sym.tree.pos) catch + // case _: Throwable => + // report.warning(s"Failed to process ${sym.fullName}:\n${throwableToString(t)}") + // None def parseRootTree(root: Tree): Seq[(String, Member)] = val docs = Seq.newBuilder[(String, Member)] @@ -187,3 +202,4 @@ case class TastyParser( docs.result() + // def parseClasslike(c: ClassDef): Member = parseClasslike(c) From a591808c1da9c4d60c334b770321715d1fbf1207 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Thu, 11 Mar 2021 13:45:50 +0100 Subject: [PATCH 2/2] Automate publishing docs with intrincis types for stdlib --- .../CommentsForIntrinsicsDefinitions.scala | 414 ------------------ .../dotty/tools/dotc/core/Definitions.scala | 390 ++++++++++++++++- project/Build.scala | 4 +- .../src/dotty/tools/scaladoc/Scaladoc.scala | 6 +- .../tools/scaladoc/ScaladocSettings.scala | 3 + .../scaladoc/renderers/WikiDocRenderer.scala | 2 +- .../scaladoc/tasty/ClassLikeSupport.scala | 6 +- .../dotty/tools/scaladoc/tasty/SymOps.scala | 7 +- .../scaladoc/tasty/SyntheticSupport.scala | 16 +- .../tools/scaladoc/tasty/TastyParser.scala | 49 ++- .../src/dotty/tools/scaladoc/util/html.scala | 2 +- 11 files changed, 438 insertions(+), 461 deletions(-) delete mode 100644 compiler/src/dotty/tools/dotc/core/CommentsForIntrinsicsDefinitions.scala diff --git a/compiler/src/dotty/tools/dotc/core/CommentsForIntrinsicsDefinitions.scala b/compiler/src/dotty/tools/dotc/core/CommentsForIntrinsicsDefinitions.scala deleted file mode 100644 index 6813a924a4b7..000000000000 --- a/compiler/src/dotty/tools/dotc/core/CommentsForIntrinsicsDefinitions.scala +++ /dev/null @@ -1,414 +0,0 @@ -package dotty.tools -package dotc -package core - -import scala.annotation.{threadUnsafe => tu} -import Comments.Comment -import util.Spans.NoSpan - -@tu lazy val syntheticSymbolsToComment: Definitions => Map[Symbols.Symbol, Comment] = { (defn: Definitions) => - import defn._ - Map( - AnyClass -> - """/** Class `Any` is the root of the Scala class hierarchy. Every class in a Scala - | * execution environment inherits directly or indirectly from this class. - | * - | * Starting with Scala 2.10 it is possible to directly extend `Any` using ''universal traits''. - | * A ''universal trait'' is a trait that extends `Any`, only has `def`s as members, and does no initialization. - | * - | * The main use case for universal traits is to allow basic inheritance of methods for [[scala.AnyVal value classes]]. - | * For example, - | * - | * {{{ - | * trait Printable extends Any { - | * def print(): Unit = println(this) - | * } - | * class Wrapper(val underlying: Int) extends AnyVal with Printable - | * - | * val w = new Wrapper(3) - | * w.print() - | * }}} - | * - | * See the [[https://docs.scala-lang.org/overviews/core/value-classes.html Value Classes and Universal Traits]] for more - | * details on the interplay of universal traits and value classes. - | */ - """.stripMargin, - - Any_== -> - """/** Test two objects for equality. - | * The expression `x == that` is equivalent to `if (x eq null) that eq null else x.equals(that)`. - | * - | * @param that the object to compare against this object for equality. - | * @return `true` if the receiver object is equivalent to the argument; `false` otherwise. - | */ - """.stripMargin, - - Any_!= -> - """/** Test two objects for inequality. - | * - | * @param that the object to compare against this object for equality. - | * @return `true` if !(this == that), false otherwise. - | */ - """.stripMargin, - - Any_equals -> - """/** Compares the receiver object (`this`) with the argument object (`that`) for equivalence. - | * - | * Any implementation of this method should be an [[https://en.wikipedia.org/wiki/Equivalence_relation equivalence relation]]: - | * - | * - It is reflexive: for any instance `x` of type `Any`, `x.equals(x)` should return `true`. - | * - It is symmetric: for any instances `x` and `y` of type `Any`, `x.equals(y)` should return `true` if and - | * only if `y.equals(x)` returns `true`. - | * - It is transitive: for any instances `x`, `y`, and `z` of type `Any` if `x.equals(y)` returns `true` and - | * `y.equals(z)` returns `true`, then `x.equals(z)` should return `true`. - | * - | * If you override this method, you should verify that your implementation remains an equivalence relation. - | * Additionally, when overriding this method it is usually necessary to override `hashCode` to ensure that - | * objects which are "equal" (`o1.equals(o2)` returns `true`) hash to the same [[scala.Int]]. - | * (`o1.hashCode.equals(o2.hashCode)`). - | * - | * @param that the object to compare against this object for equality. - | * @return `true` if the receiver object is equivalent to the argument; `false` otherwise. - | */ - """.stripMargin, - - Any_hashCode -> - """/** Calculate a hash code value for the object. - | * - | * The default hashing algorithm is platform dependent. - | * - | * Note that it is allowed for two objects to have identical hash codes (`o1.hashCode.equals(o2.hashCode)`) yet - | * not be equal (`o1.equals(o2)` returns `false`). A degenerate implementation could always return `0`. - | * However, it is required that if two objects are equal (`o1.equals(o2)` returns `true`) that they have - | * identical hash codes (`o1.hashCode.equals(o2.hashCode)`). Therefore, when overriding this method, be sure - | * to verify that the behavior is consistent with the `equals` method. - | * - | * @return the hash code value for this object. - | */ - """.stripMargin, - - Any_toString -> - """/** Returns a string representation of the object. - | * - | * The default representation is platform dependent. - | * - | * @return a string representation of the object. - | */ - """.stripMargin, - - Any_## -> - """/** Equivalent to `x.hashCode` except for boxed numeric types and `null`. - | * For numerics, it returns a hash value which is consistent - | * with value equality: if two value type instances compare - | * as true, then ## will produce the same hash value for each - | * of them. - | * For `null` returns a hashcode where `null.hashCode` throws a - | * `NullPointerException`. - | * - | * @return a hash value consistent with == - | */ - """.stripMargin, - - Any_isInstanceOf -> - """/** Test whether the dynamic type of the receiver object is `T0`. - | * - | * Note that the result of the test is modulo Scala's erasure semantics. - | * Therefore the expression `1.isInstanceOf[String]` will return `false`, while the - | * expression `List(1).isInstanceOf[List[String]]` will return `true`. - | * In the latter example, because the type argument is erased as part of compilation it is - | * not possible to check whether the contents of the list are of the specified type. - | * - | * @return `true` if the receiver object is an instance of erasure of type `T0`; `false` otherwise. - | */ - """.stripMargin, - - Any_asInstanceOf -> - """/** Cast the receiver object to be of type `T0`. - | * - | * Note that the success of a cast at runtime is modulo Scala's erasure semantics. - | * Therefore the expression `1.asInstanceOf[String]` will throw a `ClassCastException` at - | * runtime, while the expression `List(1).asInstanceOf[List[String]]` will not. - | * In the latter example, because the type argument is erased as part of compilation it is - | * not possible to check whether the contents of the list are of the requested type. - | * - | * @throws ClassCastException if the receiver object is not an instance of the erasure of type `T0`. - | * @return the receiver object. - | */ - """.stripMargin, - - Any_getClass -> - """/** Returns the runtime class representation of the object. - | * - | * @return a class object corresponding to the runtime type of the receiver. - | */ - """.stripMargin, - - MatchableClass -> - """/** - | * - | * - | * - | * - | * - | * - | * - | */ - """.stripMargin, - - AnyRefAlias -> - """/** Class `AnyRef` is the root class of all ''reference types''. - | * All types except the value types descend from this class. - | * @template - | */ - """.stripMargin, - - Object_eq -> - """/** Tests whether the argument (`that`) is a reference to the receiver object (`this`). - | * - | * The `eq` method implements an [[https://en.wikipedia.org/wiki/Equivalence_relation equivalence relation]] on - | * non-null instances of `AnyRef`, and has three additional properties: - | * - | * - It is consistent: for any non-null instances `x` and `y` of type `AnyRef`, multiple invocations of - | * `x.eq(y)` consistently returns `true` or consistently returns `false`. - | * - For any non-null instance `x` of type `AnyRef`, `x.eq(null)` and `null.eq(x)` returns `false`. - | * - `null.eq(null)` returns `true`. - | * - | * When overriding the `equals` or `hashCode` methods, it is important to ensure that their behavior is - | * consistent with reference equality. Therefore, if two objects are references to each other (`o1 eq o2`), they - | * should be equal to each other (`o1 == o2`) and they should hash to the same value (`o1.hashCode == o2.hashCode`). - | * - | * @param that the object to compare against this object for reference equality. - | * @return `true` if the argument is a reference to the receiver object; `false` otherwise. - | */ - """.stripMargin, - - Object_ne -> - """/** Equivalent to `!(this eq that)`. - | * - | * @param that the object to compare against this object for reference equality. - | * @return `true` if the argument is not a reference to the receiver object; `false` otherwise. - | */ - """.stripMargin, - - Object_synchronized -> - """/** Executes the code in `body` with an exclusive lock on `this`. - | * - | * @param body the code to execute - | * @return the result of `body` - | */ - """.stripMargin, - - Object_clone -> - """/** Create a copy of the receiver object. - | * - | * The default implementation of the `clone` method is platform dependent. - | * - | * @note not specified by SLS as a member of AnyRef - | * @return a copy of the receiver object. - | */ - """.stripMargin, - - Object_finalize -> - """/** Called by the garbage collector on the receiver object when there - | * are no more references to the object. - | * - | * The details of when and if the `finalize` method is invoked, as - | * well as the interaction between `finalize` and non-local returns - | * and exceptions, are all platform dependent. - | * - | * @note not specified by SLS as a member of AnyRef - | */ - """.stripMargin, - - Object_notify -> - """/** Wakes up a single thread that is waiting on the receiver object's monitor. - | * - | * @note not specified by SLS as a member of AnyRef - | */ - """.stripMargin, - - Object_notifyAll -> - """/** Wakes up all threads that are waiting on the receiver object's monitor. - | * - | * @note not specified by SLS as a member of AnyRef - | */ - """.stripMargin, - - Object_wait -> - """/** See [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait--]]. - | * - | * @note not specified by SLS as a member of AnyRef - | */ - """.stripMargin, - - Object_waitL -> - """/** See [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait-long-]]. - | * - | * @param timeout the maximum time to wait in milliseconds. - | * @note not specified by SLS as a member of AnyRef - | */ - """.stripMargin, - - Object_waitLI -> - """/** See [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait-long-int-]] - | * - | * @param timeout the maximum time to wait in milliseconds. - | * @param nanos additional time, in nanoseconds range 0-999999. - | * @note not specified by SLS as a member of AnyRef - | */ - """.stripMargin, - - AnyKindClass -> - """/** - | * - | * - | * - | * - | * - | * - | * - | */ - """.stripMargin, - - andType -> - """/** - | * - | * - | * - | * - | * - | * - | * - | */ - """.stripMargin, - - orType -> - """/** - | * - | * - | * - | * - | * - | * - | * - | */ - """.stripMargin, - - AnyValClass -> - """/** `AnyVal` is the root class of all ''value types'', which describe values - | * not implemented as objects in the underlying host system. Value classes - | * are specified in Scala Language Specification, section 12.2. - | * - | * The standard implementation includes nine `AnyVal` subtypes: - | * - | * [[scala.Double]], [[scala.Float]], [[scala.Long]], [[scala.Int]], [[scala.Char]], - | * [[scala.Short]], and [[scala.Byte]] are the ''numeric value types''. - | * - | * [[scala.Unit]] and [[scala.Boolean]] are the ''non-numeric value types''. - | * - | * Other groupings: - | * - | * - The ''subrange types'' are [[scala.Byte]], [[scala.Short]], and [[scala.Char]]. - | * - The ''integer types'' include the subrange types as well as [[scala.Int]] and [[scala.Long]]. - | * - The ''floating point types'' are [[scala.Float]] and [[scala.Double]]. - | * - | * Prior to Scala 2.10, `AnyVal` was a sealed trait. Beginning with Scala 2.10, - | * however, it is possible to define a subclass of `AnyVal` called a ''user-defined value class'' - | * which is treated specially by the compiler. Properly-defined user value classes provide a way - | * to improve performance on user-defined types by avoiding object allocation at runtime, and by - | * replacing virtual method invocations with static method invocations. - | * - | * User-defined value classes which avoid object allocation... - | * - | * - must have a single `val` parameter that is the underlying runtime representation. - | * - can define `def`s, but no `val`s, `var`s, or nested `traits`s, `class`es or `object`s. - | * - typically extend no other trait apart from `AnyVal`. - | * - cannot be used in type tests or pattern matching. - | * - may not override `equals` or `hashCode` methods. - | * - | * A minimal example: - | * {{{ - | * class Wrapper(val underlying: Int) extends AnyVal { - | * def foo: Wrapper = new Wrapper(underlying * 19) - | * } - | * }}} - | * - | * It's important to note that user-defined value classes are limited, and in some circumstances, - | * still must allocate a value class instance at runtime. These limitations and circumstances are - | * explained in greater detail in the [[https://docs.scala-lang.org/overviews/core/value-classes.html Value Classes and Universal Traits]]. - | */ - """.stripMargin, - - NullClass -> - """/** `Null` is - together with [[scala.Nothing]] - at the bottom of the Scala type hierarchy. - | * - | * `Null` is the type of the `null` literal. It is a subtype of every type - | * except those of value classes. Value classes are subclasses of [[AnyVal]], which includes - | * primitive types such as [[Int]], [[Boolean]], and user-defined value classes. - | * - | * Since `Null` is not a subtype of value types, `null` is not a member of any such type. - | * For instance, it is not possible to assign `null` to a variable of type [[scala.Int]]. - | */ - """.stripMargin, - - NothingClass -> - """/** `Nothing` is - together with [[scala.Null]] - at the bottom of Scala's type hierarchy. - | * - | * `Nothing` is a subtype of every other type (including [[scala.Null]]); there exist - | * ''no instances'' of this type. Although type `Nothing` is uninhabited, it is - | * nevertheless useful in several ways. For instance, the Scala library defines a value - | * [[scala.collection.immutable.Nil]] of type `List[Nothing]`. Because lists are covariant in Scala, - | * this makes [[scala.collection.immutable.Nil]] an instance of `List[T]`, for any element of type `T`. - | * - | * Another usage for Nothing is the return type for methods which never return normally. - | * One example is method error in [[scala.sys]], which always throws an exception. - | */ - """.stripMargin, - - SingletonClass -> - """/** `Singleton` is used by the compiler as a supertype for singleton types. This includes literal types, - | * as they are also singleton types. - | * - | * {{{ - | * scala> object A { val x = 42 } - | * defined object A - | * - | * scala> implicitly[A.type <:< Singleton] - | * res12: A.type <:< Singleton = generalized constraint - | * - | * scala> implicitly[A.x.type <:< Singleton] - | * res13: A.x.type <:< Singleton = generalized constraint - | * - | * scala> implicitly[42 <:< Singleton] - | * res14: 42 <:< Singleton = generalized constraint - | * - | * scala> implicitly[Int <:< Singleton] - | * ^ - | * error: Cannot prove that Int <:< Singleton. - | * }}} - | * - | * `Singleton` has a special meaning when it appears as an upper bound on a formal type - | * parameter. Normally, type inference in Scala widens singleton types to the underlying - | * non-singleton type. When a type parameter has an explicit upper bound of `Singleton`, - | * the compiler infers a singleton type. - | * - | * {{{ - | * scala> def check42[T](x: T)(implicit ev: T =:= 42): T = x - | * check42: [T](x: T)(implicit ev: T =:= 42)T - | * - | * scala> val x1 = check42(42) - | * ^ - | * error: Cannot prove that Int =:= 42. - | * - | * scala> def singleCheck42[T <: Singleton](x: T)(implicit ev: T =:= 42): T = x - | * singleCheck42: [T <: Singleton](x: T)(implicit ev: T =:= 42)T - | * - | * scala> val x2 = singleCheck42(42) - | * x2: Int = 42 - | * }}} - | * - | * See also [[https://docs.scala-lang.org/sips/42.type.html SIP-23 about Literal-based Singleton Types]]. - | */ - """.stripMargin, - ).view.mapValues(Comment(NoSpan, _)).toMap -} diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index effa0b93338a..71ac92a8d271 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -12,6 +12,8 @@ import Denotations.SingleDenotation import util.{SimpleIdentityMap, SourceFile, NoSource} import typer.ImportInfo.RootRef import Comments.CommentsContext +import Comments.Comment +import util.Spans.NoSpan import scala.annotation.tailrec @@ -1719,8 +1721,390 @@ class Definitions { isInitialized = true } - syntheticSymbolsToComment(this).foreach { (s, c) => - ctx.docCtx.get.addDocstring(s, Some(c)) - } + addSyntheticSymbolsComments } + + def addSyntheticSymbolsComments(using Context): Unit = + def add(sym: Symbol, doc: String) = ctx.docCtx.get.addDocstring(sym, Some(Comment(NoSpan, doc))) + + add(AnyClass, + """/** Class `Any` is the root of the Scala class hierarchy. Every class in a Scala + | * execution environment inherits directly or indirectly from this class. + | * + | * Starting with Scala 2.10 it is possible to directly extend `Any` using ''universal traits''. + | * A ''universal trait'' is a trait that extends `Any`, only has `def`s as members, and does no initialization. + | * + | * The main use case for universal traits is to allow basic inheritance of methods for [[scala.AnyVal value classes]]. + | * For example, + | * + | * {{{ + | * trait Printable extends Any { + | * def print(): Unit = println(this) + | * } + | * class Wrapper(val underlying: Int) extends AnyVal with Printable + | * + | * val w = new Wrapper(3) + | * w.print() + | * }}} + | * + | * See the [[https://docs.scala-lang.org/overviews/core/value-classes.html Value Classes and Universal Traits]] for more + | * details on the interplay of universal traits and value classes. + | */ + """.stripMargin) + + add(Any_==, + """/** Test two objects for equality. + | * The expression `x == that` is equivalent to `if (x eq null) that eq null else x.equals(that)`. + | * + | * @param that the object to compare against this object for equality. + | * @return `true` if the receiver object is equivalent to the argument; `false` otherwise. + | */ + """.stripMargin) + + add(Any_!=, + """/** Test two objects for inequality. + | * + | * @param that the object to compare against this object for equality. + | * @return `true` if !(this == that), `false` otherwise. + | */ + """.stripMargin) + + add(Any_equals, + """/** Compares the receiver object (`this`) with the argument object (`that`) for equivalence. + | * + | * Any implementation of this method should be an [[https://en.wikipedia.org/wiki/Equivalence_relation equivalence relation]]: + | * + | * - It is reflexive: for any instance `x` of type `Any`, `x.equals(x)` should return `true`. + | * - It is symmetric: for any instances `x` and `y` of type `Any`, `x.equals(y)` should return `true` if and + | * only if `y.equals(x)` returns `true`. + | * - It is transitive: for any instances `x`, `y`, and `z` of type `Any` if `x.equals(y)` returns `true` and + | * `y.equals(z)` returns `true`, then `x.equals(z)` should return `true`. + | * + | * If you override this method, you should verify that your implementation remains an equivalence relation. + | * Additionally, when overriding this method it is usually necessary to override `hashCode` to ensure that + | * objects which are "equal" (`o1.equals(o2)` returns `true`) hash to the same [[scala.Int]]. + | * (`o1.hashCode.equals(o2.hashCode)`). + | * + | * @param that the object to compare against this object for equality. + | * @return `true` if the receiver object is equivalent to the argument; `false` otherwise. + | */ + """.stripMargin) + + add(Any_hashCode, + """/** Calculate a hash code value for the object. + | * + | * The default hashing algorithm is platform dependent. + | * + | * Note that it is allowed for two objects to have identical hash codes (`o1.hashCode.equals(o2.hashCode)`) yet + | * not be equal (`o1.equals(o2)` returns `false`). A degenerate implementation could always return `0`. + | * However, it is required that if two objects are equal (`o1.equals(o2)` returns `true`) that they have + | * identical hash codes (`o1.hashCode.equals(o2.hashCode)`). Therefore, when overriding this method, be sure + | * to verify that the behavior is consistent with the `equals` method. + | * + | * @return the hash code value for this object. + | */ + """.stripMargin) + + add(Any_toString, + """/** Returns a string representation of the object. + | * + | * The default representation is platform dependent. + | * + | * @return a string representation of the object. + | */ + """.stripMargin) + + add(Any_##, + """/** Equivalent to `x.hashCode` except for boxed numeric types and `null`. + | * For numerics, it returns a hash value which is consistent + | * with value equality: if two value type instances compare + | * as true, then ## will produce the same hash value for each + | * of them. + | * For `null` returns a hashcode where `null.hashCode` throws a + | * `NullPointerException`. + | * + | * @return a hash value consistent with == + | */ + """.stripMargin) + + add(Any_isInstanceOf, + """/** Test whether the dynamic type of the receiver object is `T0`. + | * + | * Note that the result of the test is modulo Scala's erasure semantics. + | * Therefore the expression `1.isInstanceOf[String]` will return `false`, while the + | * expression `List(1).isInstanceOf[List[String]]` will return `true`. + | * In the latter example, because the type argument is erased as part of compilation it is + | * not possible to check whether the contents of the list are of the specified type. + | * + | * @return `true` if the receiver object is an instance of erasure of type `T0`; `false` otherwise. + | */ + """.stripMargin) + + add(Any_asInstanceOf, + """/** Cast the receiver object to be of type `T0`. + | * + | * Note that the success of a cast at runtime is modulo Scala's erasure semantics. + | * Therefore the expression `1.asInstanceOf[String]` will throw a `ClassCastException` at + | * runtime, while the expression `List(1).asInstanceOf[List[String]]` will not. + | * In the latter example, because the type argument is erased as part of compilation it is + | * not possible to check whether the contents of the list are of the requested type. + | * + | * @throws ClassCastException if the receiver object is not an instance of the erasure of type `T0`. + | * @return the receiver object. + | */ + """.stripMargin) + + add(Any_getClass, + """/** Returns the runtime class representation of the object. + | * + | * @return a class object corresponding to the runtime type of the receiver. + | */ + """.stripMargin) + + add(MatchableClass, + """/** The base trait of types that can be safely pattern matched against. + | * + | * See [[https://dotty.epfl.ch/docs/reference/other-new-features/matchable.html]]. + | */ + """.stripMargin) + + add(AnyRefAlias, + """/** Class `AnyRef` is the root class of all ''reference types''. + | * All types except the value types descend from this class. + | */ + """.stripMargin) + + add(Object_eq, + """/** Tests whether the argument (`that`) is a reference to the receiver object (`this`). + | * + | * The `eq` method implements an [[https://en.wikipedia.org/wiki/Equivalence_relation equivalence relation]] on + | * non-null instances of `AnyRef`, and has three additional properties: + | * + | * - It is consistent: for any non-null instances `x` and `y` of type `AnyRef`, multiple invocations of + | * `x.eq(y)` consistently returns `true` or consistently returns `false`. + | * - For any non-null instance `x` of type `AnyRef`, `x.eq(null)` and `null.eq(x)` returns `false`. + | * - `null.eq(null)` returns `true`. + | * + | * When overriding the `equals` or `hashCode` methods, it is important to ensure that their behavior is + | * consistent with reference equality. Therefore, if two objects are references to each other (`o1 eq o2`), they + | * should be equal to each other (`o1 == o2`) and they should hash to the same value (`o1.hashCode == o2.hashCode`). + | * + | * @param that the object to compare against this object for reference equality. + | * @return `true` if the argument is a reference to the receiver object; `false` otherwise. + | */ + """.stripMargin) + + add(Object_ne, + """/** Equivalent to `!(this eq that)`. + | * + | * @param that the object to compare against this object for reference equality. + | * @return `true` if the argument is not a reference to the receiver object; `false` otherwise. + | */ + """.stripMargin) + + add(Object_synchronized, + """/** Executes the code in `body` with an exclusive lock on `this`. + | * + | * @param body the code to execute + | * @return the result of `body` + | */ + """.stripMargin) + + add(Object_clone, + """/** Create a copy of the receiver object. + | * + | * The default implementation of the `clone` method is platform dependent. + | * + | * @note not specified by SLS as a member of AnyRef + | * @return a copy of the receiver object. + | */ + """.stripMargin) + + add(Object_finalize, + """/** Called by the garbage collector on the receiver object when there + | * are no more references to the object. + | * + | * The details of when and if the `finalize` method is invoked, as + | * well as the interaction between `finalize` and non-local returns + | * and exceptions, are all platform dependent. + | * + | * @note not specified by SLS as a member of AnyRef + | */ + """.stripMargin) + + add(Object_notify, + """/** Wakes up a single thread that is waiting on the receiver object's monitor. + | * + | * @note not specified by SLS as a member of AnyRef + | */ + """.stripMargin) + + add(Object_notifyAll, + """/** Wakes up all threads that are waiting on the receiver object's monitor. + | * + | * @note not specified by SLS as a member of AnyRef + | */ + """.stripMargin) + + add(Object_wait, + """/** See [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait--]]. + | * + | * @note not specified by SLS as a member of AnyRef + | */ + """.stripMargin) + + add(Object_waitL, + """/** See [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait-long-]]. + | * + | * @param timeout the maximum time to wait in milliseconds. + | * @note not specified by SLS as a member of AnyRef + | */ + """.stripMargin) + + add(Object_waitLI, + """/** See [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait-long-int-]] + | * + | * @param timeout the maximum time to wait in milliseconds. + | * @param nanos additional time, in nanoseconds range 0-999999. + | * @note not specified by SLS as a member of AnyRef + | */ + """.stripMargin) + + add(AnyKindClass, + """/** The super-type of all types. + | * + | * See [[https://dotty.epfl.ch/docs/reference/other-new-features/kind-polymorphism.html]]. + | */ + """.stripMargin) + + add(andType, + """/** The intersection of two types. + | * + | * See [[https://dotty.epfl.ch/docs/reference/new-types/intersection-types.html]]. + | */ + """.stripMargin) + + add(orType, + """/** The union of two types. + | * + | * See [[https://dotty.epfl.ch/docs/reference/new-types/union-types.html]]. + | */ + """.stripMargin) + + add(AnyValClass, + """/** `AnyVal` is the root class of all ''value types'', which describe values + | * not implemented as objects in the underlying host system. Value classes + | * are specified in Scala Language Specification, section 12.2. + | * + | * The standard implementation includes nine `AnyVal` subtypes: + | * + | * [[scala.Double]], [[scala.Float]], [[scala.Long]], [[scala.Int]], [[scala.Char]], + | * [[scala.Short]], and [[scala.Byte]] are the ''numeric value types''. + | * + | * [[scala.Unit]] and [[scala.Boolean]] are the ''non-numeric value types''. + | * + | * Other groupings: + | * + | * - The ''subrange types'' are [[scala.Byte]], [[scala.Short]], and [[scala.Char]]. + | * - The ''integer types'' include the subrange types as well as [[scala.Int]] and [[scala.Long]]. + | * - The ''floating point types'' are [[scala.Float]] and [[scala.Double]]. + | * + | * Prior to Scala 2.10, `AnyVal` was a sealed trait. Beginning with Scala 2.10, + | * however, it is possible to define a subclass of `AnyVal` called a ''user-defined value class'' + | * which is treated specially by the compiler. Properly-defined user value classes provide a way + | * to improve performance on user-defined types by avoiding object allocation at runtime, and by + | * replacing virtual method invocations with static method invocations. + | * + | * User-defined value classes which avoid object allocation... + | * + | * - must have a single `val` parameter that is the underlying runtime representation. + | * - can define `def`s, but no `val`s, `var`s, or nested `traits`s, `class`es or `object`s. + | * - typically extend no other trait apart from `AnyVal`. + | * - cannot be used in type tests or pattern matching. + | * - may not override `equals` or `hashCode` methods. + | * + | * A minimal example: + | * {{{ + | * class Wrapper(val underlying: Int) extends AnyVal { + | * def foo: Wrapper = new Wrapper(underlying * 19) + | * } + | * }}} + | * + | * It's important to note that user-defined value classes are limited, and in some circumstances, + | * still must allocate a value class instance at runtime. These limitations and circumstances are + | * explained in greater detail in the [[https://docs.scala-lang.org/overviews/core/value-classes.html Value Classes and Universal Traits]]. + | */ + """.stripMargin) + + add(NullClass, + """/** `Null` is - together with [[scala.Nothing]] - at the bottom of the Scala type hierarchy. + | * + | * `Null` is the type of the `null` literal. It is a subtype of every type + | * except those of value classes. Value classes are subclasses of [[AnyVal]], which includes + | * primitive types such as [[Int]], [[Boolean]], and user-defined value classes. + | * + | * Since `Null` is not a subtype of value types, `null` is not a member of any such type. + | * For instance, it is not possible to assign `null` to a variable of type [[scala.Int]]. + | */ + """.stripMargin) + + add(NothingClass, + """/** `Nothing` is - together with [[scala.Null]] - at the bottom of Scala's type hierarchy. + | * + | * `Nothing` is a subtype of every other type (including [[scala.Null]]); there exist + | * ''no instances'' of this type. Although type `Nothing` is uninhabited, it is + | * nevertheless useful in several ways. For instance, the Scala library defines a value + | * [[scala.collection.immutable.Nil]] of type `List[Nothing]`. Because lists are covariant in Scala, + | * this makes [[scala.collection.immutable.Nil]] an instance of `List[T]`, for any element of type `T`. + | * + | * Another usage for Nothing is the return type for methods which never return normally. + | * One example is method error in [[scala.sys]], which always throws an exception. + | */ + """.stripMargin) + + add(SingletonClass, + """/** `Singleton` is used by the compiler as a supertype for singleton types. This includes literal types, + | * as they are also singleton types. + | * + | * {{{ + | * scala> object A { val x = 42 } + | * defined object A + | * + | * scala> implicitly[A.type <:< Singleton] + | * res12: A.type <:< Singleton = generalized constraint + | * + | * scala> implicitly[A.x.type <:< Singleton] + | * res13: A.x.type <:< Singleton = generalized constraint + | * + | * scala> implicitly[42 <:< Singleton] + | * res14: 42 <:< Singleton = generalized constraint + | * + | * scala> implicitly[Int <:< Singleton] + | * ^ + | * error: Cannot prove that Int <:< Singleton. + | * }}} + | * + | * `Singleton` has a special meaning when it appears as an upper bound on a formal type + | * parameter. Normally, type inference in Scala widens singleton types to the underlying + | * non-singleton type. When a type parameter has an explicit upper bound of `Singleton`, + | * the compiler infers a singleton type. + | * + | * {{{ + | * scala> def check42[T](x: T)(implicit ev: T =:= 42): T = x + | * check42: [T](x: T)(implicit ev: T =:= 42)T + | * + | * scala> val x1 = check42(42) + | * ^ + | * error: Cannot prove that Int =:= 42. + | * + | * scala> def singleCheck42[T <: Singleton](x: T)(implicit ev: T =:= 42): T = x + | * singleCheck42: [T <: Singleton](x: T)(implicit ev: T =:= 42)T + | * + | * scala> val x2 = singleCheck42(42) + | * x2: Int = 42 + | * }}} + | * + | * See also [[https://docs.scala-lang.org/sips/42.type.html SIP-23 about Literal-based Singleton Types]]. + | */ + """.stripMargin) } diff --git a/project/Build.scala b/project/Build.scala index e8da4146fbbe..31fa59ebea7a 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -790,6 +790,7 @@ object Build { ).mkString(File.pathSeparator), ) }, + Compile / doc / scalacOptions += "-Ydocument-synthetic-types", scalacOptions -= "-Xfatal-warnings", ivyConfigurations += SourceDeps.hide, transitiveClassifiers := Seq("sources"), @@ -1640,7 +1641,8 @@ object Build { s"-source-links:" + s"$stdLibRoot=github://scala/scala/v${stdlibVersion(Bootstrapped)}#src/library," + s"docs=github://lampepfl/dotty/master#docs", - "-doc-root-content", docRootFile.toString + "-doc-root-content", docRootFile.toString, + "-Ydocument-synthetic-types" ) )) }.evaluated, diff --git a/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala b/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala index 1e900902c24f..3bafdba26d71 100644 --- a/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala +++ b/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala @@ -42,7 +42,8 @@ object Scaladoc: socialLinks: List[SocialLinks] = Nil, identifiersToSkip: List[String] = Nil, regexesToSkip: List[String] = Nil, - rootDocPath: Option[String] = None + rootDocPath: Option[String] = None, + documentSyntheticTypes: Boolean = false, ) def run(args: Array[String], rootContext: CompilerContext): Reporter = @@ -171,7 +172,8 @@ object Scaladoc: socialLinksParsed, skipById.get ++ deprecatedSkipPackages.get, skipByRegex.get, - docRootContent.nonDefault + docRootContent.nonDefault, + YdocumentSyntheticTypes.get ) (Some(docArgs), newContext) } diff --git a/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala b/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala index 701e5f551e8e..538292929dad 100644 --- a/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala +++ b/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala @@ -57,5 +57,8 @@ class ScaladocSettings extends SettingGroup with CommonScalaSettings: val docRootContent: Setting[String] = StringSetting("-doc-root-content", "path", "The file from which the root package documentation should be imported.", "") + val YdocumentSyntheticTypes: Setting[Boolean] = + BooleanSetting("-Ydocument-synthetic-types", "Documents intrinsic types e. g. Any, Nothing. Setting is useful only for stdlib", false) + def scaladocSpecificSettings: Set[Setting[_]] = Set(sourceLinks, syntax, revision, externalDocumentationMappings, socialLinks, skipById, skipByRegex, deprecatedSkipPackages, docRootContent) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/WikiDocRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/WikiDocRenderer.scala index 8204039abd29..ccb94ffee3b5 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/WikiDocRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/WikiDocRenderer.scala @@ -46,7 +46,7 @@ class DocRender(signatureRenderer: SignatureRenderer)(using DocContext): case 5 => h5(content) case 6 => h6(content) case Paragraph(text) => p(renderElement(text)) - case Code(data: String) => code(raw(data)) // TODO add classes + case Code(data: String) => pre(code(raw(data))) // TODO add classes case HorizontalRule => hr case UnorderedList(items) => ul(listItems(items)) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 1f28b561b3da..90b6eed98fbe 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -146,7 +146,7 @@ trait ClassLikeSupport: Some(parseMethod(c, dd.symbol)) case td: TypeDef if !td.symbol.flags.is(Flags.Synthetic) && (!td.symbol.flags.is(Flags.Case) || !td.symbol.flags.is(Flags.Enum)) => - Some(parseTypeDef(c, td)) + Some(parseTypeDef(td)) case vd: ValDef if !isSyntheticField(vd.symbol) && (!vd.symbol.flags.is(Flags.Case) || !vd.symbol.flags.is(Flags.Enum)) @@ -306,7 +306,7 @@ trait ClassLikeSupport: val enumTypes = companion.membersToDocument.collect { case td: TypeDef if !td.symbol.flags.is(Flags.Synthetic) && td.symbol.flags.is(Flags.Enum) && td.symbol.flags.is(Flags.Case) => td - }.toList.map(parseTypeDef(classDef, _)) + }.toList.map(parseTypeDef) val enumNested = companion.membersToDocument.collect { case c: ClassDef if c.symbol.flags.is(Flags.Case) && c.symbol.flags.is(Flags.Enum) => processTree(c)(parseClasslike(c)) @@ -405,7 +405,7 @@ trait ClassLikeSupport: memberInfo.get(name).fold(argument.rhs.asSignature)(_.asSignature) ) - def parseTypeDef(c: ClassDef, typeDef: TypeDef): Member = + def parseTypeDef(typeDef: TypeDef): Member = def isTreeAbstract(typ: Tree): Boolean = typ match { case TypeBoundsTree(_, _) => true case LambdaTypeTree(params, body) => isTreeAbstract(body) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala index c3fa15cbb25c..c150a1b8e304 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala @@ -160,12 +160,13 @@ class SymOps[Q <: Quotes](val q: Q) extends JavadocAnchorCreator with Scaladoc2A else if (sym.maybeOwner.isDefDef) Some(sym.owner) else None - val className = sym.className + val (className, anchor) = if sym.fullName == "scala.AnyRef" then // hacking relocation for synthetic `type AnyRef` + (Some("AnyRef"), None) + else + (sym.className, sym.anchor) val location = sym.packageNameSplitted ++ className - val anchor = sym.anchor - val externalLink = { import q.reflect._ import dotty.tools.dotc diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala index 2d57da7b047d..28440b066fa7 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala @@ -83,14 +83,10 @@ trait SyntheticsSupport: import dotty.tools.dotc given dotc.core.Contexts.Context = qctx.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx val classdef = rdef.asInstanceOf[dotc.ast.tpd.TypeDef] - classdef.symbol.info match - case ci: dotc.core.Types.ClassInfo => - val ref = ci.appliedRef - val baseTypes: List[(dotc.core.Symbols.Symbol, dotc.core.Types.Type)] = - ref.baseClasses.map(b => b -> ref.baseType(b)) - baseTypes.asInstanceOf[List[(Symbol, TypeRepr)]] - case _ => - List.empty + val ref = classdef.symbol.info.asInstanceOf[dotc.core.Types.ClassInfo].appliedRef + val baseTypes: List[(dotc.core.Symbols.Symbol, dotc.core.Types.Type)] = + ref.baseClasses.map(b => b -> ref.baseType(b)) + baseTypes.asInstanceOf[List[(Symbol, TypeRepr)]] } def hackExists(using Quotes)(rpos: qctx.reflect.Position) = { @@ -102,9 +98,7 @@ trait SyntheticsSupport: pos.exists } - def getSupertypes(using Quotes)(c: ClassDef) = hackGetSupertypes(c) match - case _ :: tail => tail - case _ => List.empty + def getSupertypes(using Quotes)(c: ClassDef) = hackGetSupertypes(c).tail def typeForClass(c: ClassDef): TypeRepr = import qctx.reflect._ diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala index eb6ee395c897..033321f0b0d1 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala @@ -118,19 +118,25 @@ case class ScaladocTastyInspector()(using ctx: DocContext) extends DocTastyInspe val defn = ctx.compilerContext.definitions - topLevels ++= Seq( - defn.AnyClass, - defn.MatchableClass, - // defn.AnyRefAlias, - defn.AnyKindClass, - // defn.andType, - // defn.orType, - defn.AnyValClass, - defn.NullClass, - defn.NothingClass, - defn.SingletonClass - ).map(s => "scala" -> parser.parseClasslike(s.asInstanceOf[parser.qctx.reflect.Symbol].tree.asInstanceOf[parser.qctx.reflect.ClassDef])) - + if ctx.args.documentSyntheticTypes then + val intrinsicClassDefs = Seq( + defn.AnyClass, + defn.MatchableClass, + defn.AnyKindClass, + defn.AnyValClass, + defn.NullClass, + defn.NothingClass, + defn.SingletonClass, + defn.andType, + defn.orType, + ).map { s => + "scala" -> s.asInstanceOf[parser.qctx.reflect.Symbol].tree.match { + case cd: parser.qctx.reflect.ClassDef => parser.parseClasslike(cd) + case td: parser.qctx.reflect.TypeDef => parser.parseTypeDef(td) + } + } + topLevels ++= intrinsicClassDefs + topLevels += mergeAnyRefAliasAndObject(parser) def result(): (List[Member], Option[Comment]) = topLevels.clear() @@ -149,6 +155,14 @@ case class ScaladocTastyInspector()(using ctx: DocContext) extends DocTastyInspe basePck.withMembers((basePck.members ++ rest).sortBy(_.name)) }.toList -> rootDoc + def mergeAnyRefAliasAndObject(parser: TastyParser) = + val defn = ctx.compilerContext.definitions + val oM = parser.parseClasslike(defn.ObjectClass.asInstanceOf[parser.qctx.reflect.Symbol].tree.asInstanceOf[parser.qctx.reflect.ClassDef]) + val aM = parser.parseTypeDef(defn.AnyRefAlias.asInstanceOf[parser.qctx.reflect.Symbol].tree.asInstanceOf[parser.qctx.reflect.TypeDef]) + "scala" -> aM.copy( + kind = oM.kind, + members = oM.members + ) /** Parses a single Tasty compilation unit. */ case class TastyParser( qctx: Quotes, @@ -169,13 +183,6 @@ case class TastyParser( report.warning(throwableToString(e), tree.pos) None - // def processSymbol[T](sym: Symbol)(op: => T): Option[T] = try Option(op) catch - // case t: Throwable => - // try report.warning(throwableToString(t), sym.tree.pos) catch - // case _: Throwable => - // report.warning(s"Failed to process ${sym.fullName}:\n${throwableToString(t)}") - // None - def parseRootTree(root: Tree): Seq[(String, Member)] = val docs = Seq.newBuilder[(String, Member)] object Traverser extends TreeTraverser: @@ -201,5 +208,3 @@ case class TastyParser( e.printStackTrace() docs.result() - - // def parseClasslike(c: ClassDef): Member = parseClasslike(c) diff --git a/scaladoc/src/dotty/tools/scaladoc/util/html.scala b/scaladoc/src/dotty/tools/scaladoc/util/html.scala index f8a02a0d6e55..3c07222341b6 100644 --- a/scaladoc/src/dotty/tools/scaladoc/util/html.scala +++ b/scaladoc/src/dotty/tools/scaladoc/util/html.scala @@ -83,7 +83,7 @@ object HTML: val ol = Tag("ol") val li = Tag("li") val code = Tag("code") - + val pre = Tag("pre") val cls = Attr("class") val href = Attr("href")