@@ -16,74 +16,32 @@ private[macwire] class EligibleValuesFinder[Q <: Quotes](log: Logger)(using val
16
16
val wiredOwner = wiredDef.owner
17
17
18
18
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
-
63
19
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)
71
33
}
72
34
73
35
def handleDefDef (scope : Scope , s : Symbol ): EligibleValues =
74
36
s.tree match {
75
37
case DefDef (_, _, _, Some (Match (_, cases))) =>
76
38
report.throwError(s " Wire for deconstructed case is not supported yet " ) // TODO
77
39
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))
83
41
}
84
42
85
43
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))
87
45
else if symbol.isClassDef && ! symbol.isPackageDef then handleClassDef(scope.widen, symbol)
88
46
else if symbol == defn.RootPackage then EligibleValues .empty
89
47
else if symbol == defn.RootClass then EligibleValues .empty
@@ -93,17 +51,19 @@ private[macwire] class EligibleValuesFinder[Q <: Quotes](log: Logger)(using val
93
51
doFind(Symbol .spliceOwner, Scope .init)
94
52
}
95
53
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
+ }
101
63
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
+ }
107
67
108
68
case class EligibleValue (tpe : TypeRepr , expr : Tree ) {
109
69
// 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
165
125
166
126
object EligibleValues {
167
127
val empty : EligibleValues = new EligibleValues (Map .empty)
168
- }
169
128
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
+ }
170
201
}
171
202
172
203
object EligibleValuesFinder {
0 commit comments