JBTable: use a singleton list
[idea/community.git] / python / src / com / jetbrains / python / psi / impl / references / PyReferenceImpl.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.jetbrains.python.psi.impl.references;
17
18 import com.google.common.collect.Lists;
19 import com.intellij.codeInsight.completion.CompletionUtil;
20 import com.intellij.codeInsight.lookup.LookupElement;
21 import com.intellij.codeInsight.lookup.LookupElementBuilder;
22 import com.intellij.lang.ASTNode;
23 import com.intellij.lang.annotation.HighlightSeverity;
24 import com.intellij.openapi.extensions.Extensions;
25 import com.intellij.openapi.util.Comparing;
26 import com.intellij.openapi.util.Pair;
27 import com.intellij.openapi.util.TextRange;
28 import com.intellij.psi.*;
29 import com.intellij.psi.impl.source.resolve.ResolveCache;
30 import com.intellij.psi.util.PsiTreeUtil;
31 import com.intellij.util.IncorrectOperationException;
32 import com.intellij.util.PlatformIcons;
33 import com.intellij.util.ProcessingContext;
34 import com.jetbrains.python.PyNames;
35 import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
36 import com.jetbrains.python.codeInsight.controlflow.ReadWriteInstruction;
37 import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
38 import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
39 import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
40 import com.jetbrains.python.psi.*;
41 import com.jetbrains.python.psi.impl.*;
42 import com.jetbrains.python.psi.resolve.*;
43 import com.jetbrains.python.psi.types.PyModuleType;
44 import com.jetbrains.python.psi.types.PyType;
45 import com.jetbrains.python.psi.types.TypeEvalContext;
46 import com.jetbrains.python.refactoring.PyDefUseUtil;
47 import org.jetbrains.annotations.NotNull;
48 import org.jetbrains.annotations.Nullable;
49
50 import java.util.Collections;
51 import java.util.HashSet;
52 import java.util.List;
53 import java.util.ListIterator;
54 import java.util.concurrent.atomic.AtomicInteger;
55
56 /**
57  * @author yole
58  */
59 public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference {
60   protected final PyQualifiedExpression myElement;
61   protected final PyResolveContext myContext;
62
63   public PyReferenceImpl(PyQualifiedExpression element, @NotNull PyResolveContext context) {
64     myElement = element;
65     myContext = context;
66   }
67
68   @Override
69   public TextRange getRangeInElement() {
70     final ASTNode nameElement = myElement.getNameElement();
71     final TextRange range = nameElement != null ? nameElement.getTextRange() : myElement.getNode().getTextRange();
72     return range.shiftRight(-myElement.getNode().getStartOffset());
73   }
74
75   @Override
76   public PsiElement getElement() {
77     return myElement;
78   }
79
80   /**
81    * Resolves reference to the most obvious point.
82    * Imported module names: to module file (or directory for a qualifier).
83    * Other identifiers: to most recent definition before this reference.
84    * This implementation is cached.
85    *
86    * @see #resolveInner().
87    */
88   @Override
89   @Nullable
90   public PsiElement resolve() {
91     final ResolveResult[] results = multiResolve(false);
92     return results.length >= 1 && !(results[0] instanceof ImplicitResolveResult) ? results[0].getElement() : null;
93   }
94
95   // it is *not* final so that it can be changed in debug time. if set to false, caching is off
96   @SuppressWarnings("FieldCanBeLocal")
97   private static boolean USE_CACHE = true;
98
99   /**
100    * Resolves reference to possible referred elements.
101    * First element is always what resolve() would return.
102    * Imported module names: to module file, or {directory, '__init__.py}' for a qualifier.
103    * todo Local identifiers: a list of definitions in the most recent compound statement
104    * (e.g. <code>if X: a = 1; else: a = 2</code> has two definitions of <code>a</code>.).
105    * todo Identifiers not found locally: similar definitions in imported files and builtins.
106    *
107    * @see com.intellij.psi.PsiPolyVariantReference#multiResolve(boolean)
108    */
109   @Override
110   @NotNull
111   public ResolveResult[] multiResolve(final boolean incompleteCode) {
112     if (USE_CACHE) {
113       final ResolveCache cache = ResolveCache.getInstance(getElement().getProject());
114       return cache.resolveWithCaching(this, CachingResolver.INSTANCE, false, incompleteCode);
115     }
116     else {
117       return multiResolveInner();
118     }
119   }
120
121   // sorts and modifies results of resolveInner
122
123   @NotNull
124   private ResolveResult[] multiResolveInner() {
125     final String referencedName = myElement.getReferencedName();
126     if (referencedName == null) return ResolveResult.EMPTY_ARRAY;
127
128     List<RatedResolveResult> targets = resolveInner();
129     if (targets.size() == 0) return ResolveResult.EMPTY_ARRAY;
130
131     // change class results to constructor results if there are any
132     if (myElement.getParent() instanceof PyCallExpression) { // we're a call
133       ListIterator<RatedResolveResult> it = targets.listIterator();
134       while (it.hasNext()) {
135         final RatedResolveResult rrr = it.next();
136         final PsiElement elt = rrr.getElement();
137         if (elt instanceof PyClass) {
138           PyClass cls = (PyClass)elt;
139           PyFunction init = cls.findMethodByName(PyNames.INIT, false);
140           if (init != null) {
141             // replace
142             it.set(rrr.replace(init));
143           }
144           else { // init not found; maybe it's ancestor's
145             for (PyClass ancestor : cls.getAncestorClasses(myContext.getTypeEvalContext())) {
146               init = ancestor.findMethodByName(PyNames.INIT, false);
147               if (init != null) {
148                 // add to results as low priority
149                 it.add(new RatedResolveResult(RatedResolveResult.RATE_LOW, init));
150                 break;
151               }
152             }
153           }
154         }
155       }
156     }
157
158     // put everything in a sorting container
159     List<RatedResolveResult> ret = RatedResolveResult.sorted(targets);
160     return ret.toArray(new ResolveResult[ret.size()]);
161   }
162
163   @NotNull
164   private static ResolveResultList resolveToLatestDefs(@NotNull List<ReadWriteInstruction> instructions, @NotNull PsiElement element, @NotNull String name) {
165     final ResolveResultList ret = new ResolveResultList();
166     for (ReadWriteInstruction instruction : instructions) {
167       PsiElement definition = instruction.getElement();
168       NameDefiner definer = null;
169       // TODO: This check may slow down resolving, but it is the current solution to the comprehension scopes problem
170       if (isInnerComprehension(element, definition)) continue;
171       if (definition instanceof NameDefiner && !(definition instanceof PsiNamedElement)) {
172         definer = (NameDefiner)definition;
173         definition = definer.getElementNamed(name);
174       }
175       if (definer != null) {
176         if (definer instanceof PyImportElement || definer instanceof PyStarImportElement || definer instanceof PyImportedModule) {
177           ret.add(new ImportedResolveResult(definition, getRate(definition), Collections.<PsiElement>singletonList(definer)));
178         }
179         else {
180           ret.poke(definition, getRate(definition));
181         }
182         // TODO this kind of resolve contract is quite stupid
183         if (definition != null) {
184           ret.poke(definer, RatedResolveResult.RATE_LOW);
185         }
186       }
187       else {
188         ret.poke(definition, getRate(definition));
189       }
190     }
191     final ResolveResultList results = new ResolveResultList();
192     for (RatedResolveResult r : ret) {
193       final PsiElement e = r.getElement();
194       if (e == element) {
195         continue;
196       }
197       if (element instanceof PyTargetExpression && PyPsiUtils.isBefore(element, e)) {
198         continue;
199       }
200       else {
201         results.add(r);
202       }
203     }
204
205     return results;
206   }
207
208   private static boolean isInnerComprehension(PsiElement referenceElement, PsiElement definition) {
209     final PyComprehensionElement definitionComprehension = PsiTreeUtil.getParentOfType(definition, PyComprehensionElement.class);
210     if (definitionComprehension != null && PyUtil.isOwnScopeComprehension(definitionComprehension)) {
211       final PyComprehensionElement elementComprehension = PsiTreeUtil.getParentOfType(referenceElement, PyComprehensionElement.class);
212       if (elementComprehension == null || !PsiTreeUtil.isAncestor(definitionComprehension, elementComprehension, false)) {
213         return true;
214       }
215     }
216     return false;
217   }
218
219   private static boolean isInOwnScopeComprehension(PsiElement uexpr) {
220     PyComprehensionElement comprehensionElement = PsiTreeUtil.getParentOfType(uexpr, PyComprehensionElement.class);
221     return comprehensionElement != null && PyUtil.isOwnScopeComprehension(comprehensionElement);
222   }
223
224   /**
225    * Does actual resolution of resolve().
226    *
227    * @return resolution result.
228    * @see #resolve()
229    */
230   @NotNull
231   protected List<RatedResolveResult> resolveInner() {
232     final ResolveResultList ret = new ResolveResultList();
233
234     final String referencedName = myElement.getReferencedName();
235     if (referencedName == null) return ret;
236
237     if (myElement instanceof PyTargetExpression) {
238       if (PsiTreeUtil.getParentOfType(myElement, PyComprehensionElement.class) != null) {
239         ret.poke(myElement, getRate(myElement));
240         return ret;
241       }
242     }
243
244     // here we have an unqualified expr. it may be defined:
245     // ...in current file
246     ResolveProcessor processor = new ResolveProcessor(referencedName);
247
248     // Use real context here to enable correct completion and resolve in case of PyExpressionCodeFragment
249     final PsiElement realContext = PyPsiUtils.getRealContext(myElement);
250
251     PsiElement roof = findResolveRoof(referencedName, realContext);
252     PyResolveUtil.scopeCrawlUp(processor, myElement, referencedName, roof);
253     return getResultsFromProcessor(referencedName, processor, realContext, roof);
254   }
255
256   protected List<RatedResolveResult> getResultsFromProcessor(String referencedName,
257                                                              ResolveProcessor processor,
258                                                              PsiElement realContext, PsiElement roof) {
259     ResolveResultList ret = new ResolveResultList();
260     PsiElement uexpr = processor.getResult();
261     List<PsiElement> definers = processor.getDefiners();
262     if (uexpr != null) {
263       if (definers.isEmpty()) {
264         final ScopeOwner originalOwner = ScopeUtil.getScopeOwner(realContext);
265         final ScopeOwner owner = ScopeUtil.getScopeOwner(uexpr);
266         if (owner != null) {
267           final Scope scope = ControlFlowCache.getScope(owner);
268           if (uexpr == originalOwner && originalOwner instanceof PyClass) {
269             uexpr = null;
270           }
271           else if (owner == originalOwner && !scope.isGlobal(referencedName)) {
272             final List<ReadWriteInstruction> instructions = PyDefUseUtil.getLatestDefs(owner, referencedName, myElement, false);
273             final ResolveResultList latest = resolveToLatestDefs(instructions, myElement, referencedName);
274             if (!latest.isEmpty()) {
275               return latest;
276             }
277             if (owner instanceof PyClass || (instructions.isEmpty() && isInOwnScopeComprehension(uexpr))) {
278               final ScopeOwner parentOwner = ScopeUtil.getScopeOwner(owner);
279               if (parentOwner != null) {
280                 processor = new ResolveProcessor(referencedName);
281                 PyResolveUtil.scopeCrawlUp(processor, parentOwner, referencedName, roof);
282                 uexpr = processor.getResult();
283                 definers = processor.getDefiners();
284               }
285             }
286             else {
287               uexpr = null;
288             }
289           }
290           else if (owner != originalOwner && originalOwner != null && !scope.isGlobal(referencedName)) {
291             final Scope originalScope = ControlFlowCache.getScope(originalOwner);
292             if (originalScope.containsDeclaration(referencedName)) {
293               uexpr = null;
294             }
295           }
296         }
297       }
298       // sort what we got
299       for (PsiElement hit : definers) {
300         ret.poke(hit, getRate(hit));
301       }
302       final PsiElement packageInit = PyUtil.turnDirIntoInit(uexpr);
303       if (packageInit != null) {
304         uexpr = packageInit; // an import statement may have returned a dir
305       }
306     }
307     else if (!definers.isEmpty()) {
308       ret.add(new ImportedResolveResult(null, RatedResolveResult.RATE_LOW, definers));
309     }
310     PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(realContext);
311     if (uexpr == null) {
312       // ...as a part of current module
313       String name = myElement.getName();
314       if (PyModuleType.MODULE_MEMBERS.contains(name)) {
315         PyType objectType = builtinCache.getObjectType(); // "object" as a closest kin to "module"
316         if (objectType != null && name != null) {
317           ret.addAll(objectType.resolveMember(name, null, AccessDirection.READ, myContext));
318         }
319       }
320     }
321     if (uexpr != null) {
322       ret.add(new ImportedResolveResult(uexpr, getRate(uexpr), definers));
323     }
324     else {
325       for (PyReferenceResolveProvider provider : Extensions.getExtensions(PyReferenceResolveProvider.EP_NAME)) {
326         final List<RatedResolveResult> results = provider.resolveName(myElement, definers);
327         for (RatedResolveResult res : results) {
328           ret.add(res);
329         }
330       }
331     }
332
333     return ret;
334   }
335
336   private PsiElement findResolveRoof(String referencedName, PsiElement realContext) {
337     if (PyUtil.isClassPrivateName(referencedName)) {
338       // a class-private name; limited by either class or this file
339       PsiElement one = myElement;
340       do {
341         one = PyUtil.getConcealingParent(one);
342       }
343       while (one instanceof PyFunction);
344       if (one instanceof PyClass) {
345         PyArgumentList superClassExpressionList = ((PyClass)one).getSuperClassExpressionList();
346         if (superClassExpressionList == null || !PsiTreeUtil.isAncestor(superClassExpressionList, myElement, false)) {
347           return one;
348         }
349       }
350     }
351
352     if (myElement instanceof PyTargetExpression) {
353       final ScopeOwner scopeOwner = PsiTreeUtil.getParentOfType(myElement, ScopeOwner.class);
354       final Scope scope;
355       if (scopeOwner != null) {
356         scope = ControlFlowCache.getScope(scopeOwner);
357         final String name = myElement.getName();
358         if (scope.isNonlocal(name)) {
359           final ScopeOwner nonlocalOwner = ScopeUtil.getDeclarationScopeOwner(myElement, referencedName);
360           if (nonlocalOwner != null && !(nonlocalOwner instanceof PyFile)) {
361             return nonlocalOwner;
362           }
363         }
364         if (!scope.isGlobal(name)) {
365           return scopeOwner;
366         }
367       }
368     }
369     return realContext.getContainingFile();
370   }
371
372   // NOTE: very crude
373
374   public static int getRate(PsiElement elt) {
375     int rate;
376     if (elt instanceof PyImportedNameDefiner || elt instanceof PyReferenceExpression) {
377       rate = RatedResolveResult.RATE_LOW;
378     }
379     else if (elt instanceof PyFile) {
380       rate = RatedResolveResult.RATE_HIGH;
381     }
382     else {
383       rate = RatedResolveResult.RATE_NORMAL;
384     }
385     return rate;
386   }
387
388   @Override
389   @NotNull
390   public String getCanonicalText() {
391     return getRangeInElement().substring(getElement().getText());
392   }
393
394   @Override
395   public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
396     ASTNode nameElement = myElement.getNameElement();
397     if (newElementName.endsWith(PyNames.DOT_PY)) {
398       newElementName = newElementName.substring(0, newElementName.length() - PyNames.DOT_PY.length());
399     }
400     if (nameElement != null && PyNames.isIdentifier(newElementName)) {
401       final ASTNode newNameElement = PyUtil.createNewName(myElement, newElementName);
402       myElement.getNode().replaceChild(nameElement, newNameElement);
403     }
404     return myElement;
405   }
406
407   @Override
408   @Nullable
409   public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
410     return null;
411   }
412
413    @Override
414    public boolean isReferenceTo(PsiElement element) {
415     if (element instanceof PsiFileSystemItem) {
416       // may be import via alias, so don't check if names match, do simple resolve check instead
417       PsiElement resolveResult = resolve();
418       if (resolveResult instanceof PyImportedModule) {
419         resolveResult = resolveResult.getNavigationElement();
420       }
421       if (element instanceof PsiDirectory) {
422         if (resolveResult instanceof PyFile) {
423           final PyFile file = (PyFile)resolveResult;
424           if (PyUtil.isPackage(file) && file.getContainingDirectory() == element) {
425             return true;
426           }
427         }
428         else if (resolveResult instanceof PsiDirectory) {
429           final PsiDirectory directory = (PsiDirectory)resolveResult;
430           if (PyUtil.isPackage(directory, null) && directory == element) {
431             return true;
432           }
433         }
434       }
435       return resolveResult == element;
436     }
437     if (element instanceof PsiNamedElement) {
438       final String elementName = ((PsiNamedElement)element).getName();
439       if ((Comparing.equal(myElement.getReferencedName(), elementName) || PyNames.INIT.equals(elementName))) {
440         if (!haveQualifiers(element)) {
441           final ScopeOwner ourScopeOwner = ScopeUtil.getScopeOwner(getElement());
442           final ScopeOwner theirScopeOwner = ScopeUtil.getScopeOwner(element);
443           if (element instanceof PyParameter || element instanceof PyTargetExpression) {
444             // Check if the reference is in the same or inner scope of the element scope, not shadowed by an intermediate declaration
445             if (resolvesToSameLocal(element, elementName, ourScopeOwner, theirScopeOwner)) {
446               return true;
447             }
448           }
449
450           final PsiElement resolveResult = resolve();
451           if (resolveResult == element) {
452             return true;
453           }
454
455           // we shadow their name or they shadow ours (PY-6241)
456           if (resolveResult instanceof PsiNamedElement && resolveResult instanceof ScopeOwner && element instanceof ScopeOwner &&
457               theirScopeOwner == ScopeUtil.getScopeOwner(resolveResult)) {
458             return true;
459           }
460
461           if (!haveQualifiers(element) && ourScopeOwner != null && theirScopeOwner != null) {
462             if (resolvesToSameGlobal(element, elementName, ourScopeOwner, theirScopeOwner, resolveResult)) return true;
463           }
464
465           if (resolvesToWrapper(element, resolveResult)) {
466             return true;
467           }
468         }
469         if (element instanceof PyExpression) {
470           final PyExpression expr = (PyExpression)element;
471           if (PyUtil.isClassAttribute(myElement) && (PyUtil.isClassAttribute(expr) || PyUtil.isInstanceAttribute(expr))) {
472             final PyClass c1 = PsiTreeUtil.getParentOfType(element, PyClass.class);
473             final PyClass c2 = PsiTreeUtil.getParentOfType(myElement, PyClass.class);
474             if (c1 != null && c2 != null && (c1.isSubclass(c2) || c2.isSubclass(c1))) {
475               return true;
476             }
477           }
478         }
479       }
480     }
481     return false;
482   }
483
484   private boolean resolvesToSameLocal(PsiElement element, String elementName, ScopeOwner ourScopeOwner, ScopeOwner theirScopeOwner) {
485     final PsiElement ourContainer = findContainer(getElement());
486     final PsiElement theirContainer = findContainer(element);
487     if (ourContainer != null) {
488       if (ourContainer == theirContainer) {
489         return true;
490       }
491       if (PsiTreeUtil.isAncestor(theirContainer, ourContainer, true)) {
492         if (ourScopeOwner != theirScopeOwner) {
493           boolean shadowsName = false;
494           ScopeOwner owner = ourScopeOwner;
495           while(owner != theirScopeOwner && owner != null) {
496             if (ControlFlowCache.getScope(owner).containsDeclaration(elementName)) {
497               shadowsName = true;
498               break;
499             }
500             owner = ScopeUtil.getScopeOwner(owner);
501           }
502           if (!shadowsName) {
503             return true;
504           }
505         }
506       }
507     }
508     return false;
509   }
510
511   @Nullable
512   private static PsiElement findContainer(@NotNull PsiElement element) {
513     final PyElement parent = PsiTreeUtil.getParentOfType(element, ScopeOwner.class, PyComprehensionElement.class);
514     if (parent instanceof PyListCompExpression && LanguageLevel.forElement(element).isOlderThan(LanguageLevel.PYTHON30)) {
515       return findContainer(parent);
516     }
517     return parent;
518   }
519
520   private boolean resolvesToSameGlobal(PsiElement element, String elementName, ScopeOwner ourScopeOwner, ScopeOwner theirScopeOwner,
521                                        PsiElement resolveResult) {
522     // Handle situations when there is no top-level declaration for globals and transitive resolve doesn't help
523     final PsiFile ourFile = getElement().getContainingFile();
524     final PsiFile theirFile = element.getContainingFile();
525     if (ourFile == theirFile) {
526       final boolean ourIsGlobal = ControlFlowCache.getScope(ourScopeOwner).isGlobal(elementName);
527       final boolean theirIsGlobal = ControlFlowCache.getScope(theirScopeOwner).isGlobal(elementName);
528       if (ourIsGlobal && theirIsGlobal) {
529         return true;
530       }
531     }
532     if (ScopeUtil.getScopeOwner(resolveResult) == ourFile && ControlFlowCache.getScope(theirScopeOwner).isGlobal(elementName)) {
533       return true;
534     }
535     return false;
536   }
537
538   protected boolean resolvesToWrapper(PsiElement element, PsiElement resolveResult) {
539     if (element instanceof PyFunction && ((PyFunction) element).getContainingClass() != null && resolveResult instanceof PyTargetExpression) {
540       final PyExpression assignedValue = ((PyTargetExpression)resolveResult).findAssignedValue();
541       if (assignedValue instanceof PyCallExpression) {
542         final PyCallExpression call = (PyCallExpression)assignedValue;
543         final Pair<String,PyFunction> functionPair = PyCallExpressionHelper.interpretAsModifierWrappingCall(call, myElement);
544         if (functionPair != null && functionPair.second == element) {
545           return true;
546         }
547       }
548     }
549     return false;
550   }
551
552   private boolean haveQualifiers(PsiElement element) {
553     if (myElement.isQualified()) {
554       return true;
555     }
556     if (element instanceof PyQualifiedExpression && ((PyQualifiedExpression)element).isQualified()) {
557       return true;
558     }
559     return false;
560   }
561
562   @Override
563   @NotNull
564   public Object[] getVariants() {
565     final List<LookupElement> ret = Lists.newArrayList();
566
567     // Use real context here to enable correct completion and resolve in case of PyExpressionCodeFragment!!!
568     final PsiElement originalElement = CompletionUtil.getOriginalElement(myElement);
569     final PyQualifiedExpression element = originalElement instanceof PyQualifiedExpression ?
570                                           (PyQualifiedExpression)originalElement : myElement;
571     final PsiElement realContext = PyPsiUtils.getRealContext(element);
572
573     // include our own names
574     final int underscores = PyUtil.getInitialUnderscores(element.getName());
575     final CompletionVariantsProcessor processor = new CompletionVariantsProcessor(element);
576     final ScopeOwner owner = realContext instanceof ScopeOwner ? (ScopeOwner)realContext : ScopeUtil.getScopeOwner(realContext);
577     if (owner != null) {
578       PyResolveUtil.scopeCrawlUp(processor, owner, null, null);
579     }
580
581     // in a call, include function's arg names
582     KeywordArgumentCompletionUtil.collectFunctionArgNames(element, ret);
583
584     // include builtin names
585     final PyFile builtinsFile = PyBuiltinCache.getInstance(element).getBuiltinsFile();
586     if (builtinsFile != null) {
587       PyResolveUtil.scopeCrawlUp(processor, builtinsFile, null, null);
588     }
589
590     if (underscores >= 2) {
591       // if we're a normal module, add module's attrs
592       PsiFile f = realContext.getContainingFile();
593       if (f instanceof PyFile) {
594         for (String name : PyModuleType.getPossibleInstanceMembers()) {
595           ret.add(LookupElementBuilder.create(name).withIcon(PlatformIcons.FIELD_ICON));
596         }
597       }
598     }
599
600     // Throw away fake elements used for completion internally
601     for (LookupElement e : processor.getResultList()) {
602       final Object o = e.getObject();
603       if (o instanceof PsiElement) {
604         final PsiElement original = CompletionUtil.getOriginalElement((PsiElement)o);
605         if (original == null) {
606           continue;
607         }
608       }
609       ret.add(e);
610     }
611
612     return ret.toArray();
613   }
614
615   @Override
616   public boolean isSoft() {
617     return false;
618   }
619
620   @Override
621   public HighlightSeverity getUnresolvedHighlightSeverity(TypeEvalContext context) {
622     if (isBuiltInConstant()) return null;
623     final PyExpression qualifier = myElement.getQualifier();
624     if (qualifier == null) {
625       return HighlightSeverity.ERROR;
626     }
627     if (context.getType(qualifier) != null) {
628       return HighlightSeverity.WARNING;
629     }
630     return null;
631   }
632
633   private boolean isBuiltInConstant() {
634     // TODO: generalize
635     String name = myElement.getReferencedName();
636     return PyNames.NONE.equals(name) || "True".equals(name) || "False".equals(name);
637   }
638
639   @Override
640   @Nullable
641   public String getUnresolvedDescription() {
642     return null;
643   }
644
645
646   // our very own caching resolver
647
648   private static class CachingResolver implements ResolveCache.PolyVariantResolver<PyReferenceImpl> {
649     public static CachingResolver INSTANCE = new CachingResolver();
650     private ThreadLocal<AtomicInteger> myNesting = new ThreadLocal<AtomicInteger>() {
651       @Override
652       protected AtomicInteger initialValue() {
653         return new AtomicInteger();
654       }
655     };
656
657     private static final int MAX_NESTING_LEVEL = 30;
658
659     @Override
660     @NotNull
661     public ResolveResult[] resolve(@NotNull final PyReferenceImpl ref, final boolean incompleteCode) {
662       if (myNesting.get().getAndIncrement() >= MAX_NESTING_LEVEL) {
663         System.out.println("Stack overflow pending");
664       }
665       try {
666         return ref.multiResolveInner();
667       }
668       finally {
669         myNesting.get().getAndDecrement();
670       }
671     }
672   }
673
674   @Override
675   public boolean equals(Object o) {
676     if (this == o) return true;
677     if (o == null || getClass() != o.getClass()) return false;
678
679     PyReferenceImpl that = (PyReferenceImpl)o;
680
681     if (!myElement.equals(that.myElement)) return false;
682     if (!myContext.equals(that.myContext)) return false;
683
684     return true;
685   }
686
687   @Override
688   public int hashCode() {
689     return myElement.hashCode();
690   }
691
692   protected static Object[] getTypeCompletionVariants(PyExpression pyExpression, PyType type) {
693     ProcessingContext context = new ProcessingContext();
694     context.put(PyType.CTX_NAMES, new HashSet<String>());
695     return type.getCompletionVariants(pyExpression.getName(), pyExpression, context);
696   }
697 }