@@ -113,8 +113,10 @@ private static IAnalysisSet ResolveAndAdd(AnalysisUnit unit, IAnalysisSet set, A
113113 return set . Union ( value . Resolve ( unit ) ) ;
114114 }
115115
116- internal IEnumerable < AnalysisVariable > ToVariables ( IReferenceable referenceable ) {
117- if ( referenceable is VariableDef def ) {
116+ internal static IEnumerable < AnalysisVariable > ToVariables ( IReferenceable referenceable ) {
117+ var def = referenceable as VariableDef ;
118+
119+ if ( def != null ) {
118120 foreach ( var type in def . Types . WhereNotNull ( ) ) {
119121 var varType = VariableType . Value ;
120122 if ( type . DeclaringModule == null ) {
@@ -123,22 +125,22 @@ internal IEnumerable<AnalysisVariable> ToVariables(IReferenceable referenceable)
123125 }
124126
125127 foreach ( var loc in type . Locations . WhereNotNull ( ) ) {
126- yield return new AnalysisVariable ( varType , loc ) ;
128+ yield return new AnalysisVariable ( def , varType , loc ) ;
127129 }
128130 }
129131 }
130132
131133 foreach ( var reference in referenceable . Definitions ) {
132134 var loc = reference . GetLocationInfo ( ) ;
133135 if ( loc != null ) {
134- yield return new AnalysisVariable ( VariableType . Definition , loc ) ;
136+ yield return new AnalysisVariable ( def , VariableType . Definition , loc ) ;
135137 }
136138 }
137139
138140 foreach ( var reference in referenceable . References ) {
139141 var loc = reference . GetLocationInfo ( ) ;
140142 if ( loc != null ) {
141- yield return new AnalysisVariable ( VariableType . Reference , loc ) ;
143+ yield return new AnalysisVariable ( def , VariableType . Reference , loc ) ;
142144 }
143145 }
144146 }
@@ -208,10 +210,22 @@ public VariablesResult GetVariables(Expression expr, SourceLocation location, st
208210
209211 var unit = GetNearestEnclosingAnalysisUnit ( scope ) ;
210212 var tree = unit . Tree ;
211-
212213 var eval = new ExpressionEvaluator ( unit . CopyForEval ( ) , scope , mergeScopes : true ) ;
213- var variables = Enumerable . Empty < IAnalysisVariable > ( ) ;
214214
215+ var result = GetVariablesFromCallExpression ( unit , eval , scope , location , originalText ) ;
216+ if ( result != null ) {
217+ return result ;
218+ }
219+
220+ result = GetVariablesFromNameExpression ( expr , unit , scope ) ;
221+ if ( result != null ) {
222+ return result ;
223+ }
224+
225+ return GetVariablesFromMemberExpression ( expr , unit , eval ) ;
226+ }
227+
228+ private static VariablesResult GetVariablesFromCallExpression ( AnalysisUnit unit , ExpressionEvaluator eval , IScope scope , SourceLocation location , string originalText ) {
215229 var finder = new ExpressionFinder ( unit . Tree , new GetExpressionOptions { Calls = true } ) ;
216230 var callNode = finder . GetExpression ( location ) as CallExpression ;
217231 if ( callNode != null ) {
@@ -229,29 +243,95 @@ public VariablesResult GetVariables(Expression expr, SourceLocation location, st
229243 unit . Tree ) ;
230244 }
231245 }
246+ return null ;
247+ }
232248
233- if ( expr is NameExpression name ) {
234- if ( ! scope . EnumerateTowardsGlobal . Any ( ) ) {
235- variables = _unit . State . BuiltinModule . GetDefinitions ( name . Name ) . SelectMany ( ToVariables ) ;
236- } else {
237- foreach ( var defScope in scope . EnumerateTowardsGlobal
238- . Where ( s => s . ContainsVariable ( name . Name ) && ( s == scope || s . VisibleToChildren || IsFirstLineOfFunction ( scope , s , location ) ) ) ) {
239- var scopeVariables = GetVariablesInScope ( name , defScope ) . Distinct ( ) ;
240- variables = variables . Union ( scopeVariables ) ;
241- }
249+ private class VariableScopePair : Tuple < IAnalysisVariable , int > {
250+ public VariableScopePair ( IAnalysisVariable v , int scopeLevel ) : base ( v , scopeLevel ) { }
251+ public IAnalysisVariable Variable => Item1 ;
252+ public int ScopeLevel => Item2 ;
253+ public VariableType VariableType => Variable . Type ;
254+ public ILocationInfo Location => Variable . Location ;
255+ }
256+
257+ private VariablesResult GetVariablesFromNameExpression ( Expression expr , AnalysisUnit unit , IScope scope ) {
258+ if ( ! ( expr is NameExpression name ) ) {
259+ return null ;
260+ }
261+
262+ if ( ! scope . EnumerateTowardsGlobal . Any ( ) ) {
263+ var v = _unit . State . BuiltinModule . GetDefinitions ( name . Name ) . SelectMany ( ToVariables ) ;
264+ return new VariablesResult ( v , unit . Tree ) ;
265+ }
266+
267+ var variables = Enumerable . Empty < VariableScopePair > ( ) ;
268+ VariableScopePair mainDefinition = null ;
269+ var scopeLevel = 0 ;
270+ foreach ( var s in scope . EnumerateTowardsGlobal ) {
271+ var scopeVariables = GetVariablesInScope ( name , s )
272+ . Distinct ( )
273+ . Select ( v => new VariableScopePair ( v , scopeLevel ) )
274+ . ToArray ( ) ;
275+
276+ variables = variables . Union ( scopeVariables ) ;
277+
278+ // If we already have definition and it is a function argument, then stop
279+ // since function argument wins over definitions in the outer scope
280+ mainDefinition = scopeVariables . FirstOrDefault ( v => v . VariableType == VariableType . Definition && IsFunctionArgument ( s , v . Variable ) ) ;
281+ if ( mainDefinition != null ) {
282+ break ;
242283 }
243- } else if ( expr is MemberExpression member && ! string . IsNullOrEmpty ( member . Name ) ) {
244- var objects = eval . Evaluate ( member . Target ) ;
284+ scopeLevel ++ ;
285+ }
286+
287+ // Now take innermost definition and treat inner ones (such as reassignments) as references
288+ var definitions = variables
289+ . Where ( v => v . VariableType == VariableType . Definition )
290+ . OrderBy ( v => v . Location . Span . Start )
291+ . Reverse ( )
292+ . ToArray ( ) ;
293+
294+ mainDefinition = mainDefinition ?? definitions . FirstOrDefault ( ) ;
295+ if ( mainDefinition != null ) {
296+ // Drop definitions in outer scopes and convert those in inner scopes to references.
297+ // Scope levels are numbered in reverse (X == main definition level, x+1 == one up).
298+ var defsToRefs = definitions
299+ . Where ( d => d != mainDefinition && d . ScopeLevel <= mainDefinition . ScopeLevel )
300+ . Select ( v => new VariableScopePair ( new AnalysisVariable ( v . Variable . Variable , VariableType . Reference , v . Location ) , v . ScopeLevel ) ) ;
301+
302+ var others = variables
303+ . Where ( v => ( v . VariableType == VariableType . Reference || v . VariableType == VariableType . Value ) &&
304+ v . ScopeLevel <= mainDefinition . ScopeLevel ) ;
305+ variables = new [ ] { mainDefinition } . Concat ( others . Concat ( defsToRefs ) ) ;
306+ }
307+
308+ return new VariablesResult ( variables . Select ( v => v . Variable ) , unit . Tree ) ;
309+ }
245310
311+ private VariablesResult GetVariablesFromMemberExpression ( Expression expr , AnalysisUnit unit , ExpressionEvaluator eval ) {
312+ var variables = Enumerable . Empty < IAnalysisVariable > ( ) ;
313+ if ( expr is MemberExpression member && ! string . IsNullOrEmpty ( member . Name ) ) {
314+ var objects = eval . Evaluate ( member . Target ) ;
246315 foreach ( var v in objects . OfType < IReferenceableContainer > ( ) ) {
247316 variables = variables . Union ( v . GetDefinitions ( member . Name ) . SelectMany ( ToVariables ) ) ;
248317 }
249318 }
250-
251319 return new VariablesResult ( variables , unit . Tree ) ;
252320 }
253321
254- private IEnumerable < IAnalysisVariable > GetVariablesInScope ( NameExpression name , IScope scope ) {
322+ private bool IsFunctionArgument ( IScope scope , IAnalysisVariable v ) {
323+ if ( v . Variable ? . Types ? . MaybeEnumerate ( ) . FirstOrDefault ( ) is ParameterInfo ) {
324+ return true ;
325+ }
326+ if ( scope is FunctionScope funcScope ) {
327+ var funcDef = funcScope . Function . FunctionDefinition ;
328+ var varSpan = v . Location . Span . ToLinearSpan ( _unit . Tree ) ;
329+ return funcDef . NameExpression . EndIndex <= varSpan . Start && varSpan . End <= funcDef . HeaderIndex ;
330+ }
331+ return false ;
332+ }
333+
334+ private static IEnumerable < IAnalysisVariable > GetVariablesInScope ( NameExpression name , IScope scope ) {
255335 var result = new List < IAnalysisVariable > ( ) ;
256336
257337 result . AddRange ( scope . GetMergedVariables ( name . Name ) . SelectMany ( ToVariables ) ) ;
@@ -289,19 +369,6 @@ private IEnumerable<IAnalysisVariable> GetVariablesInScope(NameExpression name,
289369 return result ;
290370 }
291371
292- private static bool IsFirstLineOfFunction ( IScope innerScope , IScope outerScope , SourceLocation location ) {
293- if ( innerScope . OuterScope == outerScope && innerScope is FunctionScope ) {
294- var funcScope = ( FunctionScope ) innerScope ;
295- var def = funcScope . Function . FunctionDefinition ;
296-
297- // TODO: Use indexes rather than lines to check location
298- if ( location . Line == def . GetStart ( def . GlobalParent ) . Line ) {
299- return true ;
300- }
301- }
302- return false ;
303- }
304-
305372 private class ErrorWalker : PythonWalker {
306373 public bool HasError { get ; private set ; }
307374
0 commit comments