Merge commit 'origin/master'
authorMaxim Medvedev <maxim.medvedev@jetbrains.com>
Fri, 30 Jul 2010 09:05:01 +0000 (13:05 +0400)
committerMaxim Medvedev <maxim.medvedev@jetbrains.com>
Fri, 30 Jul 2010 09:05:01 +0000 (13:05 +0400)
Conflicts:
plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearchHelper.java

1  2 
plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java

index da1ef93bd10ff8c60894903558453a6e82a59071,15642f8602ac339d3fe3e01d7698a2fd6360435d..67e627d267c99767bd1d5edac4da427b4329f70c
@@@ -26,10 -77,338 +77,338 @@@ import java.util.Set
  /**
   * @author Maxim.Medvedev
   */
- public class GroovyConstructorUsagesSearcher implements QueryExecutor<PsiReference, MethodReferencesSearch.SearchParameters> {
-   public boolean execute(MethodReferencesSearch.SearchParameters p, final Processor<PsiReference> consumer) {
-     final PsiMethod method = p.getMethod();
-     final SearchScope searchScope = p.getScope();
-     return GroovyConstructorUsagesSearchHelper.execute(method, searchScope, consumer);
+ public class GroovyConstructorUsagesSearcher extends QueryExecutorBase<PsiReference, MethodReferencesSearch.SearchParameters> {
+   public GroovyConstructorUsagesSearcher() {
+     super(true);
+   }
+   @Override
+   public void processQuery(MethodReferencesSearch.SearchParameters p, Processor<PsiReference> consumer) {
+     processConstructorUsages(p.getMethod(), p.getScope(), consumer, p.getOptimizer(), true);
+   }
+   static void processConstructorUsages(final PsiMethod constructor, final SearchScope searchScope, final Processor<PsiReference> consumer, final SearchRequestCollector collector, final boolean searchGppCalls) {
+     if (!constructor.isConstructor()) return;
+     SearchScope onlyGroovy = searchScope;
+     if (onlyGroovy instanceof GlobalSearchScope) {
+       onlyGroovy = GlobalSearchScope.getScopeRestrictedByFileTypes((GlobalSearchScope)onlyGroovy, GroovyFileType.GROOVY_FILE_TYPE);
+     }
+     final PsiClass clazz = constructor.getContainingClass();
+     if (clazz == null) return;
+     if (clazz.isEnum() && clazz instanceof GroovyPsiElement) {
+       for (PsiField field : clazz.getFields()) {
+         if (field instanceof GrEnumConstant) {
+           final PsiReference ref = field.getReference();
+           if (ref != null && ref.isReferenceTo(constructor)) {
+             if (!consumer.process(ref)) return;
+           }
+         }
+       }
+     }
+     final Set<PsiMethod> processedMethods = new ConcurrentHashSet<PsiMethod>();
+     ReferencesSearch.searchOptimized(clazz, searchScope, true, collector, true, new PairProcessor<PsiReference, SearchRequestCollector>() {
+       @Override
+       public boolean process(PsiReference ref, SearchRequestCollector collector) {
+         final PsiElement element = ref.getElement();
+         if (element instanceof GrCodeReferenceElement) {
+           if (!processGroovyConstructorUsages((GrCodeReferenceElement)element, constructor, consumer, ref, !searchGppCalls)) {
+             return false;
+           }
+         }
+         if (searchGppCalls) {
+           final PsiMethod method = getMethodToSearchForCallsWithLiteralArguments(element, clazz);
+           if (method != null && processedMethods.add(method)) {
+             processGppMethodCalls(clazz, constructor, consumer, searchScope, collector, method);
+           }
+         }
+         return true;
+       }
+     });
+     //this()
+     if (clazz instanceof GrTypeDefinition) {
+       if (!processConstructors(constructor, consumer, clazz, true)) {
+         return;
+       }
+     }
+     //super()
+     DirectClassInheritorsSearch.search(clazz, onlyGroovy).forEach(new Processor<PsiClass>() {
+       public boolean process(PsiClass inheritor) {
+         if (inheritor instanceof GrTypeDefinition) {
+           if (!processConstructors(constructor, consumer, inheritor, false)) return false;
+         }
+         return true;
+       }
+     });
+   }
+   @Nullable
+   static PsiMethod getMethodToSearchForCallsWithLiteralArguments(PsiElement element, PsiClass targetClass) {
+     final PsiParameter parameter = PsiTreeUtil.getParentOfType(element, PsiParameter.class);
+     if (parameter != null) {
+       final PsiMethod method = PsiTreeUtil.getParentOfType(parameter, PsiMethod.class);
+       if (method != null && Arrays.asList(method.getParameterList().getParameters()).contains(parameter)) {
+         final PsiType parameterType = parameter.getType();
+         if (parameterType instanceof PsiClassType) {
+           if (method.getManager().areElementsEquivalent(targetClass, ((PsiClassType)parameterType).resolve())) {
+             return method;
+           }
+         }
+       }
+     }
+     return null;
+   }
+   private static void processGppMethodCalls(final PsiClass targetClass,
+                                             final PsiMethod originalTarget,
+                                             final Processor<PsiReference> originalProcessor,
+                                             SearchScope scope,
+                                             SearchRequestCollector originalCollector, @NotNull PsiMethod currentTarget) {
+     final SearchScope gppScope = getGppScope(targetClass.getProject()).intersectWith(scope);
+     final ReadActionProcessor<PsiReference> gppCallProcessor = new ReadActionProcessor<PsiReference>() {
+       @Override
+       public boolean processInReadAction(PsiReference psiReference) {
+         if (psiReference instanceof GrReferenceElement) {
+           final PsiElement parent = ((GrReferenceElement)psiReference).getParent();
+           if (parent instanceof GrCall) {
+             final GrArgumentList argList = ((GrCall)parent).getArgumentList();
+             if (argList != null) {
+               boolean checkedTypedContext = false;
+               for (GrExpression argument : argList.getExpressionArguments()) {
+                 if (argument instanceof GrListOrMap && !((GrListOrMap)argument).isMap()) {
+                   if (!checkedTypedContext) {
+                     if (!GppTypeConverter.hasTypedContext(parent)) {
+                       return true;
+                     }
+                     checkedTypedContext = true;
+                   }
+                   for (PsiType psiType : GroovyExpectedTypesProvider.getDefaultExpectedTypes(argument)) {
+                     if (psiType instanceof PsiClassType &&
+                         targetClass.getManager().areElementsEquivalent(targetClass, ((PsiClassType)psiType).resolve()) &&
+                         !checkListInstantiation(originalTarget, originalProcessor, (GrListOrMap)argument, (PsiClassType)psiType)) {
+                       return false;
+                     }
+                   }
+                 }
+               }
+             }
+           }
+         }
+         return true;
+       }
+     };
+     if (currentTarget.isConstructor()) {
+       processConstructorUsages(currentTarget, gppScope, gppCallProcessor, originalCollector, false);
+     }
+     else {
+       MethodReferencesSearch.searchOptimized(currentTarget, gppScope, true, originalCollector, gppCallProcessor);
+     }
+   }
+   static GlobalSearchScope getGppScope(final Project project) {
+     return CachedValuesManager.getManager(project).getCachedValue(project, new CachedValueProvider<GlobalSearchScope>() {
+       @Override
+       public Result<GlobalSearchScope> compute() {
+         return Result.create(calcGppScope(project), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, ProjectRootManager.getInstance(project));
+       }
+     });
+   }
+   static boolean processGroovyConstructorUsages(GrCodeReferenceElement element,
+                                                         final PsiMethod constructor,
+                                                         final Processor<PsiReference> consumer,
+                                                         PsiReference ref, boolean usualCallsOnly) {
+     PsiElement parent = element.getParent();
+     if (parent instanceof GrAnonymousClassDefinition) {
+       parent = parent.getParent();
+     }
+     if (parent instanceof GrNewExpression) {
+       final PsiMethod resolvedConstructor = ((GrNewExpression)parent).resolveConstructor();
+       if (constructor.getManager().areElementsEquivalent(resolvedConstructor, constructor) && !consumer.process(ref)) {
+         return false;
+       }
+     }
+     else if (!usualCallsOnly && parent instanceof GrTypeElement) {
+       final GrTypeElement typeElement = (GrTypeElement)parent;
+       final PsiElement grandpa = typeElement.getParent();
+       if (grandpa instanceof GrVariableDeclaration) {
+         final GrVariable[] vars = ((GrVariableDeclaration)grandpa).getVariables();
+         if (vars.length == 1) {
+           final GrVariable variable = vars[0];
+           if (!checkListInstantiation(constructor, consumer, variable.getInitializerGroovy(), typeElement)) {
+             return false;
+           }
+         }
+       }
+       else if (grandpa instanceof GrMethod) {
+         final GrMethod method = (GrMethod)grandpa;
+         if (typeElement == method.getReturnTypeElementGroovy()) {
+           ControlFlowUtils.visitAllExitPoints(method.getBlock(), new ControlFlowUtils.ExitPointVisitor() {
+             @Override
+             public boolean visitExitPoint(Instruction instruction, @Nullable GrExpression returnValue) {
+               if (!checkListInstantiation(constructor, consumer, returnValue, typeElement)) {
+                 return false;
+               }
+               return true;
+             }
+           });
+         }
+       }
+       else if (grandpa instanceof GrTypeCastExpression) {
+         final GrTypeCastExpression cast = (GrTypeCastExpression)grandpa;
+         if (cast.getCastTypeElement() == typeElement &&
+             !checkListInstantiation(constructor, consumer, cast.getOperand(), typeElement)) {
+           return false;
+         }
+       }
+       else if (grandpa instanceof GrSafeCastExpression) {
+         final GrSafeCastExpression cast = (GrSafeCastExpression)grandpa;
+         if (cast.getCastTypeElement() == typeElement &&
+             !checkListInstantiation(constructor, consumer, cast.getOperand(), typeElement)) {
+           return false;
+         }
+       }
+     }
+     return true;
+   }
+   static GlobalSearchScope calcGppScope(Project project) {
+     final GlobalSearchScope allScope = GlobalSearchScope.allScope(project);
+     final GlobalSearchScope maximal = GlobalSearchScope.getScopeRestrictedByFileTypes(allScope, GroovyFileType.GROOVY_FILE_TYPE);
+     GlobalSearchScope gppExtensions = new DelegatingGlobalSearchScope(maximal) {
+       @Override
+       public boolean contains(VirtualFile file) {
+         return super.contains(file) && GppTypeConverter.isGppExtension(file.getExtension());
+       }
+     };
+     final PsiClass typed = JavaPsiFacade.getInstance(project).findClass(GppTypeConverter.GROOVY_LANG_TYPED, allScope);
+     if (typed != null) {
+       final Set<VirtualFile> files = new HashSet<VirtualFile>();
+       AnnotatedElementsSearch.searchElements(typed, maximal, PsiModifierListOwner.class).forEach(new Processor<PsiModifierListOwner>() {
+         @Override
+         public boolean process(PsiModifierListOwner occurrence) {
+           ContainerUtil.addIfNotNull(occurrence.getContainingFile().getVirtualFile(), files);
+           return true;
+         }
+       });
+       GlobalSearchScope withTypedAnno = GlobalSearchScope.filesScope(project, files);
+       return withTypedAnno.union(gppExtensions);
+     }
+     return gppExtensions;
+   }
+   static boolean checkListInstantiation(PsiMethod constructor,
+                                                 Processor<PsiReference> consumer,
+                                                 GrExpression expression, final GrTypeElement typeElement) {
+     if (expression instanceof GrListOrMap) {
+       final GrListOrMap list = (GrListOrMap)expression;
+       if (!list.isMap()) {
+         final PsiType expectedType = typeElement.getType();
+         if (expectedType instanceof PsiClassType) {
+           return checkListInstantiation(constructor, consumer, list, (PsiClassType)expectedType);
+         }
+       }
+     }
+     return true;
+   }
+   static boolean checkListInstantiation(PsiMethod constructor,
+                                                 Processor<PsiReference> consumer,
+                                                 GrListOrMap list,
+                                                 PsiClassType expectedType) {
+     final PsiType listType = list.getType();
+     if (listType instanceof GrTupleType) {
+       for (GroovyResolveResult candidate : PsiUtil.getConstructorCandidates(expectedType, ((GrTupleType)listType).getComponentTypes(), list)) {
+         if (constructor.getManager().areElementsEquivalent(candidate.getElement(), constructor)) {
+           if (!consumer.process(PsiReferenceBase.createSelfReference(list, TextRange.from(0, list.getTextLength()), constructor))) {
+             return false;
+           }
+         }
+       }
+     }
+     return true;
    }
 -              !consumer.process(invocation)) {
+   static boolean processConstructors(final PsiMethod constructor, final Processor<PsiReference> consumer, final PsiClass clazz,
+                                              final boolean processThisRefs) {
+     return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
+       public Boolean compute() {
+         return processClassConstructors(clazz, constructor, consumer, processThisRefs);
+       }
+     });
+   }
+   static boolean processClassConstructors(PsiClass clazz,
+                                                   PsiMethod searchedConstructor,
+                                                   Processor<PsiReference> consumer,
+                                                   boolean processThisRefs) {
+     final PsiMethod[] constructors = clazz.getConstructors();
+     if (constructors.length == 0) {
+       processImplicitConstructorCall(clazz, consumer, searchedConstructor);
+     }
+     for (PsiMethod constructor : constructors) {
+       final GrOpenBlock block = ((GrMethod)constructor).getBlock();
+       if (block != null) {
+         final GrStatement[] statements = block.getStatements();
+         if (statements.length > 0 && statements[0] instanceof GrConstructorInvocation) {
+           final GrConstructorInvocation invocation = (GrConstructorInvocation)statements[0];
+           if (invocation.isThisCall() == processThisRefs &&
+               invocation.getManager().areElementsEquivalent(invocation.resolveConstructor(), searchedConstructor) &&
++              !consumer.process(invocation.getThisOrSuperKeyword())) {
+             return false;
+           }
+         }
+         else {
+           processImplicitConstructorCall(constructor, consumer, searchedConstructor);
+         }
+       }
+     }
+     return true;
+   }
+   static void processImplicitConstructorCall(final PsiMember usage,
+                                                      final Processor<PsiReference> processor,
+                                                      final PsiMethod constructor) {
+     if (constructor instanceof GrMethod) {
+       GrParameter[] grParameters = (GrParameter[])constructor.getParameterList().getParameters();
+       if (grParameters.length > 0 && !grParameters[0].isOptional()) return;
+     }
+     else if (constructor.getParameterList().getParameters().length > 0) return;
+     PsiManager manager = constructor.getManager();
+     if (manager.areElementsEquivalent(usage, constructor) || manager.areElementsEquivalent(constructor.getContainingClass(), usage.getContainingClass())) return;
+     processor.process(new LightMemberReference(manager, usage, PsiSubstitutor.EMPTY) {
+       public PsiElement getElement() {
+         return usage;
+       }
+       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 PsiMethod) {
+           PsiIdentifier identifier = ((PsiMethod)usage).getNameIdentifier();
+           if (identifier != null) return TextRange.from(identifier.getStartOffsetInParent(), identifier.getTextLength());
+         }
+         return super.getRangeInElement();
+       }
+     });
+   }
  }