diff options
50 files changed, 585 insertions, 136 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index deb239143..b5be89440 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -469,7 +469,13 @@ object desugar { val originalVparams = constr1.vparamss.toIterator.flatten val tparamAccessors = derivedTparams.map(_.withMods(originalTparams.next.mods)) val caseAccessor = if (isCaseClass) CaseAccessor else EmptyFlags - val vparamAccessors = derivedVparamss.flatten.map(_.withMods(originalVparams.next.mods | caseAccessor)) + val vparamAccessors = derivedVparamss match { + case first :: rest => + first.map(_.withMods(originalVparams.next.mods | caseAccessor)) ++ + rest.flatten.map(_.withMods(originalVparams.next.mods)) + case _ => + Nil + } cpy.TypeDef(cdef)( name = className, rhs = cpy.Template(impl)(constr, parents1, self1, diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index 985b1ea3d..5464dce4f 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -117,11 +117,17 @@ object Annotations { } /** Create an annotation where the symbol and the tree are computed lazily. */ - def deferredSymAndTree(sym: => Symbol, treeFn: Context => Tree)(implicit ctx: Context): Annotation = + def deferredSymAndTree(symf: Context => Symbol, treeFn: Context => Tree)(implicit ctx: Context): Annotation = new LazyAnnotation { - lazy val symf = sym - - override def symbol(implicit ctx: Context): Symbol = symf + private[this] var mySym: Symbol = _ + + override def symbol(implicit ctx: Context): Symbol = { + if (mySym == null || mySym.defRunId != ctx.runId) { + mySym = symf(ctx) + assert(mySym != null) + } + mySym + } def complete(implicit ctx: Context) = treeFn(ctx) } diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 9e9e39a84..847177e2f 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -9,6 +9,7 @@ import scala.annotation.{ switch, meta } import scala.collection.{ mutable, immutable } import PartialFunction._ import collection.mutable +import util.common.alwaysZero import scala.reflect.api.{ Universe => ApiUniverse } object Definitions { @@ -152,7 +153,7 @@ class Definitions { resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags) = { val tparamNames = tpnme.syntheticTypeParamNames(typeParamCount) val tparamBounds = tparamNames map (_ => TypeBounds.empty) - val ptype = PolyType(tparamNames)(_ => tparamBounds, resultTypeFn) + val ptype = PolyType(tparamNames, tparamNames.map(alwaysZero))(_ => tparamBounds, resultTypeFn) enterMethod(cls, name, ptype, flags) } diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 6063cbf38..fca111702 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -489,7 +489,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp1 @ MethodType(_, formals1) => (tp1.signature consistentParams tp2.signature) && matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) && - (!tp1.isImplicit || tp2.isImplicit) && // non-implicit functions shadow implicit ones + (tp1.isImplicit == tp2.isImplicit) && isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1)) case _ => false diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index c2a7d7ea6..308e6e306 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -401,12 +401,12 @@ trait TypeOps { this: Context => // TODO: Make standalone object. def forwardRefs(from: Symbol, to: Type, prefs: List[TypeRef]) = to match { case to @ TypeBounds(lo1, hi1) if lo1 eq hi1 => for (pref <- prefs) { - def forward(): Unit = + def forward()(implicit ctx: Context): Unit = for (argSym <- pref.decls) if (argSym is BaseTypeArg) forwardRef(argSym, from, to, cls, decls) pref.info match { - case info: TempClassInfo => info.addSuspension(forward) + case info: TempClassInfo => info.addSuspension(implicit ctx => forward()) case _ => forward() } } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index ae9122853..200e94a1e 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2603,7 +2603,7 @@ object Types { case t => mapOver(t) } } - PolyType(paramNames ++ that.paramNames)( + PolyType(paramNames ++ that.paramNames, variances ++ that.variances)( x => this.paramBounds.mapConserve(_.subst(this, x).bounds) ++ that.paramBounds.mapConserve(shift(_).subst(that, x).bounds), x => shift(that.resultType).subst(that, x).subst(this, x)) @@ -2634,11 +2634,10 @@ object Types { } object PolyType { - def apply(paramNames: List[TypeName], variances: List[Int] = Nil)( + def apply(paramNames: List[TypeName], variances: List[Int])( paramBoundsExp: PolyType => List[TypeBounds], resultTypeExp: PolyType => Type)(implicit ctx: Context): PolyType = { - val vs = if (variances.isEmpty) paramNames.map(alwaysZero) else variances - unique(new PolyType(paramNames, vs)(paramBoundsExp, resultTypeExp)) + unique(new PolyType(paramNames, variances)(paramBoundsExp, resultTypeExp)) } def unapply(tl: PolyType): Some[(List[LambdaParam], Type)] = @@ -3089,14 +3088,14 @@ object Types { * be no longer temporary. These actions will be performed once `cls` gets a real * ClassInfo. */ - private var suspensions: List[() => Unit] = Nil + private var suspensions: List[Context => Unit] = Nil - def addSuspension(suspension: () => Unit): Unit = suspensions ::= suspension + def addSuspension(suspension: Context => Unit): Unit = suspensions ::= suspension /** Install classinfo with known parents in `denot` and resume all suspensions */ def finalize(denot: SymDenotation, parents: List[TypeRef])(implicit ctx: Context) = { denot.info = derivedClassInfo(classParents = parents) - suspensions.foreach(_()) + suspensions.foreach(_(ctx)) } } diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 5b751ef3c..36d478c6d 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -806,7 +806,7 @@ class ClassfileParser( def classSymbol(externalName: Name)(implicit ctx: Context): Symbol = { /** Return the symbol of `innerName`, having the given `externalName`. */ def innerSymbol(externalName: Name, innerName: Name, static: Boolean): Symbol = { - def getMember(sym: Symbol, name: Name): Symbol = + def getMember(sym: Symbol, name: Name)(implicit ctx: Context): Symbol = if (static) if (sym == classRoot.symbol) staticScope.lookup(name) else sym.companionModule.info.member(name).symbol diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index ffd594454..fcba957c0 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -554,7 +554,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle val end = readEnd() val tp = readType() val lazyAnnotTree = readLater(end, rdr => ctx => rdr.readTerm()(ctx)) - annots += Annotation.deferredSymAndTree(tp.typeSymbol, _ => lazyAnnotTree.complete) + annots += Annotation.deferredSymAndTree( + implicit ctx => tp.typeSymbol, + implicit ctx => lazyAnnotTree.complete) case tag => assert(false, s"illegal modifier tag $tag at $currentAddr, end = $end") } @@ -769,7 +771,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle cls.setApplicableFlags(fork.indexStats(end)) val constr = readIndexedDef().asInstanceOf[DefDef] - def mergeTypeParamsAndAliases(tparams: List[TypeDef], stats: List[Tree]): (List[Tree], List[Tree]) = + def mergeTypeParamsAndAliases(tparams: List[TypeDef], stats: List[Tree])(implicit ctx: Context): (List[Tree], List[Tree]) = (tparams, stats) match { case (tparam :: tparams1, (alias: TypeDef) :: stats1) if tparam.name == alias.name.expandedName(cls) => diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index b01f6cc6a..3a2a45fd2 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -932,9 +932,10 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas protected def deferredAnnot(end: Int)(implicit ctx: Context): Annotation = { val start = readIndex val atp = readTypeRef() + val phase = ctx.phase Annotation.deferred( - atp.typeSymbol, implicit ctx1 => - atReadPos(start, () => readAnnotationContents(end)(ctx1.withPhase(ctx.phase)))) + atp.typeSymbol, implicit ctx => + atReadPos(start, () => readAnnotationContents(end)(ctx.withPhase(phase)))) } /* Read an abstract syntax tree */ diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index b46bc401d..c14108d2e 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -993,20 +993,22 @@ object Parsers { else { val saved = placeholderParams placeholderParams = Nil + + def wrapPlaceholders(t: Tree) = try + if (placeholderParams.isEmpty) t + else new WildcardFunction(placeholderParams.reverse, t) + finally placeholderParams = saved + val t = expr1(location) if (in.token == ARROW) { - placeholderParams = saved - closureRest(start, location, convertToParams(t)) + placeholderParams = Nil // don't interpret `_' to the left of `=>` as placeholder + wrapPlaceholders(closureRest(start, location, convertToParams(t))) } else if (isWildcard(t)) { placeholderParams = placeholderParams ::: saved t } - else - try - if (placeholderParams.isEmpty) t - else new WildcardFunction(placeholderParams.reverse, t) - finally placeholderParams = saved + else wrapPlaceholders(t) } } diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index ac25f7cfd..0d1068b8c 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -7,6 +7,7 @@ import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annot import StdNames.{nme, tpnme} import ast.Trees._, ast._ import typer.Implicits._ +import typer.ImportInfo import config.Config import java.lang.Integer.toOctalString import config.Config.summarizeDepth @@ -177,7 +178,8 @@ class PlainPrinter(_ctx: Context) extends Printer { varianceString(variance) ~ name.toString ~ toText(bounds) changePrec(GlobalPrec) { "[" ~ Text((tp.variances, tp.paramNames, tp.paramBounds).zipped.map(paramText), ", ") ~ - "] => " ~ toTextGlobal(tp.resultType) + "]" ~ (" => " provided !tp.resultType.isInstanceOf[MethodType]) ~ + toTextGlobal(tp.resultType) } case tp: PolyParam => polyParamNameString(tp) ~ polyHash(tp.binder) @@ -502,6 +504,17 @@ class PlainPrinter(_ctx: Context) extends Printer { "?Unknown Implicit Result?" } + def toText(importInfo: ImportInfo): Text = { + val siteStr = importInfo.site.show + val exprStr = if (siteStr endsWith ".type") siteStr dropRight 5 else siteStr + val selectorStr = importInfo.selectors match { + case Ident(name) :: Nil => name.show + case _ => "{...}" + } + s"import $exprStr.$selectorStr" + } + + private var maxSummarized = Int.MaxValue def summarized[T](depth: Int)(op: => T): T = { diff --git a/compiler/src/dotty/tools/dotc/printing/Printer.scala b/compiler/src/dotty/tools/dotc/printing/Printer.scala index 506773a4b..e163a83f3 100644 --- a/compiler/src/dotty/tools/dotc/printing/Printer.scala +++ b/compiler/src/dotty/tools/dotc/printing/Printer.scala @@ -6,6 +6,7 @@ import Texts._, ast.Trees._ import Types.Type, Symbols.Symbol, Contexts.Context, Scopes.Scope, Constants.Constant, Names.Name, Denotations._, Annotations.Annotation import typer.Implicits.SearchResult +import typer.ImportInfo /** The base class of all printers */ @@ -98,6 +99,9 @@ abstract class Printer { /** Textual representation of implicit search result */ def toText(result: SearchResult): Text + /** Textual representation of info relating to an import clause */ + def toText(result: ImportInfo): Text + /** Perform string or text-producing operation `op` so that only a * summarized text with given recursion depth is shown */ diff --git a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala index 26c1e5ebc..b2c7abec9 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala @@ -171,22 +171,6 @@ trait Reporting { this: Context => throw ex } } - - /** Implements a fold that applies the function `f` to the result of `op` if - * there are no new errors in the reporter - * - * @param op operation checked for errors - * @param f function applied to result of op - * @return either the result of `op` if it had errors or the result of `f` - * applied to it - */ - def withNoError[A, B >: A](op: => A)(f: A => B): B = { - val before = reporter.errorCount - val op0 = op - - if (reporter.errorCount > before) op0 - else f(op0) - } } /** diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java index c74130b44..2bf15cb7c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java @@ -49,7 +49,9 @@ public enum ErrorMessageID { OverridesNothingButNameExistsID, ForwardReferenceExtendsOverDefinitionID, ExpectedTokenButFoundID, - MixedLeftAndRightAssociativeOpsID; + MixedLeftAndRightAssociativeOpsID, + CantInstantiateAbstractClassOrTraitID, + ; public int errorNumber() { return ordinal() - 2; diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 7fccebef9..34190c114 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1062,7 +1062,7 @@ object messages { | |Define `${definition.name}` before it is used, |or move the definition of `${value.name}` so it does not appear between - |the declartion of `${definition.name}` and its use, + |the declaration of `${definition.name}` and its use, |or define `${value.name}` as lazy. |""".stripMargin } @@ -1127,4 +1127,23 @@ object messages { |""".stripMargin } + case class CantInstantiateAbstractClassOrTrait(cls: Symbol, isTrait: Boolean)(implicit ctx: Context) + extends Message(CantInstantiateAbstractClassOrTraitID) { + val kind = "Usage" + private val traitOrAbstract = if (isTrait) hl"a trait" else hl"abstract" + val msg = hl"""${cls.name} is ${traitOrAbstract}; it cannot be instantiated""" + val explanation = + hl"""|Abstract classes and traits need to be extended by a concrete class or object + |to make their functionality accessible. + | + |You may want to create an anonymous class extending ${cls.name} with + | ${s"class ${cls.name} { }"} + | + |or add a companion object with + | ${s"object ${cls.name} extends ${cls.name}"} + | + |You need to implement any abstract members in both cases. + |""".stripMargin + } + } diff --git a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala index 6c69c735b..9e43fc999 100644 --- a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala +++ b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala @@ -101,6 +101,7 @@ trait FullParameterization { } val ctparams = if (abstractOverClass) clazz.typeParams else Nil val ctnames = ctparams.map(_.name.unexpandedName) + val ctvariances = ctparams.map(_.variance) /** The method result type */ def resultType(mapClassParams: Type => Type) = { @@ -122,14 +123,14 @@ trait FullParameterization { info match { case info: PolyType => - PolyType(info.paramNames ++ ctnames)( + PolyType(info.paramNames ++ ctnames, info.variances ++ ctvariances)( pt => (info.paramBounds.map(mapClassParams(_, pt).bounds) ++ mappedClassBounds(pt)).mapConserve(_.subst(info, pt).bounds), pt => resultType(mapClassParams(_, pt)).subst(info, pt)) case _ => if (ctparams.isEmpty) resultType(identity) - else PolyType(ctnames)(mappedClassBounds, pt => resultType(mapClassParams(_, pt))) + else PolyType(ctnames, ctvariances)(mappedClassBounds, pt => resultType(mapClassParams(_, pt))) } } diff --git a/compiler/src/dotty/tools/dotc/transform/TailRec.scala b/compiler/src/dotty/tools/dotc/transform/TailRec.scala index 3e7a7ed89..aa0845605 100644 --- a/compiler/src/dotty/tools/dotc/transform/TailRec.scala +++ b/compiler/src/dotty/tools/dotc/transform/TailRec.scala @@ -119,7 +119,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete // now this speculatively transforms tree and throws away result in many cases val rhsSemiTransformed = { val transformer = new TailRecElimination(origMeth, dd.tparams, owner, thisTpe, mandatory, label, abstractOverClass = defIsTopLevel) - val rhs = atGroupEnd(transformer.transform(dd.rhs)(_)) + val rhs = atGroupEnd(implicit ctx => transformer.transform(dd.rhs)) rewrote = transformer.rewrote rhs } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 284b9e21e..de017961a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -683,7 +683,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * * { val xs = es; e' = e' + args } */ - def typedOpAssign: Tree = track("typedOpAssign") { + def typedOpAssign(implicit ctx: Context): Tree = track("typedOpAssign") { val Apply(Select(lhs, name), rhss) = tree val lhs1 = typedExpr(lhs) val liftedDefs = new mutable.ListBuffer[Tree] @@ -805,16 +805,16 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * whereas overloaded variants need to have a conforming variant. */ def trySelectUnapply(qual: untpd.Tree)(fallBack: Tree => Tree): Tree = { - val genericProto = new UnapplyFunProto(WildcardType, this) - def specificProto = new UnapplyFunProto(selType, this) // try first for non-overloaded, then for overloaded ocurrences def tryWithName(name: TermName)(fallBack: Tree => Tree)(implicit ctx: Context): Tree = - tryEither { - implicit ctx => typedExpr(untpd.Select(qual, name), specificProto) + tryEither { implicit ctx => + val specificProto = new UnapplyFunProto(selType, this) + typedExpr(untpd.Select(qual, name), specificProto) } { (sel, _) => - tryEither { - implicit ctx => typedExpr(untpd.Select(qual, name), genericProto) + tryEither { implicit ctx => + val genericProto = new UnapplyFunProto(WildcardType, this) + typedExpr(untpd.Select(qual, name), genericProto) } { (_, _) => fallBack(sel) } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index fb0497c2b..48f9243f7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -29,6 +29,7 @@ import ErrorReporting.{err, errorType} import config.Printers.typr import collection.mutable import SymDenotations.NoCompleter +import dotty.tools.dotc.reporting.diagnostic.messages.CantInstantiateAbstractClassOrTrait import dotty.tools.dotc.transform.ValueClasses._ object Checking { @@ -103,7 +104,7 @@ object Checking { case tref: TypeRef => val cls = tref.symbol if (cls.is(AbstractOrTrait)) - ctx.error(em"$cls is abstract; cannot be instantiated", pos) + ctx.error(CantInstantiateAbstractClassOrTrait(cls, isTrait = cls.is(Trait)), pos) if (!cls.is(Module)) { // Create a synthetic singleton type instance, and check whether // it conforms to the self type of the class as seen from that instance. diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 1238ad568..9d6a01ab7 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -111,11 +111,23 @@ object ErrorReporting { errorTree(tree, typeMismatchMsg(normalize(tree.tpe, pt), pt, implicitFailure.postscript)) /** A subtype log explaining why `found` does not conform to `expected` */ - def whyNoMatchStr(found: Type, expected: Type) = - if (ctx.settings.explaintypes.value) + def whyNoMatchStr(found: Type, expected: Type) = { + def dropJavaMethod(tp: Type): Type = tp match { + case tp: PolyType => + tp.derivedPolyType(resType = dropJavaMethod(tp.resultType)) + case tp: JavaMethodType => + MethodType(tp.paramNames, tp.paramTypes, dropJavaMethod(tp.resultType)) + case tp => tp + } + val found1 = dropJavaMethod(found) + val expected1 = dropJavaMethod(expected) + if ((found1 eq found) != (expected eq expected1) && (found1 <:< expected1)) + "\n (Note that Scala's and Java's representation of this type differs)" + else if (ctx.settings.explaintypes.value) "\n" + ctx.typerState.show + "\n" + TypeComparer.explained((found <:< expected)(_)) else "" + } def typeMismatchMsg(found: Type, expected: Type, postScript: String = "") = { // replace constrained polyparams and their typevars by their bounds where possible diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 6949221fb..48cf0cfac 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -220,7 +220,7 @@ object Implicits { } /** The result of an implicit search */ - abstract class SearchResult extends Showable { + sealed abstract class SearchResult extends Showable { def toText(printer: Printer): Text = printer.toText(this) } diff --git a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala index f7efb2ac2..3bee0bbe8 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -5,6 +5,7 @@ package typer import ast.{tpd, untpd} import ast.Trees._ import core._ +import printing.{Printer, Showable} import util.SimpleMap import Symbols._, Names._, Denotations._, Types._, Contexts._, StdNames._, Flags._ import Decorators.StringInterpolators @@ -13,9 +14,9 @@ object ImportInfo { /** The import info for a root import from given symbol `sym` */ def rootImport(refFn: () => TermRef)(implicit ctx: Context) = { val selectors = untpd.Ident(nme.WILDCARD) :: Nil - def expr = tpd.Ident(refFn()) - def imp = tpd.Import(expr, selectors) - new ImportInfo(imp.symbol, selectors, None, isRootImport = true) + def expr(implicit ctx: Context) = tpd.Ident(refFn()) + def imp(implicit ctx: Context) = tpd.Import(expr, selectors) + new ImportInfo(implicit ctx => imp.symbol, selectors, None, isRootImport = true) } } @@ -27,14 +28,14 @@ object ImportInfo { * @param isRootImport true if this is one of the implicit imports of scala, java.lang, * scala.Predef or dotty.DottyPredef in the start context, false otherwise. */ -class ImportInfo(symf: => Symbol, val selectors: List[untpd.Tree], - symNameOpt: Option[TermName], val isRootImport: Boolean = false)(implicit ctx: Context) { +class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree], + symNameOpt: Option[TermName], val isRootImport: Boolean = false) extends Showable { // Dotty deviation: we cannot use a lazy val here for the same reason // that we cannot use one for `DottyPredefModuleRef`. - def sym = { + def sym(implicit ctx: Context) = { if (mySym == null) { - mySym = symf + mySym = symf(ctx) assert(mySym != null) } mySym @@ -91,7 +92,7 @@ class ImportInfo(symf: => Symbol, val selectors: List[untpd.Tree], } /** The implicit references imported by this import clause */ - def importedImplicits: List[TermRef] = { + def importedImplicits(implicit ctx: Context): List[TermRef] = { val pre = site if (isWildcardImport) { val refs = pre.implicitMembers @@ -115,23 +116,21 @@ class ImportInfo(symf: => Symbol, val selectors: List[untpd.Tree], * override import Predef.{any2stringAdd => _, StringAdd => _, _} // disables String + * override import java.lang.{} // disables all imports */ - lazy val unimported: Symbol = { - lazy val sym = site.termSymbol - def maybeShadowsRoot = symNameOpt match { - case Some(symName) => defn.ShadowableImportNames.contains(symName) - case None => false + def unimported(implicit ctx: Context): Symbol = { + if (myUnimported == null) { + lazy val sym = site.termSymbol + def maybeShadowsRoot = symNameOpt match { + case Some(symName) => defn.ShadowableImportNames.contains(symName) + case None => false + } + myUnimported = + if (maybeShadowsRoot && defn.RootImportTypes.exists(_.symbol == sym)) sym + else NoSymbol + assert(myUnimported != null) } - if (maybeShadowsRoot && defn.RootImportTypes.exists(_.symbol == sym)) sym - else NoSymbol + myUnimported } + private[this] var myUnimported: Symbol = _ - override def toString = { - val siteStr = site.show - val exprStr = if (siteStr endsWith ".type") siteStr dropRight 5 else siteStr - val selectorStr = selectors match { - case Ident(name) :: Nil => name.show - case _ => "{...}" - } - i"import $exprStr.$selectorStr" - } + def toText(printer: Printer) = printer.toText(this) } diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index fde304c69..27c7d0c2f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -27,7 +27,7 @@ import transform.TypeUtils._ object Inliner { import tpd._ - /** Adds accessors accessors for all non-public term members accessed + /** Adds accessors for all non-public term members accessed * from `tree`. Non-public type members are currently left as they are. * This means that references to a private type will lead to typing failures * on the code when it is inlined. Less than ideal, but hard to do better (see below). @@ -190,7 +190,8 @@ object Inliner { val inlineCtx = ctx sym.updateAnnotation(LazyBodyAnnotation { _ => implicit val ctx = inlineCtx - ctx.withNoError(treeExpr(ctx))(makeInlineable) + val body = treeExpr(ctx) + if (ctx.reporter.hasErrors) body else makeInlineable(body) }) } } @@ -233,8 +234,10 @@ object Inliner { * and body that replace it. */ def inlineCall(tree: Tree, pt: Type)(implicit ctx: Context): Tree = - if (enclosingInlineds.length < ctx.settings.xmaxInlines.value) - new Inliner(tree, bodyToInline(tree.symbol)).inlined(pt) + if (enclosingInlineds.length < ctx.settings.xmaxInlines.value) { + val body = bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors + if (ctx.reporter.hasErrors) tree else new Inliner(tree, body).inlined(pt) + } else errorTree( tree, i"""|Maximal number of successive inlines (${ctx.settings.xmaxInlines.value}) exceeded, diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 3860ba225..d5f171fe3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -24,6 +24,7 @@ import language.implicitConversions import reporting.diagnostic.messages._ trait NamerContextOps { this: Context => + import NamerContextOps._ /** Enter symbol into current class, if current class is owner of current context, * or into current scope, if not. Should always be called instead of scope.enter @@ -119,22 +120,25 @@ trait NamerContextOps { this: Context => else monotpe } - /** Find moduleClass/sourceModule in effective scope */ - private def findModuleBuddy(name: Name)(implicit ctx: Context) = { - val scope = effectiveScope - val it = scope.lookupAll(name).filter(_ is Module) - assert(it.hasNext, s"no companion $name in $scope") - it.next - } - /** Add moduleClass or sourceModule functionality to completer * for a module or module class */ - def adjustModuleCompleter(completer: LazyType, name: Name) = + def adjustModuleCompleter(completer: LazyType, name: Name) = { + val scope = this.effectiveScope if (name.isTermName) - completer withModuleClass (_ => findModuleBuddy(name.moduleClassName)) + completer withModuleClass (implicit ctx => findModuleBuddy(name.moduleClassName, scope)) else - completer withSourceModule (_ => findModuleBuddy(name.sourceModuleName)) + completer withSourceModule (implicit ctx => findModuleBuddy(name.sourceModuleName, scope)) + } +} + +object NamerContextOps { + /** Find moduleClass/sourceModule in effective scope */ + private def findModuleBuddy(name: Name, scope: Scope)(implicit ctx: Context) = { + val it = scope.lookupAll(name).filter(_ is Module) + assert(it.hasNext, s"no companion $name in $scope") + it.next + } } /** This class creates symbols from definitions and imports and gives them @@ -378,7 +382,7 @@ class Namer { typer: Typer => case ref: RefTree => Some(ref.name.asTermName) case _ => None } - ctx.fresh.setImportInfo(new ImportInfo(sym, imp.selectors, impNameOpt)) + ctx.fresh.setImportInfo(new ImportInfo(implicit ctx => sym, imp.selectors, impNameOpt)) } /** A new context for the interior of a class */ diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index eab91701b..7c573d23c 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -789,7 +789,7 @@ class RefChecks extends MiniPhase { thisTransformer => val sym = tree.symbol if (sym.exists && sym.owner.isTerm && !sym.is(Lazy)) currentLevel.levelAndIndex.get(sym) match { - case Some((level, symIdx)) if symIdx < level.maxIndex => + case Some((level, symIdx)) if symIdx <= level.maxIndex => ctx.error(ForwardReferenceExtendsOverDefinition(sym, level.refSym), level.refPos) case _ => } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 498fd001b..06200d3e4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -145,7 +145,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit */ def bindingString(prec: Int, whereFound: Context, qualifier: String = "") = if (prec == wildImport || prec == namedImport) { - ex"""imported$qualifier by ${hl"${whereFound.importInfo.toString}"}""" + ex"""imported$qualifier by ${hl"${whereFound.importInfo}"}""" } else ex"""defined$qualifier in ${hl"${whereFound.owner.toString}"}""" @@ -1964,7 +1964,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (Inliner.hasBodyToInline(tree.symbol) && !ctx.owner.ownersIterator.exists(_.isInlineMethod) && !ctx.settings.YnoInline.value && - !ctx.isAfterTyper) + !ctx.isAfterTyper && + !ctx.reporter.hasErrors) adapt(Inliner.inlineCall(tree, pt), pt) else if (ctx.typeComparer.GADTused && pt.isValueType) // Insert an explicit cast, so that -Ycheck in later phases succeeds. diff --git a/compiler/test/dotc/tests.scala b/compiler/test/dotc/tests.scala index 7af903364..a720f1294 100644 --- a/compiler/test/dotc/tests.scala +++ b/compiler/test/dotc/tests.scala @@ -184,6 +184,7 @@ class tests extends CompilerTest { @Test def neg_autoTupling = compileFile(negCustomArgs, "autoTuplingTest", args = "-language:noAutoTupling" :: Nil) @Test def neg_i1050 = compileFile(negCustomArgs, "i1050", List("-strict")) @Test def neg_i1240 = compileFile(negCustomArgs, "i1240")(allowDoubleBindings) + @Test def neg_i2002 = compileFile(negCustomArgs, "i2002")(allowDoubleBindings) val negTailcallDir = negDir + "tailcall/" @Test def neg_tailcall_t1672b = compileFile(negTailcallDir, "t1672b") diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index 37d1404bb..971a40a1b 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -159,4 +159,43 @@ class ErrorMessagesTests extends ErrorMessagesTest { assertEquals("+:", op2.show) assertFalse(op2LeftAssoc) } + + @Test def cantInstantiateAbstract = + checkMessagesAfter("refchecks") { + """ + |object Scope { + | abstract class Concept + | new Concept() + |} + """.stripMargin + } + .expect { (ictx, messages) => + implicit val ctx: Context = ictx + val defn = ictx.definitions + + assertMessageCount(1, messages) + val CantInstantiateAbstractClassOrTrait(cls, isTrait) :: Nil = messages + assertEquals("Concept", cls.name.show) + assertFalse("expected class", isTrait) + } + + @Test def cantInstantiateTrait = + checkMessagesAfter("refchecks") { + """ + |object Scope { + | trait Concept + | new Concept() + |} + """.stripMargin + } + .expect { (ictx, messages) => + implicit val ctx: Context = ictx + val defn = ictx.definitions + + assertMessageCount(1, messages) + val CantInstantiateAbstractClassOrTrait(cls, isTrait) :: Nil = messages + assertEquals("Concept", cls.name.show) + assertTrue("expected trait", isTrait) + } + } diff --git a/doc-tool/resources/_layouts/api-page.html b/doc-tool/resources/_layouts/api-page.html index 26a4290da..878ec8d8f 100644 --- a/doc-tool/resources/_layouts/api-page.html +++ b/doc-tool/resources/_layouts/api-page.html @@ -95,7 +95,7 @@ extraCSS: {% for member in entity.members %} <div id="{{ member.signature }}" class="member {% if member.isPrivate %}private{% elsif member.isProtected %}protected{% endif %}"> <div class="member-title"> - <span class="expand-button" onclick="toggleMemberBody(this, '{{ member.signature }}');">[+]</span> + <span class="expand-button {% if member.hasShortenedDocstring == false %}invisible{% endif %}" onclick="toggleMemberBody(this, '{{ member.signature }}');">[+]</span> <span class="member-annotations"> {% for annot in member.annotations %}@{{ annot | split: '.' | last }} {% endfor %} </span> diff --git a/doc-tool/resources/css/api-page.css b/doc-tool/resources/css/api-page.css index 380efb834..1dd4f559c 100644 --- a/doc-tool/resources/css/api-page.css +++ b/doc-tool/resources/css/api-page.css @@ -122,6 +122,11 @@ div.entity-section > div.member > div.member-title > span.expand-button:hover { user-select: none; } +div.entity-section > div.member > div.member-title > span.expand-button.invisible, +div.entity-section > div.member > div.member-title > span.expand-button.invisible:hover { + color: transparent; +} + div.entity-section > div.member > div.member-body { margin: 5px 0 0 39px; } diff --git a/doc-tool/src/dotty/tools/dottydoc/DocCompiler.scala b/doc-tool/src/dotty/tools/dottydoc/DocCompiler.scala index 708c26cc0..c5d20d30b 100644 --- a/doc-tool/src/dotty/tools/dottydoc/DocCompiler.scala +++ b/doc-tool/src/dotty/tools/dottydoc/DocCompiler.scala @@ -32,6 +32,7 @@ class DocCompiler extends Compiler { new LinkSuperTypes, new LinkCompanions, new AlternateConstructors, - new SortMembers)) + new SortMembers)), + List(new StatisticsPhase) ) } diff --git a/doc-tool/src/dotty/tools/dottydoc/DocDriver.scala b/doc-tool/src/dotty/tools/dottydoc/DocDriver.scala index 515de9ae9..1434d5a49 100644 --- a/doc-tool/src/dotty/tools/dottydoc/DocDriver.scala +++ b/doc-tool/src/dotty/tools/dottydoc/DocDriver.scala @@ -9,6 +9,7 @@ import model.Package import dotc.config._ import dotc.core.Comments.ContextDoc import staticsite.Site +import dotc.printing.Highlighting._ /** `DocDriver` implements the main entry point to the Dotty documentation * tool. It's methods are used by the external scala and java APIs. @@ -43,16 +44,18 @@ class DocDriver extends Driver { implicit val (filesToDocument, ctx) = setup(args, initCtx.fresh) val reporter = doCompile(newCompiler(ctx), filesToDocument)(ctx) val siteRoot = new java.io.File(ctx.settings.siteRoot.value) + val projectName = ctx.settings.projectName.value if (!siteRoot.exists || !siteRoot.isDirectory) ctx.error(s"Site root does not exist: $siteRoot") else { - Site(siteRoot, ctx.settings.projectName.value, ctx.docbase.packages) + Site(siteRoot, projectName, ctx.docbase.packages) .generateApiDocs() .copyStaticFiles() .generateHtmlFiles() .generateBlog() + ctx.docbase.printSummary() System.exit(if (reporter.hasErrors) 1 else 0) } } diff --git a/doc-tool/src/dotty/tools/dottydoc/core/ContextDottydoc.scala b/doc-tool/src/dotty/tools/dottydoc/core/ContextDottydoc.scala index 16f0776fa..8f463833d 100644 --- a/doc-tool/src/dotty/tools/dottydoc/core/ContextDottydoc.scala +++ b/doc-tool/src/dotty/tools/dottydoc/core/ContextDottydoc.scala @@ -2,9 +2,12 @@ package dotty.tools package dottydoc package core -import dotc.core.Symbols.Symbol +import dotc.core.Symbols._ +import dotc.core.Flags._ +import dotc.core.Decorators._ import dotc.core.Comments.ContextDocstrings -import model.Package +import model.{ Package, Entity } +import model.comment.Comment import dotc.core.Contexts.Context import dotc.printing.Highlighting._ @@ -17,6 +20,12 @@ class ContextDottydoc extends ContextDocstrings { def packages: Map[String, Package] = _packages.toMap def packagesMutable: mutable.Map[String, Package] = _packages + private[this] var _statistics: Map[String, Statistics] = Map.empty + def registerStatistics(pkgName: String, stat: Statistics): Unit = + _statistics = _statistics + (pkgName -> stat) + + def statistics: Map[String, Statistics] = _statistics + /** Should perhaps factorize this into caches that get flushed */ private var _defs: Map[Symbol, Set[Symbol]] = Map.empty def defs(sym: Symbol): Set[Symbol] = _defs.get(sym).getOrElse(Set.empty) @@ -49,4 +58,77 @@ class ContextDottydoc extends ContextDocstrings { }.toString, pos) def debug(msg: String)(implicit ctx: Context): Unit = debug(msg, NoSourcePosition) + + def printSummary()(implicit ctx: Context): Unit = { + def colored(part: Int, total: Int) = + if (total == 0) "0" + else { + val percentage = (part * 100.0 / total).toInt + val str = s"$part/$total ($percentage%)" + + if (percentage > 75) Green(str) + else if (percentage > 50) Yellow(str) + else Red(str) + } + + val totalEntities = statistics.totalEntities + + val projectName = ctx.settings.projectName.value + val warningsText = + if (ctx.reporter.hasWarnings) + s"total warnings with regards to compilation and documentation: ${ctx.reporter.warningCount}" + else "" + + val api = statistics.values.iterator.map(_.api).foldLeft(Counters(0,0,0,0,0,0))(_ merge _) + val internalApi = statistics.values.iterator.map(_.internalApi).foldLeft(Counters(0,0,0,0,0,0))(_ merge _) + + val apiSummary = (for { + (pkgName, stat) <- statistics.toList.sortBy(_._1) + } yield { + val pub = colored(stat.api.publicDocstrings, stat.api.publicEntities) + val pro = colored(stat.api.protectedDocstrings, stat.api.protectedEntities) + s"""|package $pkgName + |${Blue("-" * ctx.settings.pageWidth.value)} + |public: $pub \t protected: $pro + |""".stripMargin + }).mkString("\n") + + val internalSummary = (for { + (pkgName, stat) <- statistics.toList.sortBy(_._1) + } yield { + val pub = colored(stat.internalApi.publicDocstrings, stat.internalApi.publicEntities) + val pro = colored(stat.internalApi.protectedDocstrings, stat.internalApi.protectedEntities) + val pri = colored(stat.internalApi.privateDocstrings, stat.internalApi.privateEntities) + s"""|package $pkgName + |${Blue("-" * ctx.settings.pageWidth.value)} + |public: $pub \t protected: $pro \t private: $pri + |""".stripMargin + }).mkString("\n") + + ctx.echo { + s"""|${Blue("=" * ctx.settings.pageWidth.value)} + |Dottydoc summary report for project `$projectName` + |${Blue("=" * ctx.settings.pageWidth.value)} + |Documented members in public API: + | + |$apiSummary + | + |Summary: + | + |public members with docstrings: ${colored(api.publicDocstrings, api.publicEntities)} + |${hl"${"protected"}"} members with docstrings: ${colored(api.protectedDocstrings, api.protectedEntities)} + |${Blue("=" * ctx.settings.pageWidth.value)} + | + |Documented members in internal API: + | + |$internalSummary + | + |Summary internal API: + | + |public members with docstrings: ${colored(internalApi.publicDocstrings, internalApi.publicEntities)} + |${hl"${"protected"}"} members with docstrings: ${colored(internalApi.protectedDocstrings, internalApi.protectedEntities)} + |${hl"${"private"}"} members with docstrings: ${colored(internalApi.privateDocstrings, internalApi.privateEntities)} + |$warningsText""".stripMargin + } + } } diff --git a/doc-tool/src/dotty/tools/dottydoc/core/DocASTPhase.scala b/doc-tool/src/dotty/tools/dottydoc/core/DocASTPhase.scala index 460566838..7f44c5656 100644 --- a/doc-tool/src/dotty/tools/dottydoc/core/DocASTPhase.scala +++ b/doc-tool/src/dotty/tools/dottydoc/core/DocASTPhase.scala @@ -226,7 +226,7 @@ class DocASTPhase extends Phase { override def run(implicit ctx: Context): Unit = { currentRun += 1 - ctx.docbase.echo(s"Compiling ($currentRun/$totalRuns): ${ctx.compilationUnit.source.file.name}") + ctx.echo(s"Compiling ($currentRun/$totalRuns): ${ctx.compilationUnit.source.file.name}") collect(ctx.compilationUnit.tpdTree) // Will put packages in `packages` var } @@ -237,8 +237,7 @@ class DocASTPhase extends Phase { // (2) Set parents of entities, needed for linking for { - parentName <- rootPackages(packages) - parent = packages(parentName) + parent <- rootPackages(packages) child <- parent.members } setParent(child, to = parent) diff --git a/doc-tool/src/dotty/tools/dottydoc/core/DocstringPhase.scala b/doc-tool/src/dotty/tools/dottydoc/core/DocstringPhase.scala index 3de8f68f7..2471e9220 100644 --- a/doc-tool/src/dotty/tools/dottydoc/core/DocstringPhase.scala +++ b/doc-tool/src/dotty/tools/dottydoc/core/DocstringPhase.scala @@ -14,6 +14,7 @@ import util.syntax._ /** Phase to add docstrings to the Dottydoc AST */ class DocstringPhase extends DocMiniPhase with CommentParser with CommentCleaner { + private def getComment(sym: Symbol)(implicit ctx: Context): Option[CompilerComment] = ctx.docbase.docstring(sym) .orElse { diff --git a/doc-tool/src/dotty/tools/dottydoc/core/StatisticsPhase.scala b/doc-tool/src/dotty/tools/dottydoc/core/StatisticsPhase.scala new file mode 100644 index 000000000..e7f9a9ec7 --- /dev/null +++ b/doc-tool/src/dotty/tools/dottydoc/core/StatisticsPhase.scala @@ -0,0 +1,156 @@ +package dotty.tools +package dottydoc +package core + +import dotc.core.Phases.Phase +import dotc.core.Contexts.Context +import dotc.core.Symbols.Symbol +import dotc.core.Decorators._ +import dotc.core.Flags._ +import dotc.CompilationUnit +import dottydoc.util.syntax._ +import dottydoc.util.traversing._ + +import model._ + +object Statistics { + implicit class MapTotals(val map: Map[String, Statistics]) extends AnyVal { + def totalEntities = + map.values.foldLeft(0)(_ + _.totalEntities) + } +} + +case class Statistics(pkgName: String, api: Counters, internalApi: Counters) { + def totalEntities = + api.totalEntities + internalApi.totalEntities + + def totalDocstrings = + api.totalDocstrings + internalApi.totalDocstrings +} + +case class Counters( + publicEntities: Int, + privateEntities: Int, + protectedEntities: Int, + + publicDocstrings: Int, + privateDocstrings: Int, + protectedDocstrings: Int +) { + def totalEntities = + publicEntities + privateEntities + protectedEntities + + def totalDocstrings = + publicDocstrings + privateDocstrings + protectedDocstrings + + def merge(o: Counters): Counters = Counters( + publicEntities + o.publicEntities, + privateEntities + o.privateEntities, + protectedEntities + o.protectedEntities, + publicDocstrings + o.publicDocstrings, + privateDocstrings + o.privateDocstrings, + protectedDocstrings + o.protectedDocstrings + ) +} + +class StatisticsPhase extends Phase { + + def phaseName = "StatisticsPhase" + + override def run(implicit ctx: Context): Unit = () + + override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { + for { + (pkgName, pack) <- ctx.docbase.packages + externalApi = collectPublicStats(pack) + internalApi = collectInternalStats(pack) + stats = Statistics(pkgName, externalApi, internalApi) + } ctx.docbase.registerStatistics(pkgName, stats) + + units + } + + def collectPublicStats(pack: Package)(implicit ctx: Context): Counters = { + var publicEntities: Int = 0 + var protectedEntities: Int = 0 + var publicDocstrings: Int = 0 + var protectedDocstrings: Int = 0 + + if (pack.comment.isDefined) { + publicEntities += 1 + publicDocstrings += 1 + } + + def doCount(sym: Symbol, comment: Int): Unit = + if (!sym.is(Protected)) { + publicEntities += 1 + publicDocstrings += comment + } + else { + protectedEntities += 1 + protectedDocstrings += comment + } + + + def recur(e: Entity, reachable: Boolean): Unit = { + val isVisible = !e.symbol.is(Private) && !e.symbol.privateWithin.exists + val shouldCount = isVisible && reachable + e match { + case e: Package => () + case e: Entity with Members => if (shouldCount) { + doCount(e.symbol, if (e.comment.isDefined) 1 else 0) + e.members.foreach { c => + if (!(e.symbol.is(Final) && c.symbol.is(Protected))) recur(c, true) + } + } + case e => + if (shouldCount) doCount(e.symbol, if (e.comment.isDefined) 1 else 0) + } + } + + pack.members.foreach(recur(_, true)) + Counters(publicEntities, 0, protectedEntities, publicDocstrings, 0, protectedDocstrings) + } + + def collectInternalStats(pack: Package)(implicit ctx: Context): Counters = { + var publicEntities: Int = 0 + var privateEntities: Int = 0 + var protectedEntities: Int = 0 + var publicDocstrings: Int = 0 + var privateDocstrings: Int = 0 + var protectedDocstrings: Int = 0 + + def doCount(sym: Symbol, comment: Int): Unit = + if (sym.is(Private)) { + privateEntities += 1 + privateDocstrings += comment + } + else if (!sym.is(Protected)) { + publicEntities += 1 + publicDocstrings += comment + } + else { + protectedEntities += 1 + protectedDocstrings += comment + } + + + def recur(e: Entity, reachable: Boolean): Unit = { + val internal = !reachable || e.symbol.is(Private) || e.symbol.privateWithin.exists + e match { + case _: Package => () + case e: Entity with Members => + e.members.foreach { c => + val childIsInternal = !internal || (e.symbol.is(Final) && c.symbol.is(Protected)) + recur(c, childIsInternal) + } + if (internal) doCount(e.symbol, if (e.comment.isDefined) 1 else 0) + case _ => + if (internal) doCount(e.symbol, if (e.comment.isDefined) 1 else 0) + } + } + + pack.members.foreach(recur(_, true)) + Counters(publicEntities, privateEntities, protectedEntities, publicDocstrings, privateDocstrings, protectedDocstrings) + } +} diff --git a/doc-tool/src/dotty/tools/dottydoc/core/MiniPhaseTransform.scala b/doc-tool/src/dotty/tools/dottydoc/core/transform.scala index 1f30e089d..e4b9ae6b6 100644 --- a/doc-tool/src/dotty/tools/dottydoc/core/MiniPhaseTransform.scala +++ b/doc-tool/src/dotty/tools/dottydoc/core/transform.scala @@ -45,15 +45,15 @@ object transform { * ------------------------- * To delete a node in the AST, simply return `NonEntity` from transforming method */ - abstract class DocMiniTransformations(transformations: List[DocMiniPhase]) extends Phase { + trait DocMiniTransformations extends Phase { + def transformations: List[DocMiniPhase] override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { for { - rootName <- rootPackages(ctx.docbase.packages) - pack = ctx.docbase.packages(rootName) + pack <- rootPackages(ctx.docbase.packages) transformed = performPackageTransform(pack) - } yield ctx.docbase.packagesMutable(rootName) = transformed - super.runOn(units) + } yield ctx.docbase.packagesMutable(pack.name) = transformed + units } private def performPackageTransform(pack: Package)(implicit ctx: Context): Package = { @@ -197,8 +197,9 @@ object transform { object DocMiniTransformations { private var previousPhase = 0 - def apply(transformations: DocMiniPhase*) = - new DocMiniTransformations(transformations.toList) { + def apply(miniPhases: DocMiniPhase*) = + new DocMiniTransformations { + val transformations = miniPhases.toList val packages = Map.empty[String, Package] def phaseName = s"MiniTransformation${ previousPhase += 1 }" diff --git a/doc-tool/src/dotty/tools/dottydoc/model/JavaConverters.scala b/doc-tool/src/dotty/tools/dottydoc/model/JavaConverters.scala index e7cf54a03..239656141 100644 --- a/doc-tool/src/dotty/tools/dottydoc/model/JavaConverters.scala +++ b/doc-tool/src/dotty/tools/dottydoc/model/JavaConverters.scala @@ -55,6 +55,7 @@ object JavaConverters { "comment" -> ent.comment.map(_.asJava).asJava, "superTypes" -> ent.superTypes, "hasVisibleMembers" -> ent.hasVisibleMembers, + "hasShortenedDocstring" -> ent.hasShortenedDocstring, "signature" -> ent.signature ) ++ extras).asJava } @@ -74,6 +75,7 @@ object JavaConverters { "isPrivate" -> ent.isPrivate, "isProtected" -> ent.isProtected, "hasVisibleMembers" -> ent.hasVisibleMembers, + "hasShortenedDocstring" -> ent.hasShortenedDocstring, "hasCompanion" -> ent.hasCompanion, "companionPath" -> ent.companionPath.asJava, "signature" -> ent.signature @@ -95,6 +97,7 @@ object JavaConverters { "isPrivate" -> ent.isPrivate, "isProtected" -> ent.isProtected, "hasVisibleMembers" -> ent.hasVisibleMembers, + "hasShortenedDocstring" -> ent.hasShortenedDocstring, "hasCompanion" -> ent.hasCompanion, "companionPath" -> ent.companionPath.asJava, "signature" -> ent.signature @@ -115,6 +118,7 @@ object JavaConverters { "isPrivate" -> ent.isPrivate, "isProtected" -> ent.isProtected, "hasVisibleMembers" -> ent.hasVisibleMembers, + "hasShortenedDocstring" -> ent.hasShortenedDocstring, "hasCompanion" -> ent.hasCompanion, "companionPath" -> ent.companionPath.asJava, "signature" -> ent.signature @@ -134,6 +138,7 @@ object JavaConverters { "isPrivate" -> ent.isPrivate, "isProtected" -> ent.isProtected, "hasVisibleMembers" -> ent.hasVisibleMembers, + "hasShortenedDocstring" -> ent.hasShortenedDocstring, "hasCompanion" -> ent.hasCompanion, "companionPath" -> ent.companionPath.asJava, "signature" -> ent.signature @@ -152,6 +157,7 @@ object JavaConverters { "paramLists" -> ent.paramLists.map(_.asJava).asJava, "comment" -> ent.comment.map(_.asJava).asJava, "implicitlyAddedFrom" -> ent.implicitlyAddedFrom.map(_.asJava).asJava, + "hasShortenedDocstring" -> ent.hasShortenedDocstring, "isPrivate" -> ent.isPrivate, "isProtected" -> ent.isProtected, "signature" -> ent.signature @@ -168,6 +174,7 @@ object JavaConverters { "returnValue" -> ent.returnValue.asJava, "comment" -> ent.comment.map(_.asJava).asJava, "implicitlyAddedFrom" -> ent.implicitlyAddedFrom.map(_.asJava).asJava, + "hasShortenedDocstring" -> ent.hasShortenedDocstring, "isPrivate" -> ent.isPrivate, "isProtected" -> ent.isProtected, "signature" -> ent.signature @@ -183,6 +190,7 @@ object JavaConverters { "path" -> ent.path.asJava, "alias" -> ent.alias.map(_.asJava).asJava, "comment" -> ent.comment.map(_.asJava).asJava, + "hasShortenedDocstring" -> ent.hasShortenedDocstring, "isPrivate" -> ent.isPrivate, "isProtected" -> ent.isProtected, "signature" -> ent.signature diff --git a/doc-tool/src/dotty/tools/dottydoc/model/entities.scala b/doc-tool/src/dotty/tools/dottydoc/model/entities.scala index 9cbd1f6c8..d35077816 100644 --- a/doc-tool/src/dotty/tools/dottydoc/model/entities.scala +++ b/doc-tool/src/dotty/tools/dottydoc/model/entities.scala @@ -21,6 +21,9 @@ trait Entity { entity => def annotations: List[String] + def hasShortenedDocstring: Boolean = + comment.map(d => d.body.length > d.short.length).getOrElse(false) + def signature: String = entity.name + (entity match { case o: Object => "$" diff --git a/doc-tool/src/dotty/tools/dottydoc/util/traversing.scala b/doc-tool/src/dotty/tools/dottydoc/util/traversing.scala index 9c2e3bf54..956cb9291 100644 --- a/doc-tool/src/dotty/tools/dottydoc/util/traversing.scala +++ b/doc-tool/src/dotty/tools/dottydoc/util/traversing.scala @@ -24,7 +24,7 @@ object traversing { } - def rootPackages(pkgs: Map[String, Package]): List[String] = { + def rootPackages(pkgs: Map[String, Package]): List[Package] = { var currentDepth = Int.MaxValue var packs = List.empty[String] @@ -38,6 +38,6 @@ object traversing { key :: packs } else packs } - packs + packs.map(pkgs.apply) } } diff --git a/project/Build.scala b/project/Build.scala index f03111f76..376e06bb1 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -56,6 +56,9 @@ object DottyBuild extends Build { // Compiles the documentation and static site lazy val genDocs = inputKey[Unit]("run dottydoc to generate static documentation site") + // Shorthand for compiling a docs site + lazy val dottydoc = inputKey[Unit]("run dottydoc") + /** Dottydoc deps */ lazy val dottydocDeps = SettingKey[Seq[ModuleID]]( "dottydocDeps", @@ -273,17 +276,25 @@ object DottyBuild extends Build { ) }.evaluated, + dottydoc := Def.inputTaskDyn { + val args: Seq[String] = spaceDelimited("<arg>").parsed + val dottyLib = packageAll.value("dotty-library") + val dottyInterfaces = packageAll.value("dotty-interfaces") + val otherDeps = (dependencyClasspath in Compile).value.map(_.data).mkString(":") + val cp: Seq[String] = Seq("-classpath", s"$dottyLib:$dottyInterfaces:$otherDeps") + (runMain in Compile).toTask(s""" dotty.tools.dottydoc.Main ${cp.mkString(" ")} """ + args.mkString(" ")) + }.evaluated, + // Override run to be able to run compiled classfiles dotr := { val args: Seq[String] = spaceDelimited("<arg>").parsed - val java: String = Process("which" :: "java" :: Nil) !! - val scalaLib = (dependencyClasspath in Runtime, packageAll) - .map { (attList, _) => - attList - .map(_.data.getAbsolutePath) - .find(_.contains("scala-library")) - .toList.mkString(":") - }.value + val java: String = Process("which" :: "java" :: Nil).!! + val attList = (dependencyClasspath in Runtime).value + val _ = packageAll.value + val scalaLib = attList + .map(_.data.getAbsolutePath) + .find(_.contains("scala-library")) + .toList.mkString(":") if (java == "") println("Couldn't find java executable on path, please install java to a default location") @@ -291,7 +302,7 @@ object DottyBuild extends Build { println("Couldn't find scala-library on classpath, please run using script in bin dir instead") } else { val dottyLib = packageAll.value("dotty-library") - s"""$java -classpath .:$dottyLib:$scalaLib ${args.mkString(" ")}""" ! + s"""$java -classpath .:$dottyLib:$scalaLib ${args.mkString(" ")}""".! } }, run := Def.inputTaskDyn { @@ -327,7 +338,7 @@ object DottyBuild extends Build { partestLockFile.createNewFile partestLockFile.deleteOnExit }, - runPartestRunner <<= Def.inputTaskDyn { + runPartestRunner := Def.inputTaskDyn { // Magic! This is both an input task and a dynamic task. Apparently // command line arguments get passed to the last task in an aliased // sequence (see partest alias below), so this works. @@ -341,7 +352,7 @@ object DottyBuild extends Build { s"""-dottyJars ${jars.length + 2} dotty.jar dotty-lib.jar ${jars.mkString(" ")}""" // Provide the jars required on the classpath of run tests runTask(Test, "dotty.partest.DPConsoleRunner", dottyJars + " " + args.mkString(" ")) - }, + }.evaluated, /* Add the sources of scalajs-ir. * To guarantee that dotty can bootstrap without depending on a version @@ -386,7 +397,10 @@ object DottyBuild extends Build { // http://grokbase.com/t/gg/simple-build-tool/135ke5y90p/sbt-setting-jvm-boot-paramaters-for-scala // packageAll should always be run before tests - javaOptions <++= (dependencyClasspath in Runtime, packageAll) map { (attList, pA) => + javaOptions ++= { + val attList = (dependencyClasspath in Runtime).value + val pA = packageAll.value + // put needed dependencies on classpath: val path = for { file <- attList.map(_.data) @@ -632,7 +646,7 @@ object DottyInjectedPlugin extends AutoPlugin { /* Make sure jsDependencyManifest runs after compile, otherwise compile * might remove the entire directory afterwards. */ - jsDependencyManifest <<= jsDependencyManifest.dependsOn(compile) + jsDependencyManifest := jsDependencyManifest.dependsOn(compile).value ))) lazy val `dotty-bench` = project.in(file("bench")). @@ -652,7 +666,10 @@ object DottyInjectedPlugin extends AutoPlugin { parallelExecution in Test := false, // http://grokbase.com/t/gg/simple-build-tool/135ke5y90p/sbt-setting-jvm-boot-paramaters-for-scala - javaOptions <++= (dependencyClasspath in Runtime, packageBin in Compile) map { (attList, bin) => + javaOptions ++= { + val attList = (dependencyClasspath in Runtime).value + val bin = (packageBin in Compile).value + // put the Scala {library, reflect, compiler} in the classpath val path = for { file <- attList.map(_.data) diff --git a/tests/neg/customArgs/i2002.scala b/tests/neg/customArgs/i2002.scala new file mode 100644 index 000000000..5561b77b8 --- /dev/null +++ b/tests/neg/customArgs/i2002.scala @@ -0,0 +1,4 @@ +class Test { + def foo(i: Int): Int = i + def foo(implicit i: Int): Int = i // error +} diff --git a/tests/neg/i1747.scala b/tests/neg/i1747.scala new file mode 100644 index 000000000..54492aaed --- /dev/null +++ b/tests/neg/i1747.scala @@ -0,0 +1,3 @@ +class Coll[E] extends java.util.Collection[E] { // error: needs to be abstract + def toArray[T](a: Array[T]): Array[T] = ??? // error: cannot override +} diff --git a/tests/neg/i1992.scala b/tests/neg/i1992.scala new file mode 100644 index 000000000..818b46771 --- /dev/null +++ b/tests/neg/i1992.scala @@ -0,0 +1,9 @@ +object Test { + def main(args: Array[String]) = { + val x: Int => Unit = + y => println(x) // error: `x` is a forward reference + implicit val z: String => Unit = + y => println(implicitly[String => Unit]) // error: `z` is a forward reference + } +} + diff --git a/tests/neg/i2000.scala b/tests/neg/i2000.scala new file mode 100644 index 000000000..aa1250f08 --- /dev/null +++ b/tests/neg/i2000.scala @@ -0,0 +1,23 @@ +object test1 { + class C[A] { def foo(a: A) = "c" } + class D extends C[String] { override def foo(implicit s: String) = "d" } // error +} + +object test2 { + class C[A] { final def foo(a: A) = "c" } + class D extends C[String] { def foo(implicit s: String) = "d" } // error + object Test { + def main(args: Array[String]) = + new D + } +} + +object test3 { + class A { + def foo(implicit i: Int): Int = i + i + } + + class B extends A { + override def foo(i: Int): Int = i // error + } +} diff --git a/tests/neg/i2005.scala b/tests/neg/i2005.scala new file mode 100644 index 000000000..fc2e4ddec --- /dev/null +++ b/tests/neg/i2005.scala @@ -0,0 +1,7 @@ + +object Test { + val a = 42 + def main(args: Array[String]) = { + val a: Int = a // error + } +} diff --git a/tests/neg/i2006.scala b/tests/neg/i2006.scala new file mode 100644 index 000000000..f1b48b011 --- /dev/null +++ b/tests/neg/i2006.scala @@ -0,0 +1,10 @@ +object Test { + + inline def foo(f: ImplicitFunction1[Int, Int]): AnyRef = f // error + inline def bar(f: ImplicitFunction1[Int, Int]) = f // error + + def main(args: Array[String]) = { + foo(implicit thisTransaction => 43) + bar(implicit thisTransaction => 44) + } +} diff --git a/tests/pos/i2009.scala b/tests/pos/i2009.scala new file mode 100644 index 000000000..e2cf47203 --- /dev/null +++ b/tests/pos/i2009.scala @@ -0,0 +1,9 @@ +object Test { + + trait Gen[T] { + def map[U](f: T => U): Gen[U] = ??? + } + + def f[T](implicit g: Gen[T]): Gen[() => T] = + g map ( () => _ ) +} diff --git a/tests/repl/imports.check b/tests/repl/imports.check index 5260589a9..345fac142 100644 --- a/tests/repl/imports.check +++ b/tests/repl/imports.check @@ -11,7 +11,7 @@ scala> buf += xs 11 |buf += xs | ^^ | found: scala.collection.immutable.List[Int](o.xs) - | required: String + | required: Int | scala> buf ++= xs val res1: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3) diff --git a/tests/run/i2020.scala b/tests/run/i2020.scala new file mode 100644 index 000000000..78794a590 --- /dev/null +++ b/tests/run/i2020.scala @@ -0,0 +1,8 @@ +object Test { + + case class Foo(x: Int)(y: Int) + + def main(args: Array[String]) = + assert(Foo(1)(1) == Foo(1)(2)) + +} |