diff options
Diffstat (limited to 'src')
14 files changed, 164 insertions, 95 deletions
diff --git a/src/build/pack.xml b/src/build/pack.xml index 381d3f1931..20c4034107 100644 --- a/src/build/pack.xml +++ b/src/build/pack.xml @@ -112,8 +112,10 @@ MAIN DISTRIBUTION PACKAGING </target> <target name="pack-archives.latest.unix" depends="pack-archives.src" unless="os.win"> + <!-- be sure to use a relative symlink to make the distribution portable, + `resource` is relative to directory of `link` --> <symlink link="${dists.dir}/archives/scala-latest-sources.tgz" - resource="${dists.dir}/archives/scala-${version.number}-sources.tgz" + resource="scala-${version.number}-sources.tgz" overwrite="yes"/> </target> diff --git a/src/compiler/scala/tools/ant/templates/tool-windows.tmpl b/src/compiler/scala/tools/ant/templates/tool-windows.tmpl index 411a27b9c7..bd6cf561b9 100644 --- a/src/compiler/scala/tools/ant/templates/tool-windows.tmpl +++ b/src/compiler/scala/tools/ant/templates/tool-windows.tmpl @@ -14,8 +14,9 @@ set _LINE_TOOLCP= :another_param -if "%1%"=="-toolcp" ( - set _LINE_TOOLCP=%2% +rem Use "%~1" to handle spaces in paths. See http://ss64.com/nt/syntax-args.html +if "%~1"=="-toolcp" ( + set _LINE_TOOLCP=%~2 shift shift goto another_param diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 2f5cb23abb..998b7f3de8 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -470,7 +470,7 @@ abstract class UnCurry extends InfoTransform val fn1 = withInPattern(value = false)(transform(fn)) val args1 = transformTrees(fn.symbol.name match { case nme.unapply => args - case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, analyzer.unapplyTypeList(fn.pos, fn.symbol, fn.tpe, args.length)) + case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, analyzer.unapplyTypeList(fn.pos, fn.symbol, fn.tpe, args)) case _ => sys.error("internal error: UnApply node has wrong symbol") }) treeCopy.UnApply(tree, fn1, args1) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index 92b7700c04..7508843a48 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -384,18 +384,35 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { // else debug.patmat("NOT implies: "+(lower, upper)) - /* does V = C preclude V having value `other`? - (1) V = null is an exclusive assignment, - (2) V = A and V = B, for A and B value constants, are mutually exclusive unless A == B - we err on the safe side, for example: - - assume `val X = 1; val Y = 1`, then - (2: Int) match { case X => case Y => <falsely considered reachable> } - - V = 1 does not preclude V = Int, or V = Any, it could be said to preclude V = String, but we don't model that - - (3) for types we could try to do something fancy, but be conservative and just say no + /** Does V=A preclude V=B? + * + * (0) A or B must be in the domain to draw any conclusions. + * + * For example, knowing the the scrutinee is *not* true does not + * statically exclude it from being `X`, because that is an opaque + * Boolean. + * + * val X = true + * (true: Boolean) match { case true => case X <reachable> } + * + * (1) V = null excludes assignment to any other constant (modulo point #0). This includes + * both values and type tests (which are both modelled here as `Const`) + * (2) V = A and V = B, for A and B domain constants, are mutually exclusive unless A == B + * + * (3) We only reason about test tests as being excluded by null assignments, otherwise we + * only consider value assignments. + * TODO: refine this, a == 0 excludes a: String, or `a: Int` excludes `a: String` + * (since no value can be of both types. See also SI-7211) + * + * NOTE: V = 1 does not preclude V = Int, or V = Any, it could be said to preclude + * V = String, but we don't model that. */ - def excludes(a: Const, b: Const): Boolean = - a != b && ((a == NullConst || b == NullConst) || (a.isValue && b.isValue)) + def excludes(a: Const, b: Const): Boolean = { + val bothInDomain = domain exists (d => d(a) && d(b)) + val eitherIsNull = a == NullConst || b == NullConst + val bothAreValues = a.isValue && b.isValue + bothInDomain && (eitherIsNull || bothAreValues) && (a != b) + } // if(r) debug.patmat("excludes : "+(a, a.tp, b, b.tp)) // else debug.patmat("NOT excludes: "+(a, b)) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index a0660ce71d..61cdb63ac9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -606,6 +606,10 @@ trait ContextErrors { setError(tree) } + // typedPattern + def PatternMustBeValue(pat: Tree, pt: Type) = + issueNormalTypeError(pat, s"pattern must be a value: $pat"+ typePatternAdvice(pat.tpe.typeSymbol, pt.typeSymbol)) + // SelectFromTypeTree def TypeSelectionFromVolatileTypeError(tree: Tree, qual: Tree) = { val hiBound = qual.tpe.bounds.hi @@ -951,33 +955,10 @@ trait ContextErrors { def IncompatibleScrutineeTypeError(tree: Tree, pattp: Type, pt: Type) = issueNormalTypeError(tree, "scrutinee is incompatible with pattern type" + foundReqMsg(pattp, pt)) - def PatternTypeIncompatibleWithPtError2(pat: Tree, pt1: Type, pt: Type) = { - def errMsg = { - val sym = pat.tpe.typeSymbol - val clazz = sym.companionClass - val addendum = ( - if (sym.isModuleClass && clazz.isCaseClass && (clazz isSubClass pt1.typeSymbol)) { - // TODO: move these somewhere reusable. - val typeString = clazz.typeParams match { - case Nil => "" + clazz.name - case xs => xs map (_ => "_") mkString (clazz.name + "[", ",", "]") - } - val caseString = ( - clazz.caseFieldAccessors - map (_ => "_") // could use the actual param names here - mkString (clazz.name + "(", ",", ")") - ) - ( - "\nNote: if you intended to match against the class, try `case _: " + - typeString + "` or `case " + caseString + "`" - ) - } - else "" - ) - "pattern type is incompatible with expected type"+foundReqMsg(pat.tpe, pt) + addendum - } - issueNormalTypeError(pat, errMsg) - } + def PatternTypeIncompatibleWithPtError2(pat: Tree, pt1: Type, pt: Type) = + issueNormalTypeError(pat, + "pattern type is incompatible with expected type"+ foundReqMsg(pat.tpe, pt) + + typePatternAdvice(pat.tpe.typeSymbol, pt1.typeSymbol)) def PolyAlternativeError(tree: Tree, argtypes: List[Type], sym: Symbol, err: PolyAlternativeErrorKind.ErrorType) = { import PolyAlternativeErrorKind._ diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 056cf61db5..aaa6c66630 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -69,6 +69,10 @@ trait Infer extends Checkable { * * `formals` are the formal types before expanding a potential repeated parameter (must come last in `formals`, if at all) * + * @param nbSubPats The number of arguments to the extractor pattern + * @param effectiveNbSubPats `nbSubPats`, unless there is one sub-pattern which, after unwrapping + * bind patterns, is a Tuple pattern, in which case it is the number of + * elements. Used to issue warnings about binding a `TupleN` to a single value. * @throws TypeError when the unapply[Seq] definition is ill-typed * @returns (null, null) when the expected number of sub-patterns cannot be satisfied by the given extractor * @@ -98,7 +102,8 @@ trait Infer extends Checkable { * `T_1, ..., T_m, U, ..., U`. Here, `U` is repeated `n-m` times. * */ - def extractorFormalTypes(pos: Position, resTp: Type, nbSubPats: Int, unappSym: Symbol): (List[Type], List[Type]) = { + def extractorFormalTypes(pos: Position, resTp: Type, nbSubPats: Int, + unappSym: Symbol, effectiveNbSubPats: Int): (List[Type], List[Type]) = { val isUnapplySeq = unappSym.name == nme.unapplySeq val booleanExtractor = resTp.typeSymbolDirect == BooleanClass @@ -128,8 +133,9 @@ trait Infer extends Checkable { else if (optionArgs.nonEmpty) if (nbSubPats == 1) { val productArity = productArgs.size - if (productArity > 1 && settings.lint) - global.currentUnit.warning(pos, s"extractor pattern binds a single value to a Product${productArity} of type ${optionArgs.head}") + if (productArity > 1 && productArity != effectiveNbSubPats && settings.lint) + global.currentUnit.warning(pos, + s"extractor pattern binds a single value to a Product${productArity} of type ${optionArgs.head}") optionArgs } // TODO: update spec to reflect we allow any ProductN, not just TupleN diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 5ef54bfad1..290ad76a1d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -159,7 +159,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // This has become noisy with implicit classes. if (settings.lint && settings.developer) { clazz.info.decls filter (x => x.isImplicit && x.typeParams.nonEmpty) foreach { sym => - val alts = clazz.info.decl(sym.name).alternatives + // implicit classes leave both a module symbol and a method symbol as residue + val alts = clazz.info.decl(sym.name).alternatives filterNot (_.isModule) if (alts.size > 1) alts foreach (x => unit.warning(x.pos, "parameterized overloaded implicit methods are not visible as view bounds")) } @@ -980,12 +981,15 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val s = fn.symbol (s == Object_==) || (s == Object_!=) || (s == Any_==) || (s == Any_!=) } + def haveSubclassRelationship = (actual isSubClass receiver) || (receiver isSubClass actual) + // Whether the operands+operator represent a warnable combo (assuming anyrefs) // Looking for comparisons performed with ==/!= in combination with either an // equals method inherited from Object or a case class synthetic equals (for // which we know the logic.) def isWarnable = isReferenceOp || (isUsingDefaultScalaOp && isUsingWarnableEquals) def isEitherNullable = (NullClass.tpe <:< receiver.info) || (NullClass.tpe <:< actual.info) + def isEitherValueClass = actual.isDerivedValueClass || receiver.isDerivedValueClass def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s) @@ -997,6 +1001,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // used to short-circuit unrelatedTypes check if both sides are special def isSpecial(s: Symbol) = isMaybeAnyValue(s) || isAnyNumber(s) val nullCount = onSyms(_ filter (_ == NullClass) size) + def isNonsenseValueClassCompare = ( + !haveSubclassRelationship + && isUsingDefaultScalaOp + && isEitherValueClass + && !isCaseEquals + ) def nonSensibleWarning(what: String, alwaysEqual: Boolean) = { val msg = alwaysEqual == (name == nme.EQ || name == nme.eq) @@ -1008,10 +1018,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def nonSensiblyNeq() = nonSensible("", false) def nonSensiblyNew() = nonSensibleWarning("a fresh object", false) + def unrelatedMsg = name match { + case nme.EQ | nme.eq => "never compare equal" + case _ => "always compare unequal" + } def unrelatedTypes() = { - val msg = if (name == nme.EQ || name == nme.eq) - "never compare equal" else "always compare unequal" - unit.warning(pos, typesString + " are unrelated: they will most likely " + msg) + val weaselWord = if (isEitherValueClass) "" else " most likely" + unit.warning(pos, s"$typesString are unrelated: they will$weaselWord $unrelatedMsg") } if (nullCount == 2) // null == null @@ -1050,15 +1063,19 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } } + // warn if one but not the other is a derived value class + // this is especially important to enable transitioning from + // regular to value classes without silent failures. + if (isNonsenseValueClassCompare) + unrelatedTypes() // possibleNumericCount is insufficient or this will warn on e.g. Boolean == j.l.Boolean - if (isWarnable && nullCount == 0 && !(isSpecial(receiver) && isSpecial(actual))) { + else if (isWarnable && nullCount == 0 && !(isSpecial(receiver) && isSpecial(actual))) { // better to have lubbed and lost def warnIfLubless(): Unit = { val common = global.lub(List(actual.tpe, receiver.tpe)) if (ObjectClass.tpe <:< common) unrelatedTypes() } - def eitherSubclasses = (actual isSubClass receiver) || (receiver isSubClass actual) // warn if actual has a case parent that is not same as receiver's; // if actual is not a case, then warn if no common supertype, as below if (isCaseEquals) { @@ -1071,14 +1088,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans //else // if a class, it must be super to thisCase (and receiver) since not <: thisCase if (!actual.isTrait && !(receiver isSubClass actual)) nonSensiblyNeq() - else if (!eitherSubclasses) warnIfLubless() + else if (!haveSubclassRelationship) warnIfLubless() case _ => } } - else if (actual isSubClass receiver) () - else if (receiver isSubClass actual) () // warn only if they have no common supertype below Object - else { + else if (!haveSubclassRelationship) { warnIfLubless() } } @@ -1276,6 +1291,16 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } } + private def checkDelayedInitSelect(qual: Tree, sym: Symbol, pos: Position) = { + def isLikelyUninitialized = ( + (sym.owner isSubClass DelayedInitClass) + && !qual.tpe.isInstanceOf[ThisType] + && sym.accessedOrSelf.isVal + ) + if (settings.lint.value && isLikelyUninitialized) + unit.warning(pos, s"Selecting ${sym} from ${sym.owner}, which extends scala.DelayedInit, is likely to yield an uninitialized value") + } + private def lessAccessible(otherSym: Symbol, memberSym: Symbol): Boolean = ( (otherSym != NoSymbol) && !otherSym.isProtected @@ -1479,6 +1504,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans if(settings.Xmigration.value != NoScalaVersion) checkMigration(sym, tree.pos) checkCompileTimeOnly(sym, tree.pos) + checkDelayedInitSelect(qual, sym, tree.pos) if (sym eq NoSymbol) devWarning("Select node has NoSymbol! " + tree + " / " + tree.tpe) diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 5f45fead91..5146cf6fa7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -271,6 +271,24 @@ trait TypeDiagnostics { ) } + def typePatternAdvice(sym: Symbol, ptSym: Symbol) = { + val clazz = if (sym.isModuleClass) sym.companionClass else sym + val caseString = + if (clazz.isCaseClass && (clazz isSubClass ptSym)) + ( clazz.caseFieldAccessors + map (_ => "_") // could use the actual param names here + mkString (s"`case ${clazz.name}(", ",", ")`") + ) + else + "`case _: " + (clazz.typeParams match { + case Nil => "" + clazz.name + case xs => xs map (_ => "_") mkString (clazz.name + "[", ",", "]") + })+ "`" + + "\nNote: if you intended to match against the class, try "+ caseString + + } + case class TypeDiag(tp: Type, sym: Symbol) extends Ordered[TypeDiag] { // save the name because it will be mutated until it has been // distinguished from the other types in the same error message diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 4b7260c798..f501ff468d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3411,8 +3411,8 @@ trait Typers extends Adaptations with Tags { else { val resTp = fun1.tpe.finalResultType.dealiasWiden val nbSubPats = args.length - - val (formals, formalsExpanded) = extractorFormalTypes(fun0.pos, resTp, nbSubPats, fun1.symbol) + val (formals, formalsExpanded) = + extractorFormalTypes(fun0.pos, resTp, nbSubPats, fun1.symbol, treeInfo.effectivePatternArity(args)) if (formals == null) duplErrorTree(WrongNumberOfArgsError(tree, fun)) else { val args1 = typedArgs(args, mode, formals, formalsExpanded) @@ -4952,7 +4952,7 @@ trait Typers extends Adaptations with Tags { def typedUnApply(tree: UnApply) = { val fun1 = typed(tree.fun) - val tpes = formalTypes(unapplyTypeList(tree.fun.pos, tree.fun.symbol, fun1.tpe, tree.args.length), tree.args.length) + val tpes = formalTypes(unapplyTypeList(tree.fun.pos, tree.fun.symbol, fun1.tpe, tree.args), tree.args.length) val args1 = map2(tree.args, tpes)(typedPattern) treeCopy.UnApply(tree, fun1, args1) setType pt } @@ -5396,7 +5396,10 @@ trait Typers extends Adaptations with Tags { // as a compromise, context.enrichmentEnabled tells adaptToMember to go ahead and enrich, // but arbitrary conversions (in adapt) are disabled // TODO: can we achieve the pattern matching bit of the string interpolation SIP without this? - typingInPattern(context.withImplicitsDisabledAllowEnrichment(typed(tree, PATTERNmode, pt))) + typingInPattern(context.withImplicitsDisabledAllowEnrichment(typed(tree, PATTERNmode, pt))) match { + case tpt if tpt.isType => PatternMustBeValue(tpt, pt); tpt + case pat => pat + } } /** Types a (fully parameterized) type tree */ diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index d55dce70c7..af3f772f79 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -33,12 +33,13 @@ trait Unapplies extends ast.TreeDSL /** returns type list for return type of the extraction * @see extractorFormalTypes */ - def unapplyTypeList(pos: Position, ufn: Symbol, ufntpe: Type, nbSubPats: Int) = { + def unapplyTypeList(pos: Position, ufn: Symbol, ufntpe: Type, args: List[Tree]) = { assert(ufn.isMethod, ufn) + val nbSubPats = args.length //Console.println("utl "+ufntpe+" "+ufntpe.typeSymbol) ufn.name match { case nme.unapply | nme.unapplySeq => - val (formals, _) = extractorFormalTypes(pos, unapplyUnwrap(ufntpe), nbSubPats, ufn) + val (formals, _) = extractorFormalTypes(pos, unapplyUnwrap(ufntpe), nbSubPats, ufn, treeInfo.effectivePatternArity(args)) if (formals == null) throw new TypeError(s"$ufn of type $ufntpe cannot extract $nbSubPats sub-patterns") else formals case _ => throw new TypeError(ufn+" is not an unapply or unapplySeq") diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala index b6f4779bc4..c9b4603d74 100644 --- a/src/interactive/scala/tools/nsc/interactive/Global.scala +++ b/src/interactive/scala/tools/nsc/interactive/Global.scala @@ -905,9 +905,9 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") /** Implements CompilerControl.askDocComment */ private[interactive] def getDocComment(sym: Symbol, source: SourceFile, site: Symbol, fragments: List[(Symbol,SourceFile)], response: Response[(String, String, Position)]) { - informIDE(s"getDocComment $sym at $source site $site") + informIDE(s"getDocComment $sym at $source, site $site") respond(response) { - withTempUnits(fragments.toList.unzip._2){ units => + withTempUnits(fragments.unzip._2){ units => for((sym, src) <- fragments) { val mirror = findMirrorSymbol(sym, units(src)) if (mirror ne NoSymbol) forceDocComment(mirror, units(src)) @@ -920,6 +920,8 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } } } + // New typer run to remove temp units and drop per-run caches that might refer to symbols entered from temp units. + newTyperRun() } def stabilizedType(tree: Tree): Type = tree match { diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index bf3857ea4e..de872bb762 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -17,7 +17,7 @@ abstract class TreeInfo { val global: SymbolTable import global._ - import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass, TupleClass, MacroContextClass, MacroContextPrefixType } + import definitions.{ isTupleSymbol, isVarArgsList, isCastSymbol, ThrowableClass, TupleClass, MacroContextClass, MacroContextPrefixType } /* Does not seem to be used. Not sure what it does anyway. def isOwnerDefinition(tree: Tree): Boolean = tree match { @@ -531,6 +531,20 @@ abstract class TreeInfo { case _ => false } + /** + * {{{ + * //------------------------ => effectivePatternArity(args) + * case Extractor(a) => 1 + * case Extractor(a, b) => 2 + * case Extractor((a, b)) => 2 + * case Extractor(a @ (b, c)) => 2 + * }}} + */ + def effectivePatternArity(args: List[Tree]): Int = (args.map(unbind) match { + case Apply(fun, xs) :: Nil if isTupleSymbol(fun.symbol) => xs + case xs => xs + }).length + // used in the symbols for labeldefs and valdefs emitted by the pattern matcher // tailcalls, cps,... use this flag combination to detect translated matches diff --git a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala index cc228082c1..3831f64fab 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala @@ -837,36 +837,36 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } } - /** */ def makeAnnotation(annot: AnnotationInfo): scala.tools.nsc.doc.model.Annotation = { val aSym = annot.symbol new EntityImpl(aSym, makeTemplate(aSym.owner)) with scala.tools.nsc.doc.model.Annotation { lazy val annotationClass = makeTemplate(annot.symbol) - val arguments = { // lazy - def annotArgs = annot.args match { - case Nil => annot.assocs collect { case (_, LiteralAnnotArg(const)) => Literal(const) } - case xs => xs - } - def noParams = annotArgs map (_ => None) - - val params: List[Option[ValueParam]] = annotationClass match { + val arguments = { + val paramsOpt: Option[List[ValueParam]] = annotationClass match { case aClass: DocTemplateEntity with Class => - (aClass.primaryConstructor map { _.valueParams.head }) match { - case Some(vps) => vps map { Some(_) } - case _ => noParams + val constr = aClass.constructors collectFirst { + case c: MemberImpl if c.sym == annot.original.symbol => c } - case _ => noParams + constr flatMap (_.valueParams.headOption) + case _ => None } - assert(params.length == annotArgs.length, (params, annotArgs)) - - params zip annotArgs flatMap { case (param, arg) => - makeTree(arg) map { tree => - new ValueArgument { - def parameter = param - def value = tree + val argTrees = annot.args map makeTree + paramsOpt match { + case Some (params) => + params zip argTrees map { case (param, tree) => + new ValueArgument { + def parameter = Some(param) + def value = tree + } + } + case None => + argTrees map { tree => + new ValueArgument { + def parameter = None + def value = tree + } } - } } } } @@ -906,9 +906,8 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { sym.name == aSym.name && sym.isParamWithDefault ) - (unit.body find (t => isCorrespondingParam(t.symbol))) match { - case Some(ValDef(_,_,_,rhs)) => makeTree(rhs) - case _ => None + unit.body find (t => isCorrespondingParam(t.symbol)) collect { + case ValDef(_,_,_,rhs) if rhs ne EmptyTree => makeTree(rhs) } case _ => None } diff --git a/src/scaladoc/scala/tools/nsc/doc/model/TreeFactory.scala b/src/scaladoc/scala/tools/nsc/doc/model/TreeFactory.scala index b972649194..b381176b17 100755 --- a/src/scaladoc/scala/tools/nsc/doc/model/TreeFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/TreeFactory.scala @@ -19,7 +19,7 @@ trait TreeFactory { thisTreeFactory: ModelFactory with TreeFactory => val global: Global import global._ - def makeTree(rhs: Tree): Option[TreeEntity] = { + def makeTree(rhs: Tree): TreeEntity = { val expr = new StringBuilder var refs = new immutable.TreeMap[Int, (Entity, Int)] // start, (Entity to be linked to , end) @@ -80,17 +80,16 @@ trait TreeFactory { thisTreeFactory: ModelFactory with TreeFactory => traverser.traverse(rhs) - Some(new TreeEntity { + new TreeEntity { val expression = expr.toString val refEntity = refs - }) + } } - case pos: OffsetPosition => - Some(new TreeEntity { + case _ => + new TreeEntity { val expression = rhs.toString val refEntity = new immutable.TreeMap[Int, (Entity, Int)] - }) - case _ => None + } } } } |