constructor search should not hang idea

This commit is contained in:
Alexey Kudravtsev
2013-05-29 12:25:37 +04:00
parent af281e22b2
commit f3448d5af8
3 changed files with 194 additions and 132 deletions
@@ -1,5 +1,8 @@
package com.intellij.psi.impl.search;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadActionProcessor;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.TextRange;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
@@ -12,6 +15,7 @@ import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.PairProcessor;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
/**
* @author max
@@ -19,31 +23,30 @@ import com.intellij.util.Processor;
public class ConstructorReferencesSearchHelper {
private final PsiManager myManager;
public ConstructorReferencesSearchHelper(final PsiManager manager) {
public ConstructorReferencesSearchHelper(@NotNull PsiManager manager) {
myManager = manager;
}
public boolean processConstructorReferences(final Processor<PsiReference> processor,
final PsiMethod constructor,
final SearchScope searchScope,
public boolean processConstructorReferences(@NotNull final Processor<PsiReference> processor,
@NotNull final PsiMethod constructor,
@NotNull final PsiClass containingClass,
@NotNull final SearchScope searchScope,
boolean ignoreAccessScope,
final boolean isStrictSignatureSearch, SearchRequestCollector collector) {
PsiClass aClass = constructor.getContainingClass();
if (aClass == null) {
return true;
}
if (aClass.isEnum()) {
for (PsiField field : aClass.getFields()) {
if (field instanceof PsiEnumConstant) {
PsiReference reference = field.getReference();
if (reference != null && reference.isReferenceTo(constructor)) {
if (!processor.process(reference)) {
return false;
}
}
}
final boolean isStrictSignatureSearch,
@NotNull SearchRequestCollector collector) {
final boolean[] constructorCanBeCalledImplicitly = new boolean[1];
final boolean[] isEnum = new boolean[1];
final boolean[] isUnder18 = new boolean[1];
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
constructorCanBeCalledImplicitly[0] = constructor.getParameterList().getParametersCount() == 0;
isEnum[0] = containingClass.isEnum();
isUnder18[0] = PsiUtil.getLanguageLevel(containingClass).isAtLeast(LanguageLevel.JDK_1_8);
}
});
if (isEnum[0]) {
if (!processEnumReferences(processor, constructor, containingClass)) return false;
}
// search usages like "new XXX(..)"
@@ -63,7 +66,7 @@ public class ConstructorReferencesSearchHelper {
}
}
else {
if (myManager.areElementsEquivalent(constructor.getContainingClass(), constructor1.getContainingClass())) {
if (myManager.areElementsEquivalent(containingClass, constructor1.getContainingClass())) {
return processor.process(reference);
}
}
@@ -73,30 +76,18 @@ public class ConstructorReferencesSearchHelper {
}
};
ReferencesSearch.searchOptimized(aClass, searchScope, ignoreAccessScope, collector, true, processor1);
if (PsiUtil.getLanguageLevel(aClass).isAtLeast(LanguageLevel.JDK_1_8)) {
ReferencesSearch.search(aClass).forEach(new Processor<PsiReference>() {
@Override
public boolean process(PsiReference reference) {
final PsiElement element = reference.getElement();
if (element != null) {
final PsiElement parent = element.getParent();
if (parent instanceof PsiMethodReferenceExpression &&
((PsiMethodReferenceExpression)parent).getReferenceNameElement() instanceof PsiKeyword) {
if (((PsiMethodReferenceExpression)parent).isReferenceTo(constructor)) {
processor.process((PsiReference)parent);
}
}
}
return true;
}
});
ReferencesSearch.searchOptimized(containingClass, searchScope, ignoreAccessScope, collector, true, processor1);
if (isUnder18[0]) {
if (!process18MethodPointers(processor, constructor, containingClass)) return false;
}
final boolean constructorCanBeCalledImplicitly = constructor.getParameterList().getParametersCount() == 0;
// search usages like "this(..)"
if (!processSuperOrThis(processor, aClass, constructor, constructorCanBeCalledImplicitly, searchScope, isStrictSignatureSearch,
PsiKeyword.THIS)) {
if (!ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
public Boolean compute() {
return processSuperOrThis(containingClass, constructor, constructorCanBeCalledImplicitly[0], searchScope, isStrictSignatureSearch,
PsiKeyword.THIS, processor);
}
})) {
return false;
}
@@ -106,23 +97,67 @@ public class ConstructorReferencesSearchHelper {
public boolean process(PsiClass inheritor) {
final PsiElement navigationElement = inheritor.getNavigationElement();
if (navigationElement instanceof PsiClass) {
return processSuperOrThis(processor, (PsiClass)navigationElement, constructor, constructorCanBeCalledImplicitly, searchScope, isStrictSignatureSearch, PsiKeyword.SUPER);
return processSuperOrThis((PsiClass)navigationElement, constructor, constructorCanBeCalledImplicitly[0], searchScope,
isStrictSignatureSearch, PsiKeyword.SUPER, processor);
}
return true;
}
};
return ClassInheritorsSearch.search(aClass, searchScope, false).forEach(processor2);
return ClassInheritorsSearch.search(containingClass, searchScope, false).forEach(processor2);
}
private boolean processSuperOrThis(final Processor<PsiReference> processor,
final PsiClass inheritor,
final PsiMethod constructor, final boolean constructorCanBeCalledImplicitly, final SearchScope searchScope,
final boolean isStrictSignatureSearch,
final String superOrThisKeyword) {
private static boolean processEnumReferences(@NotNull final Processor<PsiReference> processor,
@NotNull final PsiMethod constructor,
@NotNull final PsiClass aClass) {
return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
public Boolean compute() {
for (PsiField field : aClass.getFields()) {
if (field instanceof PsiEnumConstant) {
PsiReference reference = field.getReference();
if (reference != null && reference.isReferenceTo(constructor)) {
if (!processor.process(reference)) {
return false;
}
}
}
}
return true;
}
});
}
private static boolean process18MethodPointers(@NotNull final Processor<PsiReference> processor,
@NotNull final PsiMethod constructor,
@NotNull PsiClass aClass) {
return ReferencesSearch.search(aClass).forEach(new ReadActionProcessor<PsiReference>() {
@Override
public boolean processInReadAction(PsiReference reference) {
final PsiElement element = reference.getElement();
if (element != null) {
final PsiElement parent = element.getParent();
if (parent instanceof PsiMethodReferenceExpression &&
((PsiMethodReferenceExpression)parent).getReferenceNameElement() instanceof PsiKeyword) {
if (((PsiMethodReferenceExpression)parent).isReferenceTo(constructor)) {
if (!processor.process((PsiReference)parent)) return false;
}
}
}
return true;
}
});
}
private boolean processSuperOrThis(@NotNull PsiClass inheritor,
@NotNull PsiMethod constructor,
final boolean constructorCanBeCalledImplicitly,
@NotNull SearchScope searchScope,
final boolean isStrictSignatureSearch,
@NotNull String superOrThisKeyword,
@NotNull Processor<PsiReference> processor) {
PsiMethod[] constructors = inheritor.getConstructors();
if (constructors.length == 0 && constructorCanBeCalledImplicitly) {
processImplicitConstructorCall(inheritor, processor, constructor, inheritor);
if (!processImplicitConstructorCall(inheritor, processor, constructor, inheritor)) return false;
}
for (PsiMethod method : constructors) {
PsiCodeBlock body = method.getBody();
@@ -137,7 +172,7 @@ public class ConstructorReferencesSearchHelper {
if (expr instanceof PsiMethodCallExpression) {
PsiReferenceExpression refExpr = ((PsiMethodCallExpression)expr).getMethodExpression();
if (PsiSearchScopeUtil.isInScope(searchScope, refExpr)) {
if (refExpr.getText().equals(superOrThisKeyword)) {
if (refExpr.textMatches(superOrThisKeyword)) {
PsiElement referencedElement = refExpr.resolve();
if (referencedElement instanceof PsiMethod) {
PsiMethod constructor1 = (PsiMethod)referencedElement;
@@ -154,43 +189,48 @@ public class ConstructorReferencesSearchHelper {
}
}
if (constructorCanBeCalledImplicitly) {
processImplicitConstructorCall(method, processor, constructor, inheritor);
if (!processImplicitConstructorCall(method, processor, constructor, inheritor)) return false;
}
}
return true;
}
private void processImplicitConstructorCall(final PsiMember usage,
final Processor<PsiReference> processor,
final PsiMethod constructor,
final PsiClass containingClass) {
if (containingClass instanceof PsiAnonymousClass) return;
PsiClass superClass = containingClass.getSuperClass();
if (myManager.areElementsEquivalent(constructor.getContainingClass(), superClass)) {
processor.process(new LightMemberReference(myManager, usage, PsiSubstitutor.EMPTY) {
@Override
public PsiElement getElement() {
return usage;
}
@Override
public TextRange getRangeInElement() {
if (usage instanceof PsiClass) {
PsiIdentifier identifier = ((PsiClass)usage).getNameIdentifier();
if (identifier != null) return TextRange.from(identifier.getStartOffsetInParent(), identifier.getTextLength());
}
else if (usage instanceof PsiField) {
PsiIdentifier identifier = ((PsiField)usage).getNameIdentifier();
return TextRange.from(identifier.getStartOffsetInParent(), identifier.getTextLength());
}
else if (usage instanceof PsiMethod) {
PsiIdentifier identifier = ((PsiMethod)usage).getNameIdentifier();
if (identifier != null) return TextRange.from(identifier.getStartOffsetInParent(), identifier.getTextLength());
}
return super.getRangeInElement();
}
});
private boolean processImplicitConstructorCall(@NotNull final PsiMember usage,
@NotNull final Processor<PsiReference> processor,
@NotNull final PsiMethod constructor,
@NotNull final PsiClass containingClass) {
if (containingClass instanceof PsiAnonymousClass) return true;
boolean same = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
public Boolean compute() {
return myManager.areElementsEquivalent(constructor.getContainingClass(), containingClass.getSuperClass());
}
});
if (!same) {
return true;
}
return processor.process(new LightMemberReference(myManager, usage, PsiSubstitutor.EMPTY) {
@Override
public PsiElement getElement() {
return usage;
}
@Override
public TextRange getRangeInElement() {
if (usage instanceof PsiClass) {
PsiIdentifier identifier = ((PsiClass)usage).getNameIdentifier();
if (identifier != null) return TextRange.from(identifier.getStartOffsetInParent(), identifier.getTextLength());
}
else if (usage instanceof PsiField) {
PsiIdentifier identifier = ((PsiField)usage).getNameIdentifier();
return TextRange.from(identifier.getStartOffsetInParent(), identifier.getTextLength());
}
else if (usage instanceof PsiMethod) {
PsiIdentifier identifier = ((PsiMethod)usage).getNameIdentifier();
if (identifier != null) return TextRange.from(identifier.getStartOffsetInParent(), identifier.getTextLength());
}
return super.getRangeInElement();
}
});
}
}
@@ -1,9 +1,9 @@
package com.intellij.psi.impl.search;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.QueryExecutorBase;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiReference;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
@@ -12,16 +12,26 @@ import org.jetbrains.annotations.NotNull;
* @author max
*/
public class ConstructorReferencesSearcher extends QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters> {
public ConstructorReferencesSearcher() {
super(true);
}
@Override
public void processQuery(@NotNull ReferencesSearch.SearchParameters p, @NotNull Processor<PsiReference> consumer) {
final PsiElement element = p.getElementToSearch();
if (element instanceof PsiMethod && ((PsiMethod)element).isConstructor()) {
new ConstructorReferencesSearchHelper(element.getManager())
.processConstructorReferences(consumer, (PsiMethod)p.getElementToSearch(), p.getScope(), p.isIgnoreAccessScope(), true, p.getOptimizer());
if (!(element instanceof PsiMethod)) {
return;
}
final PsiMethod method = (PsiMethod)element;
final PsiManager[] manager = new PsiManager[1];
PsiClass aClass = ApplicationManager.getApplication().runReadAction(new Computable<PsiClass>() {
public PsiClass compute() {
if (!method.isConstructor()) return null;
PsiClass aClass = method.getContainingClass();
manager[0] = aClass == null ? null : aClass.getManager();
return aClass;
}
});
if (aClass == null) {
return;
}
new ConstructorReferencesSearchHelper(manager[0])
.processConstructorReferences(consumer, method, aClass, p.getScope(), p.isIgnoreAccessScope(), true, p.getOptimizer());
}
}
@@ -1,7 +1,8 @@
package com.intellij.psi.impl.search;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.QueryExecutorBase;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.SearchRequestCollector;
@@ -17,66 +18,77 @@ import org.jetbrains.annotations.NotNull;
* @author max
*/
public class MethodUsagesSearcher extends QueryExecutorBase<PsiReference, MethodReferencesSearch.SearchParameters> {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.search.MethodUsagesSearcher");
public MethodUsagesSearcher() {
super(true);
}
@Override
public void processQuery(@NotNull MethodReferencesSearch.SearchParameters p, @NotNull final Processor<PsiReference> consumer) {
final PsiMethod method = p.getMethod();
final boolean[] isConstructor = new boolean[1];
final PsiManager[] psiManager = new PsiManager[1];
final String[] methodName = new String[1];
final boolean[] isValueAnnotation = new boolean[1];
final boolean[] needStrictSignatureSearch = new boolean[1];
final boolean strictSignatureSearch = p.isStrictSignatureSearch();
final PsiClass aClass = ApplicationManager.getApplication().runReadAction(new Computable<PsiClass>() {
public PsiClass compute() {
PsiClass aClass = method.getContainingClass();
if (aClass == null) return null;
isConstructor[0] = method.isConstructor();
psiManager[0] = aClass.getManager();
methodName[0] = method.getName();
isValueAnnotation[0] = PsiUtil.isAnnotationMethod(method) &&
PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.equals(methodName[0]) &&
method.getParameterList().getParametersCount() == 0;
needStrictSignatureSearch[0] = strictSignatureSearch && (aClass instanceof PsiAnonymousClass
|| aClass.hasModifierProperty(PsiModifier.FINAL)
|| method.hasModifierProperty(PsiModifier.STATIC)
|| method.hasModifierProperty(PsiModifier.FINAL)
|| method.hasModifierProperty(PsiModifier.PRIVATE));
return aClass;
}
});
if (aClass == null) return;
final SearchRequestCollector collector = p.getOptimizer();
final SearchScope searchScope = p.getScope();
final PsiManager psiManager = method.getManager();
final PsiClass aClass = method.getContainingClass();
if (aClass == null) return;
final boolean strictSignatureSearch = p.isStrictSignatureSearch();
if (method.isConstructor()) {
new ConstructorReferencesSearchHelper(psiManager).
processConstructorReferences(consumer, method, searchScope, !strictSignatureSearch, strictSignatureSearch, collector);
if (isConstructor[0]) {
new ConstructorReferencesSearchHelper(psiManager[0]).
processConstructorReferences(consumer, method, aClass, searchScope, !strictSignatureSearch, strictSignatureSearch, collector);
}
if (PsiUtil.isAnnotationMethod(method) &&
PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.equals(method.getName()) &&
method.getParameterList().getParametersCount() == 0) {
ReferencesSearch.search(aClass, p.getScope()).forEach(PsiAnnotationMethodReferencesSearcher.createImplicitDefaultAnnotationMethodConsumer( consumer));
if (isValueAnnotation[0]) {
Processor<PsiReference> refProcessor = PsiAnnotationMethodReferencesSearcher.createImplicitDefaultAnnotationMethodConsumer(consumer);
ReferencesSearch.search(aClass, searchScope).forEach(refProcessor);
}
boolean needStrictSignatureSearch = strictSignatureSearch && (aClass instanceof PsiAnonymousClass
|| aClass.hasModifierProperty(PsiModifier.FINAL)
|| method.hasModifierProperty(PsiModifier.STATIC)
|| method.hasModifierProperty(PsiModifier.FINAL)
|| method.hasModifierProperty(PsiModifier.PRIVATE));
if (needStrictSignatureSearch) {
if (needStrictSignatureSearch[0]) {
ReferencesSearch.searchOptimized(method, searchScope, false, collector, consumer);
return;
}
final String textToSearch = method.getName();
if (StringUtil.isEmpty(textToSearch)) {
if (StringUtil.isEmpty(methodName[0])) {
return;
}
final PsiMethod[] methods = strictSignatureSearch ? new PsiMethod[]{method} : aClass.findMethodsByName(textToSearch, false);
SearchScope accessScope = methods[0].getUseScope();
for (int i = 1; i < methods.length; i++) {
PsiMethod method1 = methods[i];
accessScope = accessScope.union(method1.getUseScope());
}
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final PsiMethod[] methods = strictSignatureSearch ? new PsiMethod[]{method} : aClass.findMethodsByName(methodName[0], false);
SearchScope accessScope = methods[0].getUseScope();
for (int i = 1; i < methods.length; i++) {
PsiMethod method1 = methods[i];
accessScope = accessScope.union(method1.getUseScope());
}
final SearchScope restrictedByAccess = searchScope.intersectWith(accessScope);
SearchScope restrictedByAccessScope = searchScope.intersectWith(accessScope);
short searchContext = UsageSearchContext.IN_CODE | UsageSearchContext.IN_COMMENTS | UsageSearchContext.IN_FOREIGN_LANGUAGES;
collector.searchWord(textToSearch, restrictedByAccess, searchContext, true,
new MethodTextOccurrenceProcessor(aClass, strictSignatureSearch, methods));
SimpleAccessorReferenceSearcher.addPropertyAccessUsages(method, restrictedByAccess, collector);
short searchContext = UsageSearchContext.IN_CODE | UsageSearchContext.IN_COMMENTS | UsageSearchContext.IN_FOREIGN_LANGUAGES;
collector.searchWord(methodName[0], restrictedByAccessScope, searchContext, true,
new MethodTextOccurrenceProcessor(aClass, strictSignatureSearch, methods));
SimpleAccessorReferenceSearcher.addPropertyAccessUsages(method, restrictedByAccessScope, collector);
}
});
}
}