Skip to content

Commit d6f601d

Browse files
authored
Merge pull request scala#5332 from retronym/review/5304
Fixes to Java source support in Scaladoc
2 parents 69c6500 + 36732d7 commit d6f601d

File tree

15 files changed

+408
-47
lines changed

15 files changed

+408
-47
lines changed

src/compiler/scala/tools/nsc/ast/TreeInfo.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,12 @@ abstract class TreeInfo extends scala.reflect.internal.TreeInfo {
9797
case DocDef(_, definition) => isPureDef(definition)
9898
case _ => super.isPureDef(tree)
9999
}
100+
101+
override def firstConstructor(stats: List[Tree]): Tree = {
102+
def unwrap(stat: Tree): Tree = stat match {
103+
case DocDef(_, defn) => unwrap(defn)
104+
case tree => tree
105+
}
106+
super.firstConstructor(stats map unwrap)
107+
}
100108
}

src/compiler/scala/tools/nsc/javac/JavaParsers.scala

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,8 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
117117
atPos(pkg.pos) { PackageDef(pkg, stats) }
118118

119119
def makeTemplate(parents: List[Tree], stats: List[Tree]) =
120-
Template(
121-
parents,
122-
noSelfType,
123-
if (treeInfo.firstConstructor(stats) == EmptyTree) makeConstructor(List()) :: stats
124-
else stats)
120+
Template(parents, noSelfType, if (treeInfo.firstConstructor(stats) == EmptyTree)
121+
makeConstructor(Nil) :: stats else stats)
125122

126123
def makeSyntheticParam(count: Int, tpt: Tree): ValDef =
127124
makeParam(nme.syntheticParamName(count), tpt)
@@ -586,7 +583,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
586583
case CLASS | ENUM | INTERFACE | AT =>
587584
typeDecl(if (definesInterface(parentToken)) mods | Flags.STATIC else mods)
588585
case _ =>
589-
joinComment(termDecl(mods, parentToken))
586+
termDecl(mods, parentToken)
590587
}
591588

592589
def makeCompanionObject(cdef: ClassDef, statics: List[Tree]): Tree =
@@ -600,26 +597,8 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
600597
Import(Ident(cdef.name.toTermName), ImportSelector.wildList)
601598
}
602599

603-
// Importing the companion object members cannot be done uncritically: see
604-
// ticket #2377 wherein a class contains two static inner classes, each of which
605-
// has a static inner class called "Builder" - this results in an ambiguity error
606-
// when each performs the import in the enclosing class's scope.
607-
//
608-
// To address this I moved the import Companion._ inside the class, as the first
609-
// statement. This should work without compromising the enclosing scope, but may (?)
610-
// end up suffering from the same issues it does in scala - specifically that this
611-
// leaves auxiliary constructors unable to access members of the companion object
612-
// as unqualified identifiers.
613-
def addCompanionObject(statics: List[Tree], cdef: ClassDef): List[Tree] = {
614-
def implWithImport(importStmt: Tree) = deriveTemplate(cdef.impl)(importStmt :: _)
615-
// if there are no statics we can use the original cdef, but we always
616-
// create the companion so import A._ is not an error (see ticket #1700)
617-
val cdefNew =
618-
if (statics.isEmpty) cdef
619-
else deriveClassDef(cdef)(_ => implWithImport(importCompanionObject(cdef)))
620-
621-
List(makeCompanionObject(cdefNew, statics), cdefNew)
622-
}
600+
def addCompanionObject(statics: List[Tree], cdef: ClassDef): List[Tree] =
601+
List(makeCompanionObject(cdef, statics), cdef)
623602

