From 4e7c23af5238c572c5fd5bba8edbbffc82690702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Zyba=C5=82a?= Date: Wed, 25 Aug 2021 15:24:09 +0200 Subject: [PATCH] Expose methods used by Scaladoc in Quotes API --- .../quoted/runtime/impl/QuotesImpl.scala | 20 +++ library/src/scala/quoted/Quotes.scala | 27 ++++ .../tools/scaladoc/tasty/BasicSupport.scala | 13 ++ .../scaladoc/tasty/ClassLikeSupport.scala | 20 +-- .../tools/scaladoc/tasty/InkuireSupport.scala | 1 - .../dotty/tools/scaladoc/tasty/SymOps.scala | 15 +++ .../scaladoc/tasty/SyntheticSupport.scala | 121 ------------------ .../tools/scaladoc/tasty/TypesSupport.scala | 1 - 8 files changed, 86 insertions(+), 132 deletions(-) delete mode 100644 scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 18bb3a40cf12..c951659d1bff 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -246,6 +246,13 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler optional(self.rhs.asInstanceOf[tpd.Template].self) def body: List[Statement] = self.rhs.asInstanceOf[tpd.Template].body + def tpe: TypeRepr = + self.symbol.typeRef.appliedTo(self.symbol.typeParams.map(_.typeRef)) + def supertypes: List[TypeRepr] = self.symbol match + case cls: dotc.core.Symbols.ClassSymbol => + val ref = cls.classDenot.classInfo.appliedRef + ref.baseClasses.map(ref.baseType(_)) + case _ => List() end extension end ClassDefMethods @@ -1694,11 +1701,17 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler val tpNoRefinement = self.dropDependentRefinement tpNoRefinement != self && dotc.core.Symbols.defn.isNonRefinedFunction(tpNoRefinement) + def isTupleType: Boolean = + dotc.core.Symbols.defn.isTupleType(self) + def isCompiletimeAppliedType: Boolean = + dotc.core.Symbols.defn.isCompiletimeAppliedType(self.typeSymbol) def select(sym: Symbol): TypeRepr = self.select(sym) def appliedTo(targ: TypeRepr): TypeRepr = dotc.core.Types.decorateTypeApplications(self).appliedTo(targ) def appliedTo(targs: List[TypeRepr]): TypeRepr = dotc.core.Types.decorateTypeApplications(self).appliedTo(targs) + def memberInfo(sym: Symbol): TypeRepr = + self.memberInfo(sym) end extension end TypeReprMethods @@ -2465,6 +2478,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def isAnonymousFunction: Boolean = self.denot.isAnonymousFunction def isAbstractType: Boolean = self.denot.isAbstractType def isClassConstructor: Boolean = self.denot.isClassConstructor + def isSuperAccessor = self.name.is(dotc.core.NameKinds.SuperAccessorName) def isType: Boolean = self.isType def isTerm: Boolean = self.isTerm def isPackageDef: Boolean = self.is(dotc.core.Flags.Package) @@ -2544,6 +2558,11 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def typeMembers: List[Symbol] = self.unforcedDecls.filter(_.isType) + def allTypeMembers: List[Symbol] = + lookupPrefix.allMembers.iterator.map(_.symbol).collect { + case sym if sym.isType => sym.asType + }.toList + def declarations: List[Symbol] = self.typeRef.info.decls.toList @@ -2733,6 +2752,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def sourceCode: Option[String] = // TODO detect when we do not have a source and return None Some(new String(self.source.content(), self.start, self.end - self.start)) + def exists: Boolean = self.exists end extension end PositionMethods diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index aff686aeb68b..2c001b54c655 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -490,6 +490,10 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * @syntax markdown */ def body: List[Statement] + /** Type of the class */ + def tpe: TypeRepr + /** Supertypes of the class */ + def supertypes: List[TypeRepr] end extension end ClassDefMethods @@ -2473,6 +2477,17 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => */ def isDependentFunctionType: Boolean + /** Is this type a tuple type? + * + * @return true if the dealiased type of `self` without refinement is `TupleN[T1, T2, ..., Tn]` + */ + def isTupleType: Boolean + + /** Is this type a compile-time applied type? + * + */ + def isCompiletimeAppliedType: Boolean + /** The type , reduced if possible */ def select(sym: Symbol): TypeRepr @@ -2482,6 +2497,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** The current type applied to given type arguments: `this[targ0, ..., targN]` */ def appliedTo(targs: List[TypeRepr]): TypeRepr + /** Member info of `sym` as seen from the TypeRepr */ + def memberInfo(sym: Symbol): TypeRepr + end extension } @@ -3559,6 +3577,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Is this the constructor of a class? */ def isClassConstructor: Boolean + /* Is this the super accessor? */ + def isSuperAccessor: Boolean + /** Is this the definition of a type? */ def isType: Boolean @@ -3649,6 +3670,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Type member directly declared in the class */ def typeMembers: List[Symbol] + /** All type members declared or inherited */ + def allTypeMembers: List[Symbol] + /** All members directly declared in the class */ def declarations: List[Symbol] @@ -4126,6 +4150,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Source code within the position */ def sourceCode: Option[String] + /** Does the position exist? */ + def exists: Boolean + end extension } diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala index bbfe50f421ec..c27f25038aae 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala @@ -36,6 +36,19 @@ trait BasicSupport: Annotation(dri, params) + def isValidPos(using Quotes)(pos: reflect.Position) = + if pos.exists then pos.start != pos.end else false + + /* Heuristics solving a problem if the constructor of class in source code contains param clauses or not. + It seems that this information is lost during source code processing */ + def constructorWithoutParamLists(using Quotes)(c: reflect.ClassDef): Boolean = + !isValidPos(c.constructor.pos) || { + val end = c.constructor.pos.end + val typesEnd = c.constructor.leadingTypeParams.lastOption.fold(end - 1)(_.pos.end) + val classDefTree = c.constructor.show + c.constructor.leadingTypeParams.nonEmpty && end <= typesEnd + 1 + } + extension (using Quotes)(sym: reflect.Symbol) def documentation = sym.docstring.map(parseComment(_, sym.tree)) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index bbf737464eff..10c42eb1a0a7 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -9,7 +9,6 @@ import scala.quoted._ import SymOps._ import NameNormalizer._ -import SyntheticsSupport._ import dotty.tools.dotc.core.NameKinds trait ClassLikeSupport: @@ -79,7 +78,10 @@ trait ClassLikeSupport: Seq(link -> superLink) ++ getSupertypesGraph(tree, superLink) } - val supertypes = getSupertypes(using qctx)(classDef).map { + val supertypes = classDef.supertypes + .tail + .map(t => t.typeSymbol -> t) + .map { case (symbol, tpe) => LinkToType(tpe.asSignature, symbol.dri, bareClasslikeKind(symbol)) } @@ -88,7 +90,7 @@ trait ClassLikeSupport: val tpe = valdef.tpt.tpe LinkToType(tpe.asSignature, symbol.dri, Kind.Type(false, false, Seq.empty)) } - val selfSignature: DSignature = typeForClass(classDef).asSignature + val selfSignature: DSignature = classDef.tpe.asSignature val graph = HierarchyGraph.withEdges( getSupertypesGraph(classDef, LinkToType(selfSignature, classDef.symbol.dri, bareClasslikeKind(classDef.symbol))) @@ -303,13 +305,13 @@ trait ClassLikeSupport: case td: TypeDef if !td.symbol.flags.is(Flags.Synthetic) && (!td.symbol.flags.is(Flags.Case) || !td.symbol.flags.is(Flags.Enum)) => Some(parseTypeDef(td)) - case vd: ValDef if !isSyntheticField(vd.symbol) + case vd: ValDef if !vd.symbol.isSyntheticField && (!vd.symbol.flags.is(Flags.Case) || !vd.symbol.flags.is(Flags.Enum)) && vd.symbol.isGiven => val classDef = Some(vd.tpt.tpe).flatMap(_.classSymbol.map(_.tree.asInstanceOf[ClassDef])) Some(classDef.filter(_.symbol.flags.is(Flags.Module)).fold[Member](parseValDef(c, vd))(parseGivenClasslike(_))) - case vd: ValDef if !isSyntheticField(vd.symbol) && (!vd.symbol.flags.is(Flags.Case) || !vd.symbol.flags.is(Flags.Enum)) => + case vd: ValDef if !vd.symbol.isSyntheticField && (!vd.symbol.flags.is(Flags.Case) || !vd.symbol.flags.is(Flags.Enum)) => Some(parseValDef(c, vd)) case c: ClassDef if c.symbol.owner.methodMember(c.name).exists(_.flags.is(Flags.Given)) => @@ -368,14 +370,14 @@ trait ClassLikeSupport: def membersToDocument = c.body.filterNot(_.symbol.isHiddenByVisibility) def getNonTrivialInheritedMemberTrees = - c.symbol.getmembers.filterNot(s => s.isHiddenByVisibility || s.maybeOwner == c.symbol) + c.symbol.getAllMembers.filterNot(s => s.isHiddenByVisibility || s.maybeOwner == c.symbol) .filter(s => s.maybeOwner != defn.ObjectClass && s.maybeOwner != defn.AnyClass) .map(_.tree) extension (c: ClassDef) def extractMembers: Seq[Member] = { val inherited = c.getNonTrivialInheritedMemberTrees.collect { - case dd: DefDef if !dd.symbol.isClassConstructor && !(dd.symbol.isSuperBridgeMethod || dd.symbol.isDefaultHelperMethod) => dd + case dd: DefDef if !dd.symbol.isClassConstructor && !dd.symbol.isSyntheticFunc => dd case other => other } c.membersToDocument.flatMap(parseMember(c)) ++ @@ -458,7 +460,7 @@ trait ClassLikeSupport: val companion = classDef.symbol.getCompanionSymbol.map(_.tree.asInstanceOf[ClassDef]).get val enumVals = companion.membersToDocument.collect { - case vd: ValDef if !isSyntheticField(vd.symbol) && vd.symbol.flags.is(Flags.Enum) && vd.symbol.flags.is(Flags.Case) => vd + case vd: ValDef if !vd.symbol.isSyntheticField && vd.symbol.flags.is(Flags.Enum) && vd.symbol.flags.is(Flags.Case) => vd }.toList.map(parseValDef(classDef, _)) val enumTypes = companion.membersToDocument.collect { @@ -631,7 +633,7 @@ trait ClassLikeSupport: def unwrapMemberInfo(c: ClassDef, symbol: Symbol): MemberInfo = - val baseTypeRepr = memberInfo(c, symbol) + val baseTypeRepr = c.tpe.memberInfo(symbol) def isSyntheticEvidence(name: String) = if !name.startsWith(NameKinds.EvidenceParamName.separator) then false else diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala index 2b34ba633b0e..ace6df9a1daf 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala @@ -10,7 +10,6 @@ import scala.quoted._ import SymOps._ import NameNormalizer._ -import SyntheticsSupport._ trait InkuireSupport: self: TastyParser => diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala index defbdd5bbee7..59cc57c2b402 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala @@ -134,8 +134,16 @@ object SymOps: import reflect._ sym.flags.is(Flags.Artifact) + def isOpaque: Boolean = + import reflect._ + sym.flags.is(Flags.Opaque) + def isLeftAssoc: Boolean = !sym.name.endsWith(":") + def isSyntheticField = + import reflect._ + sym.flags.is(Flags.CaseAccessor) || (sym.flags.is(Flags.Module) && !sym.flags.is(Flags.Given)) + def extendedSymbol: Option[reflect.ValDef] = import reflect.* Option.when(sym.isExtensionMethod){ @@ -191,6 +199,13 @@ object SymOps: case TypeParamClause(params) => params }.toList.flatten + def getAllMembers: List[reflect.Symbol] = sym.methodMembers ++ sym.allTypeMembers ++ sym.fieldMembers + + def isSyntheticFunc: Boolean = + import reflect._ + /* Most of flag getters have Synthetic flag set but there are some exceptions which need to be filtered by name */ + sym.flags.is(Flags.Synthetic) || sym.flags.is(Flags.FieldAccessor) || sym.isSuperAccessor || sym.name.contains("default$") + end extension end SymOps diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala deleted file mode 100644 index 689c66a200ee..000000000000 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala +++ /dev/null @@ -1,121 +0,0 @@ -package dotty.tools.scaladoc -package tasty - -import scala.quoted._ - -object SyntheticsSupport: - - extension (using Quotes)(t: reflect.TypeRepr) - def isTupleType: Boolean = t.hackIsTupleType(t) - - def isCompiletimeAppliedType: Boolean = t.hackIsCompiletimeAppliedType(t) - - private def hackIsTupleType(rtpe: reflect.TypeRepr): Boolean = - import dotty.tools.dotc - given ctx: dotc.core.Contexts.Context = quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx - val tpe = rtpe.asInstanceOf[dotc.core.Types.Type] - ctx.definitions.isTupleType(tpe) - - private def hackIsCompiletimeAppliedType(rtpe: reflect.TypeRepr): Boolean = - import dotty.tools.dotc - given ctx: dotc.core.Contexts.Context = quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx - val tpe = rtpe.asInstanceOf[dotc.core.Types.Type] - ctx.definitions.isCompiletimeAppliedType(tpe.typeSymbol) - end extension - - extension (using Quotes)(s: reflect.Symbol) - def isSyntheticFunc: Boolean = - import reflect._ - s.flags.is(Flags.Synthetic) || s.flags.is(Flags.FieldAccessor) || s.isDefaultHelperMethod - - def isSuperBridgeMethod: Boolean = s.name.contains("$super$") - - def isDefaultHelperMethod: Boolean = ".*\\$default\\$\\d+$".r.matches(s.name) - - def isOpaque: Boolean = - import reflect._ - s.flags.is(Flags.Opaque) - - def isInfix: Boolean = hackIsInfix(s) - - def getmembers: List[reflect.Symbol] = hackGetmembers(s) - - end extension - - def isValidPos(using Quotes)(pos: reflect.Position) = - if hackExists(pos) then pos.start != pos.end else false - - def isSyntheticField(using Quotes)(c: reflect.Symbol) = - import reflect._ - c.flags.is(Flags.CaseAccessor) || (c.flags.is(Flags.Module) && !c.flags.is(Flags.Given)) - - def constructorWithoutParamLists(using Quotes)(c: reflect.ClassDef): Boolean = - !isValidPos(c.constructor.pos) || { - val end = c.constructor.pos.end - val typesEnd = c.constructor.leadingTypeParams.lastOption.fold(end - 1)(_.pos.end) - val classDefTree = c.constructor.show - c.constructor.leadingTypeParams.nonEmpty && end <= typesEnd + 1 - } - - // TODO: #49 Remove it after TASTY-Reflect release with published flag Extension - private def hackIsInfix(using Quotes)(rsym: reflect.Symbol): Boolean = { - import reflect._ - import dotty.tools.dotc - given ctx: dotc.core.Contexts.Context = quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx - val sym = rsym.asInstanceOf[dotc.core.Symbols.Symbol] - ctx.definitions.isInfix(sym) - } - /* We need there to filter out symbols with certain flagsets, because these symbols come from compiler and TASTY can't handle them well. - They are valdefs that describe case companion objects and cases from enum. - TASTY crashed when calling _.tree on them. - */ - private def hackGetmembers(using Quotes)(rsym: reflect.Symbol): List[reflect.Symbol] = { - import reflect._ - import dotty.tools.dotc - given ctx: dotc.core.Contexts.Context = quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx - val sym = rsym.asInstanceOf[dotc.core.Symbols.Symbol] - sym.namedType.allMembers.iterator.map(_.symbol) - .collect { - case sym if - (!sym.is(dotc.core.Flags.ModuleVal) || sym.is(dotc.core.Flags.Given)) && - !sym.flags.isAllOf(dotc.core.Flags.Enum | dotc.core.Flags.Case | dotc.core.Flags.JavaStatic) => - sym.asInstanceOf[Symbol] - }.toList - } - - private def hackGetSupertypes(using Quotes)(rdef: reflect.ClassDef) = { - import reflect._ - import dotty.tools.dotc - given dotc.core.Contexts.Context = quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx - val classdef = rdef.asInstanceOf[dotc.ast.tpd.TypeDef] - val ref = classdef.symbol.info.asInstanceOf[dotc.core.Types.ClassInfo].appliedRef - val baseTypes: List[(dotc.core.Symbols.Symbol, dotc.core.Types.Type)] = - ref.baseClasses.map(b => b -> ref.baseType(b)) - baseTypes.asInstanceOf[List[(Symbol, TypeRepr)]] - } - - private def hackExists(using Quotes)(rpos: reflect.Position) = { - import reflect._ - import dotty.tools.dotc - import dotty.tools.dotc.util.Spans._ - given dotc.core.Contexts.Context = quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx - val pos = rpos.asInstanceOf[dotc.util.SourcePosition] - pos.exists - } - - def getSupertypes(using Quotes)(c: reflect.ClassDef) = hackGetSupertypes(c).tail - - def typeForClass(using Quotes)(c: reflect.ClassDef): reflect.TypeRepr = - import reflect._ - import dotty.tools.dotc - given dotc.core.Contexts.Context = quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx - val cSym = c.symbol.asInstanceOf[dotc.core.Symbols.Symbol] - cSym.typeRef.appliedTo(cSym.typeParams.map(_.typeRef)).asInstanceOf[TypeRepr] - - def memberInfo(using Quotes)(c: reflect.ClassDef, symbol: reflect.Symbol): reflect.TypeRepr = - import reflect._ - import dotty.tools.dotc - given dotc.core.Contexts.Context = quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx - typeForClass(c).asInstanceOf[dotc.core.Types.Type] - .memberInfo(symbol.asInstanceOf[dotc.core.Symbols.Symbol]) - .asInstanceOf[TypeRepr] diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala index ed79da0e83b8..2786a15bc576 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala @@ -6,7 +6,6 @@ import collection.JavaConverters._ import scala.quoted._ import NameNormalizer._ -import SyntheticsSupport._ trait TypesSupport: self: TastyParser =>