@@ -178,7 +178,9 @@ def visit_Call(self, node: cst.Call) -> Optional[bool]:
178178 BOOL_NAMES = {"True" : True , "False" : False , "None" : None }
179179 PRETEND_ARGV = ["setup.py" , "bdist_wheel" ]
180180
181- def evaluate_in_scope (self , item : cst .CSTNode , scope : Any ) -> Any :
181+ def evaluate_in_scope (
182+ self , item : cst .CSTNode , scope : Any , target_name : str = ""
183+ ) -> Any :
182184 qnames = self .get_metadata (QualifiedNameProvider , item )
183185
184186 if isinstance (item , cst .SimpleString ):
@@ -224,13 +226,23 @@ def evaluate_in_scope(self, item: cst.CSTNode, scope: Any) -> Any:
224226 # module scope isn't in the dict
225227 return "??"
226228
227- return self .evaluate_in_scope (gp .value , scope )
229+ # we have recursed, likey due to `x = x + y` assignment or similar
230+ # self-referential evaluation
231+ if target_name and target_name == name :
232+ return "??"
233+
234+ # keep trying assignments until we get something other than ??
235+ result = self .evaluate_in_scope (gp .value , scope , name )
236+ if result and result != "??" :
237+ return result
238+ # give up
239+ return "??"
228240 elif isinstance (item , (cst .Tuple , cst .List )):
229241 lst = []
230242 for el in item .elements :
231243 lst .append (
232244 self .evaluate_in_scope (
233- el .value , self .get_metadata (ScopeProvider , el )
245+ el .value , self .get_metadata (ScopeProvider , el ), target_name
234246 )
235247 )
236248 if isinstance (item , cst .Tuple ):
@@ -248,10 +260,10 @@ def evaluate_in_scope(self, item: cst.CSTNode, scope: Any) -> Any:
248260 for arg in item .args :
249261 if isinstance (arg .keyword , cst .Name ):
250262 args [names .index (arg .keyword .value )] = self .evaluate_in_scope (
251- arg .value , scope
263+ arg .value , scope , target_name
252264 )
253265 else :
254- args [i ] = self .evaluate_in_scope (arg .value , scope )
266+ args [i ] = self .evaluate_in_scope (arg .value , scope , target_name )
255267 i += 1
256268
257269 # TODO clear ones that are still default
@@ -264,26 +276,30 @@ def evaluate_in_scope(self, item: cst.CSTNode, scope: Any) -> Any:
264276 d = {}
265277 for arg in item .args :
266278 if isinstance (arg .keyword , cst .Name ):
267- d [arg .keyword .value ] = self .evaluate_in_scope (arg .value , scope )
279+ d [arg .keyword .value ] = self .evaluate_in_scope (
280+ arg .value , scope , target_name
281+ )
268282 # TODO something with **kwargs
269283 return d
270284 elif isinstance (item , cst .Dict ):
271285 d = {}
272286 for el2 in item .elements :
273287 if isinstance (el2 , cst .DictElement ):
274288 d [self .evaluate_in_scope (el2 .key , scope )] = self .evaluate_in_scope (
275- el2 .value , scope
289+ el2 .value , scope , target_name
276290 )
277291 return d
278292 elif isinstance (item , cst .Subscript ):
279- lhs = self .evaluate_in_scope (item .value , scope )
293+ lhs = self .evaluate_in_scope (item .value , scope , target_name )
280294 if isinstance (lhs , str ):
281295 # A "??" entry, propagate
282296 return "??"
283297
284298 # TODO: Figure out why this is Sequence
285299 if isinstance (item .slice [0 ].slice , cst .Index ):
286- rhs = self .evaluate_in_scope (item .slice [0 ].slice .value , scope )
300+ rhs = self .evaluate_in_scope (
301+ item .slice [0 ].slice .value , scope , target_name
302+ )
287303 try :
288304 if isinstance (lhs , dict ):
289305 return lhs .get (rhs , "??" )
@@ -296,8 +312,10 @@ def evaluate_in_scope(self, item: cst.CSTNode, scope: Any) -> Any:
296312 # LOG.warning(f"Omit2 {type(item.slice[0].slice)!r}")
297313 return "??"
298314 elif isinstance (item , cst .BinaryOperation ):
299- lhs = self .evaluate_in_scope (item .left , scope )
300- rhs = self .evaluate_in_scope (item .right , scope )
315+ lhs = self .evaluate_in_scope (item .left , scope , target_name )
316+ rhs = self .evaluate_in_scope (item .right , scope , target_name )
317+ if lhs == "??" or rhs == "??" :
318+ return "??"
301319 if isinstance (item .operator , cst .Add ):
302320 try :
303321 return lhs + rhs
0 commit comments