@@ -16,74 +16,32 @@ private[macwire] class EligibleValuesFinder[Q <: Quotes](log: Logger)(using val
1616 val wiredOwner = wiredDef.owner
1717
1818 def doFind (symbol : Symbol , scope : Scope ): EligibleValues = {
19- def hasModuleAnnotation (symbol : Symbol ): Boolean =
20- symbol.annotations.map(_.tpe.show).exists(_ == " com.softwaremill.macwire.Module" )
21-
22- def inspectModule (scope : Scope , tpe : TypeRepr , expr : Tree ): Option [EligibleValues ] = {
23- // it might be a @Module, let's see
24- val hasSymbol = expr.symbol != null // sometimes expr has no symbol...
25- val valIsModule = hasSymbol && hasModuleAnnotation(expr.symbol)
26- // the java @Inherited meta-annotation does not seem to be understood by scala-reflect...
27- val valParentIsModule = hasSymbol && ! valIsModule && tpe.baseClasses.exists(hasModuleAnnotation)
28-
29- if (valIsModule || valParentIsModule) {
30- log.withBlock(s " Inspecting module $tpe" ) {
31- val r = expr.symbol.declarations.map(_.tree).map {
32- case m : ValDef =>
33- EligibleValue (
34- m.tpt.tpe,
35- This (expr.symbol.owner).select(expr.symbol).select(m.symbol)
36- )
37- case m : DefDef if m.termParamss.flatMap(_.params).isEmpty =>
38- EligibleValue (m.rhs.map(_.tpe).getOrElse(m.returnTpt.tpe), Select .unique(Ref (expr.symbol), m.name))
39- }
40-
41- Some (EligibleValues (Map (scope.widen -> r)))
42- }
43- } else {
44- None
45- }
46- }
47-
48- def buildEligibleValue (scope : Scope ): PartialFunction [Tree , EligibleValues ] = {
49- case m : ValDef =>
50- merge(
51- inspectModule(scope.widen, m.rhs.map(_.tpe).getOrElse(m.tpt.tpe), m).getOrElse(EligibleValues .empty),
52- EligibleValues (Map (scope -> List (EligibleValue (m.rhs.map(_.tpe).getOrElse(m.tpt.tpe), m))))
53- )
54-
55- case m : DefDef if m.termParamss.flatMap(_.params).isEmpty =>
56- merge(
57- inspectModule(scope.widen, m.rhs.map(_.tpe).getOrElse(m.returnTpt.tpe), m).getOrElse(EligibleValues .empty),
58- EligibleValues (Map (scope -> List (EligibleValue (m.rhs.map(_.tpe).getOrElse(m.returnTpt.tpe), m))))
59- )
60-
61- }
62-
6319 def handleClassDef (scope : Scope , s : Symbol ): EligibleValues = {
64- (s.declaredMethods ::: s.declaredFields)
65- .filter(m => ! m.fullName.startsWith(" java.lang.Object" ) && ! m.fullName.startsWith(" scala.Any" ))
66- .map(_.tree)
67- .foldLeft(EligibleValues .empty)((values, tree) =>
68- merge(values, buildEligibleValue(scope).applyOrElse(tree, _ => EligibleValues .empty))
69- )
70-
20+ val declared = EligibleValues .build(
21+ symbol,
22+ scope,
23+ s.declarations.filter(nonSyntethic).map(_.tree)
24+ )
25+ val inherited = EligibleValues .build(
26+ symbol,
27+ scope.widen,
28+ (s.memberFields ::: s.memberMethods)
29+ .filter(m => nonSyntethic(m) && isPublic(m) && ! s.declarations.contains(m))
30+ .map(_.tree)
31+ )
32+ EligibleValues .merge(declared, inherited)
7133 }
7234
7335 def handleDefDef (scope : Scope , s : Symbol ): EligibleValues =
7436 s.tree match {
7537 case DefDef (_, _, _, Some (Match (_, cases))) =>
7638 report.throwError(s " Wire for deconstructed case is not supported yet " ) // TODO
7739 case DefDef (s, lpc, tt, ot) =>
78- lpc
79- .flatMap(_.params)
80- .foldLeft(EligibleValues .empty)((values, tree) =>
81- merge(values, buildEligibleValue(scope).applyOrElse(tree, _ => EligibleValues .empty))
82- )
40+ EligibleValues .build(symbol, scope, lpc.flatMap(_.params))
8341 }
8442
8543 if symbol.isNoSymbol then EligibleValues .empty
86- else if symbol.isDefDef then merge(handleDefDef(scope, symbol), doFind(symbol.maybeOwner, scope))
44+ else if symbol.isDefDef then EligibleValues . merge(handleDefDef(scope, symbol), doFind(symbol.maybeOwner, scope))
8745 else if symbol.isClassDef && ! symbol.isPackageDef then handleClassDef(scope.widen, symbol)
8846 else if symbol == defn.RootPackage then EligibleValues .empty
8947 else if symbol == defn.RootClass then EligibleValues .empty
@@ -93,17 +51,19 @@ private[macwire] class EligibleValuesFinder[Q <: Quotes](log: Logger)(using val
9351 doFind(Symbol .spliceOwner, Scope .init)
9452 }
9553
96- private def merge (
97- ev1 : EligibleValues ,
98- ev2 : EligibleValues
99- ): EligibleValues =
100- EligibleValues (merge(ev1.values, ev2.values))
54+ private def nonSyntethic (member : Symbol ): Boolean = {
55+ ! member.fullName.startsWith(" java.lang.Object" ) &&
56+ ! member.fullName.startsWith(" scala.Any" ) &&
57+ ! member.fullName.startsWith(" scala.AnyRef" ) &&
58+ ! member.fullName.endsWith(" <init>" ) &&
59+ ! member.fullName.endsWith(" $init$" ) &&
60+ ! member.fullName.contains(" $default$" ) && // default params for copy on case classes
61+ ! member.fullName.matches(" .*_\\ d+" ) // tuple methods on case classes
62+ }
10163
102- private def merge (
103- m1 : Map [Scope , List [EligibleValue ]],
104- m2 : Map [Scope , List [EligibleValue ]]
105- ): Map [Scope , List [EligibleValue ]] =
106- (m1.toSeq ++ m2.toSeq).groupBy(_._1).view.mapValues(_.flatMap(_._2).toList).toMap
64+ private def isPublic (member : Symbol ): Boolean = {
65+ ! ((member.flags is Flags .Private ) || (member.flags is Flags .Protected ))
66+ }
10767
10868 case class EligibleValue (tpe : TypeRepr , expr : Tree ) {
10969 // equal trees should have equal hash codes; if trees are equal structurally they should have the same toString?
@@ -165,8 +125,79 @@ private[macwire] class EligibleValuesFinder[Q <: Quotes](log: Logger)(using val
165125
166126 object EligibleValues {
167127 val empty : EligibleValues = new EligibleValues (Map .empty)
168- }
169128
129+ private def inspectModule (scope : Scope , tpe : TypeRepr , expr : Tree ) = {
130+ def hasModuleAnnotation (symbol : Symbol ): Boolean =
131+ symbol.annotations.map(_.tpe.show).exists(_ == " com.softwaremill.macwire.Module" )
132+
133+ // it might be a @Module, let's see
134+ val hasSymbol = expr.symbol != null // sometimes expr has no symbol...
135+ val valIsModule = hasSymbol && hasModuleAnnotation(expr.symbol)
136+ // the java @Inherited meta-annotation does not seem to be understood by scala-reflect...
137+ val valParentIsModule = hasSymbol && ! valIsModule && tpe.baseClasses.exists(hasModuleAnnotation)
138+
139+ if (valIsModule || valParentIsModule) {
140+ log.withBlock(s " Inspecting module $tpe" ) {
141+ val r = (expr.symbol.memberFields ::: expr.symbol.memberMethods)
142+ .filter(m => nonSyntethic(m) && isPublic(m))
143+ .map(_.tree)
144+ .collect {
145+ case m : ValDef =>
146+ EligibleValue (
147+ m.tpt.tpe,
148+ This (expr.symbol.owner).select(expr.symbol).select(m.symbol)
149+ )
150+ case m : DefDef if m.termParamss.flatMap(_.params).isEmpty =>
151+ EligibleValue (
152+ m.rhs.map(_.tpe).getOrElse(m.returnTpt.tpe),
153+ This (expr.symbol.owner).select(expr.symbol).select(m.symbol)
154+ )
155+ }
156+ EligibleValues (Map (scope.widen -> r))
157+ }
158+ } else {
159+ EligibleValues .empty
160+ }
161+ }
162+
163+ private def buildEligibleValue (symbol : Symbol , scope : Scope ): PartialFunction [Tree , EligibleValues ] = {
164+ case m : ValDef =>
165+ merge(
166+ inspectModule(scope.widen, m.rhs.map(_.tpe).getOrElse(m.tpt.tpe), m),
167+ EligibleValues (
168+ Map (
169+ scope -> List (
170+ EligibleValue (
171+ m.rhs.map(_.tpe).getOrElse(m.tpt.tpe),
172+ if symbol.isClassDef then This (symbol).select(m.symbol) else m
173+ )
174+ )
175+ )
176+ )
177+ )
178+ case m : DefDef if m.termParamss.flatMap(_.params).isEmpty =>
179+ merge(
180+ inspectModule(scope.widen, m.rhs.map(_.tpe).getOrElse(m.returnTpt.tpe), m),
181+ EligibleValues (
182+ Map (
183+ scope -> List (
184+ EligibleValue (
185+ m.rhs.map(_.tpe).getOrElse(m.returnTpt.tpe),
186+ if symbol.isClassDef then This (symbol).select(m.symbol) else m
187+ )
188+ )
189+ )
190+ )
191+ )
192+ }
193+
194+ def build (symbol : Symbol , scope : Scope , l : Iterable [Tree ]) = l.foldLeft(empty)((values, tree) =>
195+ merge(values, buildEligibleValue(symbol, scope).applyOrElse(tree, _ => EligibleValues .empty))
196+ )
197+
198+ def merge (ev1 : EligibleValues , ev2 : EligibleValues ): EligibleValues =
199+ EligibleValues ((ev1.values.toSeq ++ ev2.values.toSeq).groupBy(_._1).view.mapValues(_.flatMap(_._2).toList).toMap)
200+ }
170201}
171202
172203object EligibleValuesFinder {
0 commit comments