@@ -267,39 +267,150 @@ public static NameLink lookupMemberVar(Element node, WurstType receiverType, Str
267267 scope = nextScope (scope );
268268 }
269269
270+ DefLinkMatch bestMatch = null ;
271+
270272 for (WScope s : scopes ) {
271273 Collection <DefLink > links = s .attrNameLinks ().get (name );
272274 if (links .isEmpty ()) continue ;
273275
274- for (DefLink n : links ) {
275- if (!(n instanceof VarLink )) {
276- continue ;
277- }
278- DefLink n2 = matchDefLinkReceiver (n , receiverType , node , showErrors );
279- if (n2 != null ) {
280- if (!showErrors ) {
281- GlobalCaches .CacheKey key = new GlobalCaches .CacheKey (node , name + "@" + receiverType , GlobalCaches .LookupType .MEMBER_VAR );
282- GlobalCaches .lookupCache .put (key , n2 );
276+ DefLinkMatch candidate = findBestMemberVarMatch (links , receiverType , node , showErrors );
277+ if (candidate != null ) {
278+ if (bestMatch == null || candidate .distance < bestMatch .distance ) {
279+ bestMatch = candidate ;
280+ if (bestMatch .distance == 0 ) {
281+ break ;
283282 }
284- return n2 ;
285283 }
286284 }
287285 }
288286
287+ if (bestMatch != null ) {
288+ if (!showErrors ) {
289+ GlobalCaches .CacheKey key = new GlobalCaches .CacheKey (node , name + "@" + receiverType , GlobalCaches .LookupType .MEMBER_VAR );
290+ GlobalCaches .lookupCache .put (key , bestMatch .link );
291+ }
292+ return bestMatch .link ;
293+ }
294+
289295 if (receiverType instanceof WurstTypeClassOrInterface ) {
290296 WurstTypeClassOrInterface ct = (WurstTypeClassOrInterface ) receiverType ;
291- for (DefLink n : ct .nameLinks ().get (name )) {
292- if (n instanceof VarLink || n instanceof TypeDefLink ) {
293- if (n .getVisibility ().isPublic ()) {
294- return n ;
295- }
297+ Collection <DefLink > typeNameLinks = ct .nameLinks ().get (name );
298+ DefLinkMatch candidate = findBestMemberVarMatch (typeNameLinks , receiverType , node , showErrors );
299+ if (candidate != null && candidate .link .getVisibility ().isPublic ()) {
300+ return candidate .link ;
301+ }
302+ for (DefLink n : typeNameLinks ) {
303+ if (n instanceof TypeDefLink && n .getVisibility ().isPublic ()) {
304+ return n ;
296305 }
297306 }
298307 }
299308
300309 return null ;
301310 }
302311
312+ private static @ Nullable DefLinkMatch findBestMemberVarMatch (Collection <DefLink > links , WurstType receiverType , Element node , boolean showErrors ) {
313+ DefLink bestLink = null ;
314+ int bestDistance = Integer .MAX_VALUE ;
315+
316+ for (DefLink n : links ) {
317+ if (!(n instanceof VarLink )) {
318+ continue ;
319+ }
320+ DefLink matched = matchDefLinkReceiver (n , receiverType , node , showErrors );
321+ if (matched == null ) {
322+ continue ;
323+ }
324+ int distance = receiverDistance (receiverType , matched .getReceiverType (), node );
325+ if (distance < bestDistance ) {
326+ bestLink = matched ;
327+ bestDistance = distance ;
328+ if (distance == 0 ) {
329+ break ;
330+ }
331+ }
332+ }
333+
334+ if (bestLink == null ) {
335+ return null ;
336+ }
337+ return new DefLinkMatch (bestLink , bestDistance );
338+ }
339+
340+ private static int receiverDistance (WurstType receiverType , @ Nullable WurstType candidateType , Element node ) {
341+ if (candidateType == null ) {
342+ return Integer .MAX_VALUE ;
343+ }
344+
345+ if (receiverType .equalsType (candidateType , node )) {
346+ return 0 ;
347+ }
348+
349+ ClassDef receiverClass = owningClass (receiverType );
350+ ClassDef candidateClass = owningClass (candidateType );
351+ if (receiverClass != null && candidateClass != null ) {
352+ int distance = inheritanceDistance (receiverClass , candidateClass );
353+ if (distance >= 0 ) {
354+ return distance ;
355+ }
356+ }
357+
358+ return Integer .MAX_VALUE / 2 ;
359+ }
360+
361+ private static int inheritanceDistance (ClassDef start , ClassDef target ) {
362+ int distance = 0 ;
363+ ClassDef current = start ;
364+ while (current != null ) {
365+ if (current == target ) {
366+ return distance ;
367+ }
368+ OptTypeExpr extended = current .getExtendedClass ();
369+ if (!(extended instanceof TypeExpr )) {
370+ break ;
371+ }
372+ WurstType extendedType = ((TypeExpr ) extended ).attrTyp ();
373+ if (!(extendedType instanceof WurstTypeClass )) {
374+ break ;
375+ }
376+ current = ((WurstTypeClass ) extendedType ).getClassDef ();
377+ distance ++;
378+ }
379+ return -1 ;
380+ }
381+
382+ private static @ Nullable ClassDef owningClass (WurstType type ) {
383+ if (type instanceof WurstTypeClass ) {
384+ return ((WurstTypeClass ) type ).getClassDef ();
385+ }
386+ if (type instanceof WurstTypeClassOrInterface ) {
387+ ClassOrInterface def = ((WurstTypeClassOrInterface ) type ).getDef ();
388+ if (def instanceof ClassDef ) {
389+ return (ClassDef ) def ;
390+ }
391+ return null ;
392+ }
393+ if (type instanceof WurstTypeModuleInstanciation ) {
394+ NamedScope inst = ((WurstTypeModuleInstanciation ) type ).getDef ();
395+ return inst .attrNearestClassDef ();
396+ }
397+ if (type instanceof WurstTypeModule ) {
398+ ModuleDef moduleDef = ((WurstTypeModule ) type ).getDef ();
399+ return moduleDef .attrNearestClassDef ();
400+ }
401+ return null ;
402+ }
403+
404+ private static final class DefLinkMatch {
405+ private final DefLink link ;
406+ private final int distance ;
407+
408+ private DefLinkMatch (DefLink link , int distance ) {
409+ this .link = link ;
410+ this .distance = distance ;
411+ }
412+ }
413+
303414 public static DefLink matchDefLinkReceiver (DefLink n , WurstType receiverType , Element node , boolean showErrors ) {
304415 WurstType n_receiverType = n .getReceiverType ();
305416 if (n_receiverType == null ) {
@@ -312,7 +423,7 @@ public static DefLink matchDefLinkReceiver(DefLink n, WurstType receiverType, El
312423 if (showErrors ) {
313424 if (n .getVisibility () == Visibility .PRIVATE_OTHER ) {
314425 node .addError (Utils .printElement (n .getDef ()) + " is private and cannot be used here." );
315- } else if (n .getVisibility () == Visibility .PROTECTED_OTHER ) {
426+ } else if (n .getVisibility () == Visibility .PROTECTED_OTHER && ! receiverType . isSubtypeOf ( n_receiverType , node ) ) {
316427 node .addError (Utils .printElement (n .getDef ()) + " is protected and cannot be used here." );
317428 }
318429 }
@@ -339,6 +450,7 @@ public static DefLink matchDefLinkReceiver(DefLink n, WurstType receiverType, El
339450 scope = nextScope (scope );
340451 }
341452
453+
342454 for (WScope s : scopes ) {
343455 ImmutableCollection <TypeLink > links = s .attrTypeNameLinks ().get (name );
344456 if (links .isEmpty ()) continue ;
0 commit comments