624603
def importDecl(): List[Tree] = {
625604
accept(IMPORT)
@@ -726,8 +705,15 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
726705
in.nextToken()
727706
} else {
728707
if (in.token == ENUM || definesInterface(in.token)) mods |= Flags.STATIC
729-
val decls = memberDecl(mods, parentToken)
730-
(if (mods.hasStaticFlag || inInterface && !(decls exists (_.isInstanceOf[DefDef])))
708+
val decls = joinComment(memberDecl(mods, parentToken))
709+
710+
def isDefDef(tree: Tree): Boolean = tree match {
711+
case _: DefDef => true
712+
case DocDef(_, defn) => isDefDef(defn)
713+
case _ => false
714+
}
715+
716+
(if (mods.hasStaticFlag || inInterface && !(decls exists isDefDef))
731717
statics
732718
else
733719
members) ++= decls

src/compiler/scala/tools/nsc/typechecker/Contexts.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1016,7 +1016,16 @@ trait Contexts { self: Analyzer =>
10161016
|| unit.exists && s.sourceFile != unit.source.file
10171017
)
10181018
)
1019-
def lookupInPrefix(name: Name) = pre member name filter qualifies
1019+
def lookupInPrefix(name: Name) = {
1020+
val sym = pre.member(name).filter(qualifies)
1021+
def isNonPackageNoModuleClass(sym: Symbol) =
1022+
sym.isClass && !sym.isModuleClass && !sym.isPackageClass
1023+
if (!sym.exists && unit.isJava && isNonPackageNoModuleClass(pre.typeSymbol)) {
1024+
// TODO factor out duplication with Typer::inCompanionForJavaStatic
1025+
val pre1 = companionSymbolOf(pre.typeSymbol, this).typeOfThis
1026+
pre1.member(name).filter(qualifies).andAlso(_ => pre = pre1)
1027+
} else sym
1028+
}
10201029
def accessibleInPrefix(s: Symbol) = isAccessible(s, pre, superAccess = false)
10211030

10221031
def searchPrefix = {

src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ trait MethodSynthesis {
201201

202202
import AnnotationInfo.{mkFilter => annotationFilter}
203203
def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match {
204-
case vd @ ValDef(mods, name, tpt, rhs) if deriveAccessors(vd) && !vd.symbol.isModuleVar =>
204+
case vd @ ValDef(mods, name, tpt, rhs) if deriveAccessors(vd) && !vd.symbol.isModuleVar && !vd.symbol.isJava =>
205205
stat.symbol.initialize // needed!
206206

207207
val getter = Getter(vd)

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4719,6 +4719,16 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
47194719
if (isStableContext(tree, mode, pt)) tree setType clazz.thisType else tree
47204720
}
47214721

4722+
4723+
// For Java, instance and static members are in the same scope, but we put the static ones in the companion object
4724+
// so, when we can't find a member in the class scope, check the companion
4725+
def inCompanionForJavaStatic(pre: Type, cls: Symbol, name: Name): Symbol =
4726+
if (!(context.unit.isJava && cls.isClass && !cls.isModuleClass)) NoSymbol else {
4727+
val companion = companionSymbolOf(cls, context)
4728+
if (!companion.exists) NoSymbol
4729+
else member(gen.mkAttributedRef(pre, companion), name) // assert(res.isStatic, s"inCompanionJavaStatic($pre, $cls, $name) = $res ${res.debugFlagString}")
4730+
}
4731+
47224732
/* Attribute a selection where `tree` is `qual.name`.
47234733
* `qual` is already attributed.
47244734
*/
@@ -4745,7 +4755,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
47454755
dyna.wrapErrors(t, (_.typed1(t, mode, pt)))
47464756
}
47474757

4748-
val sym = tree.symbol orElse member(qual, name) orElse {
4758+
val sym = tree.symbol orElse member(qual, name) orElse inCompanionForJavaStatic(qual.tpe.prefix, qual.symbol, name) orElse {
47494759
// symbol not found? --> try to convert implicitly to a type that does have the required
47504760
// member. Added `| PATTERNmode` to allow enrichment in patterns (so we can add e.g., an
47514761
// xml member to StringContext, which in turn has an unapply[Seq] method)

src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,16 @@ abstract class ScaladocSyntaxAnalyzer[G <: Global](val global: G) extends Syntax
216216

217217
class ScaladocJavaUnitScanner(unit: CompilationUnit) extends JavaUnitScanner(unit) {
218218

219-
private val docBuffer: StringBuilder = new StringBuilder
219+
private var docBuffer: StringBuilder = _
220220
private var inDocComment = false
221221
private var docStart: Int = 0
222222
private var lastDoc: DocComment = null
223223

224+
override def init() = {
225+
docBuffer = new StringBuilder
226+
super.init()
227+
}
228+
224229
// get last doc comment
225230
def flushDoc(): DocComment = try lastDoc finally lastDoc = null
226231

src/scaladoc/scala/tools/nsc/doc/ScaladocGlobal.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ trait ScaladocGlobalTrait extends Global {
1313

1414
override val useOffsetPositions = false
1515
override def newUnitParser(unit: CompilationUnit) = new syntaxAnalyzer.ScaladocUnitParser(unit, Nil)
16-
override def newJavaUnitParser(unit: CompilationUnit) = new syntaxAnalyzer.ScaladocJavaUnitParser(unit)
16+
override def newJavaUnitParser(unit: CompilationUnit) = if (createJavadoc) {
17+
new syntaxAnalyzer.ScaladocJavaUnitParser(unit)
18+
} else {
19+
super.newJavaUnitParser(unit)
20+
}
1721

1822
override lazy val syntaxAnalyzer = new ScaladocSyntaxAnalyzer[outer.type](outer) {
1923
val runsAfter = List[String]()
@@ -41,7 +45,7 @@ class ScaladocGlobal(settings: doc.Settings, reporter: Reporter) extends Global(
4145
phasesSet += analyzer.typerFactory
4246
}
4347
override def forScaladoc = true
44-
override def createJavadoc = true
48+
override def createJavadoc = if (settings.docNoJavaComments.value) false else true
4549

4650
override lazy val analyzer = new {
4751
val global: ScaladocGlobal.this.type = ScaladocGlobal.this

src/scaladoc/scala/tools/nsc/doc/Settings.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,11 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_))
213213
"Group similar functions together (based on the @group annotation)"
214214
)
215215

216+
val docNoJavaComments = BooleanSetting (
217+
"-no-java-comments",
218+
"Prevents parsing and inclusion of comments from java sources."
219+
)
220+
216221
// For improved help output.
217222
def scaladocSpecific = Set[Settings#Setting](
218223
docformat, doctitle, docfooter, docversion, docUncompilable, docsourceurl, docgenerator, docRootContent, useStupidTypes,
@@ -222,7 +227,7 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_))
222227
docImplicits, docImplicitsDebug, docImplicitsShowAll, docImplicitsHide, docImplicitsSoundShadowing,
223228
docDiagramsMaxNormalClasses, docDiagramsMaxImplicitClasses,
224229
docNoPrefixes, docNoLinkWarnings, docRawOutput, docSkipPackages,
225-
docExpandAllTypes, docGroups
230+
docExpandAllTypes, docGroups, docNoJavaComments
226231
)
227232
val isScaladocSpecific: String => Boolean = scaladocSpecific map (_.name)
228233

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package scala.tools.partest
2+
3+
import scala.tools.nsc.doc.Universe
4+
5+
/** A class for testing scaladoc model generation on java sources. */
6+
abstract class ScaladocJavaModelTest extends ScaladocModelTest {
7+
8+
// overridden to pass explicit files to newDocFactory.makeUniverse (rather than code strings)
9+
// since the .java file extension is required
10+
override def model: Option[Universe] = {
11+
val path = resourcePath + "/" + resourceFile
12+
newDocFactory.makeUniverse(Left(List(path)))
13+
}
14+
15+
}

test/files/pos/t2377b/Q.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
public class Q {
2+
3+
public static class Builder {}
4+
5+
public static class Inner {
6+
public static class Builder { public void innerMethod() {} }
7+
public Builder foo() { return new Builder(); } // this line gives an error, that Builder is ambiguous
8+
9+
public Inner.Builder viaSelect() { return new Builder(); } // this line gives an error, that Builder is ambiguous
10+
}
11+
12+
}
13+

test/files/pos/t2377b/a.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object Test {
2+
(new Q.Inner).foo.innerMethod
3+
(new Q.Inner).viaSelect.innerMethod
4+
5+
}

0 commit comments

Comments
 (0)