implicit toString search
authorDmitry Batkovich <dmitry.batkovich@jetbrains.com>
Mon, 27 Nov 2017 14:04:41 +0000 (17:04 +0300)
committerDmitry Batkovich <dmitry.batkovich@jetbrains.com>
Tue, 24 Apr 2018 12:35:48 +0000 (15:35 +0300)
39 files changed:
java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java
java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java
java/java-analysis-impl/src/com/intellij/find/findUsages/JavaFindUsagesHelper.java
java/java-analysis-impl/src/com/intellij/find/findUsages/JavaMethodFindUsagesOptions.java
java/java-impl/src/com/intellij/find/findUsages/FindMethodUsagesDialog.java
java/java-impl/src/com/intellij/psi/impl/search/ImplicitToStringSearcher.java [new file with mode: 0644]
java/java-indexing-api/src/com/intellij/psi/search/searches/ImplicitToStringSearch.java [new file with mode: 0644]
java/java-indexing-impl/src/META-INF/JavaIndexingPlugin.xml
java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java
java/java-indexing-impl/src/com/intellij/psi/impl/java/JavaBinaryPlusExpressionIndex.java [new file with mode: 0644]
java/java-tests/testData/compiler/compilerReferenceFindUsages/testImplicitToStringSearch/A.java [new file with mode: 0644]
java/java-tests/testData/compiler/compilerReferenceFindUsages/testImplicitToStringSearch/B.java [new file with mode: 0644]
java/java-tests/testData/compiler/compilerReferenceFindUsages/testImplicitToStringSearch/Foo.java [new file with mode: 0644]
java/java-tests/testData/compiler/compilerReferenceFindUsages/testPrimitiveToStringSearch/Foo.java [new file with mode: 0644]
java/java-tests/testData/psi/search/findUsages/implicitToString/A.java [new file with mode: 0644]
java/java-tests/testSrc/com/intellij/java/compiler/CompilerReferencesFindUsagesTest.java
java/java-tests/testSrc/com/intellij/java/psi/impl/search/JavaBinaryPlusExpressionIndexTest.kt [new file with mode: 0644]
java/java-tests/testSrc/com/intellij/java/psi/search/FindUsagesTest.java
jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java
jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacTreeRefScanner.java
jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java
jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/api/JavacNameTable.java
jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexUtil.java
jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceRegistrar.java
jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompiledFileData.java
jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompilerIndices.java
jps/jps-builders/src/org/jetbrains/jps/javac/ast/InProcessRefCollectorCompilerToolExtension.java
jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileReferencesRegistrar.java
jps/jps-builders/testData/referencesIndex/implicitToString/Foo.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/implicitToString/initialIndex.txt [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/implicitToStringHierarchy/Foo.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/implicitToStringHierarchy/initialIndex.txt [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/implicitToStringLongObject/Foo.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/implicitToStringLongObject/initialIndex.txt [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/implicitToStringPrimitives/Foo.java [new file with mode: 0644]
jps/jps-builders/testData/referencesIndex/implicitToStringPrimitives/initialIndex.txt [new file with mode: 0644]
jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt
jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt
platform/platform-resources-en/src/messages/FindBundle.properties

index c42380ef9d574962cae07eb303ebee377fbb6526..54619cc3e425fcd62d4147947c5ddd34d8d7a7af 100644 (file)
@@ -1,18 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.compiler.backwardRefs;
 
 import com.intellij.compiler.server.BuildManager;
@@ -74,6 +60,23 @@ class CompilerReferenceReader {
     return set;
   }
 
+  @Nullable
+  TIntHashSet findFileIdsWithImplicitToString(@NotNull LightRef ref) throws StorageException {
+    TIntHashSet result = new TIntHashSet();
+    myIndex.get(CompilerIndices.IMPLICIT_TO_STRING).getData(ref).forEach(
+      new ValueContainer.ContainerAction<Void>() {
+        @Override
+        public boolean perform(int id, Void value) {
+          final VirtualFile file = findFile(id);
+          if (file != null) {
+            result.add(((VirtualFileWithId)file).getId());
+          }
+          return true;
+        }
+      });
+    return result;
+  }
+
   /**
    * @return two maps of classes grouped per file
    *
index 24a340f2d678e4b661a717ac1a87703cff40d3f3..5ec4b78a3dd777fb5a51e8a3139edb75268951d2 100644 (file)
@@ -1,27 +1,13 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.compiler.backwardRefs;
 
 import com.intellij.compiler.CompilerDirectHierarchyInfo;
 import com.intellij.compiler.backwardRefs.view.CompilerReferenceFindUsagesTestInfo;
 import com.intellij.compiler.backwardRefs.view.CompilerReferenceHierarchyTestInfo;
 import com.intellij.compiler.backwardRefs.view.DirtyScopeTestInfo;
+import com.intellij.compiler.chainsSearch.ChainOpAndOccurrences;
 import com.intellij.compiler.chainsSearch.ChainSearchMagicConstants;
 import com.intellij.compiler.chainsSearch.MethodCall;
-import com.intellij.compiler.chainsSearch.ChainOpAndOccurrences;
 import com.intellij.compiler.chainsSearch.TypeCast;
 import com.intellij.compiler.chainsSearch.context.ChainCompletionContext;
 import com.intellij.compiler.server.BuildManager;
@@ -175,12 +161,29 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp
 
     try {
       return CachedValuesManager.getCachedValue(element,
-                                                () -> CachedValueProvider.Result.create(calculateScopeWithoutReferences(element),
-                                                                                        PsiModificationTracker.MODIFICATION_COUNT,
-                                                                                        this));
+                                                () -> CachedValueProvider.Result.create(buildScopeWithoutReferences(getReferentFileIds(element)),
+                                                  PsiModificationTracker.MODIFICATION_COUNT,
+                                                  this));
     }
-    catch (RuntimeException e) {
-      return onException(e, "scope without code references");
+    catch (RuntimeException e1) {
+      return onException(e1, "scope without code references");
+    }
+  }
+
+  @Nullable
+  @Override
+  public GlobalSearchScope getScopeWithoutImplicitToStringCodeReferences(@NotNull PsiElement aClass) {
+    if (!isServiceEnabledFor(aClass)) return null;
+
+    try {
+      return CachedValuesManager.getCachedValue(aClass,
+                                                () -> CachedValueProvider.Result.create(
+                                                  buildScopeWithoutReferences(getReferentFileIdsViaImplicitToString(aClass)),
+                                                  PsiModificationTracker.MODIFICATION_COUNT,
+                                                  this));
+    }
+    catch (RuntimeException e1) {
+      return onException(e1, "scope without implicit toString references");
     }
   }
 
@@ -532,8 +535,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp
   }
 
   @Nullable
-  private GlobalSearchScope calculateScopeWithoutReferences(@NotNull PsiElement element) {
-    TIntHashSet referentFileIds = getReferentFileIds(element);
+  private GlobalSearchScope buildScopeWithoutReferences(@Nullable TIntHashSet referentFileIds) {
     if (referentFileIds == null) return null;
 
     return getScopeRestrictedByFileTypes(new ScopeWithoutReferencesOnCompilation(referentFileIds, myProjectFileIndex).intersectWith(notScope(
@@ -543,7 +545,19 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp
 
   @Nullable
   private TIntHashSet getReferentFileIds(@NotNull PsiElement element) {
-    final CompilerElementInfo compilerElementInfo = asCompilerElements(element, true, true);
+    return getReferentFileIds(element, true, (ref, elementPlace) -> myReader.findReferentFileIds(ref, elementPlace == ElementPlace.SRC));
+  }
+
+  @Nullable
+  private TIntHashSet getReferentFileIdsViaImplicitToString(@NotNull PsiElement element) {
+    return getReferentFileIds(element, false, (ref, elementPlace) -> myReader.findFileIdsWithImplicitToString(ref));
+  }
+
+  @Nullable
+  private TIntHashSet getReferentFileIds(@NotNull PsiElement element,
+                                         boolean buildHierarchyForLibraryElements,
+                                         @NotNull ReferentFileSearcher referentFileSearcher) {
+    final CompilerElementInfo compilerElementInfo = asCompilerElements(element, buildHierarchyForLibraryElements, true);
     if (compilerElementInfo == null) return null;
 
     myReadDataLock.lock();
@@ -552,14 +566,14 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp
       TIntHashSet referentFileIds = new TIntHashSet();
       for (LightRef ref : compilerElementInfo.searchElements) {
         try {
-          final TIntHashSet referents = myReader.findReferentFileIds(ref, compilerElementInfo.place == ElementPlace.SRC);
+          final TIntHashSet referents = referentFileSearcher.findReferentFiles(ref, compilerElementInfo.place);
           if (referents == null) return null;
           referentFileIds.addAll(referents.toArray());
         }
         catch (StorageException e) {
           throw new RuntimeException(e);
         }
-         }
+      }
       return referentFileIds;
 
     } finally {
@@ -567,6 +581,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp
     }
   }
 
+
   @Nullable
   private CompilerElementInfo asCompilerElements(@NotNull PsiElement psiElement,
                                                  boolean buildHierarchyForLibraryElements,
@@ -861,4 +876,10 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp
     COMPILATION_FINISHED,
     UP_TO_DATE_CACHE
   }
+
+  @FunctionalInterface
+  private interface ReferentFileSearcher {
+    @Nullable
+    TIntHashSet findReferentFiles(@NotNull LightRef ref, @NotNull ElementPlace place) throws StorageException;
+  }
 }
index ea584efd7932a5ee3f4bd74db0eb6d71fbfb5de6..3f8b096bf638ce955e5f9e44303d5ebee8af36bc 100644 (file)
@@ -1,18 +1,4 @@
-/*
- * Copyright 2000-2015 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.find.findUsages;
 
 import com.intellij.find.FindBundle;
@@ -197,6 +183,9 @@ public class JavaFindUsagesHelper {
         FunctionalExpressionSearch.search(psiMethod, methodOptions.searchScope).forEach(new PsiElementProcessorAdapter<>(
           expression -> addResult(expression, options, processor)));
       }
+      if (methodOptions.isImplicitToString) {
+        ImplicitToStringSearch.search(psiMethod, methodOptions.searchScope).forEach(new PsiElementProcessorAdapter<>(ref -> addResult(ref, options, processor)));
+      }
     }
 
     if (element instanceof PomTarget) {
index 87970138d70a25917bd2c177d11f2b0962fabf4d..adaf74a7cf0725470e704296ec3baf23449d4ae9 100644 (file)
@@ -1,18 +1,4 @@
-/*
- * Copyright 2000-2016 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.find.findUsages;
 
 import com.intellij.find.FindBundle;
@@ -31,6 +17,7 @@ public class JavaMethodFindUsagesOptions extends JavaFindUsagesOptions {
   public boolean isCheckDeepInheritance = true;
   public boolean isIncludeInherited;
   public boolean isIncludeOverloadUsages;
+  public boolean isImplicitToString = true;
 
   public JavaMethodFindUsagesOptions(@NotNull Project project) {
     super(project);
@@ -54,6 +41,7 @@ public class JavaMethodFindUsagesOptions extends JavaFindUsagesOptions {
     if (isIncludeInherited != that.isIncludeInherited) return false;
     if (isIncludeOverloadUsages != that.isIncludeOverloadUsages) return false;
     if (isOverridingMethods != that.isOverridingMethods) return false;
+    if (isImplicitToString != that.isImplicitToString) return false;
 
     return true;
   }
@@ -65,6 +53,7 @@ public class JavaMethodFindUsagesOptions extends JavaFindUsagesOptions {
     result = 31 * result + (isCheckDeepInheritance ? 1 : 0);
     result = 31 * result + (isIncludeInherited ? 1 : 0);
     result = 31 * result + (isIncludeOverloadUsages ? 1 : 0);
+    result = 31 * result + (isImplicitToString ? 1 : 0);
     return result;
   }
 
@@ -80,6 +69,9 @@ public class JavaMethodFindUsagesOptions extends JavaFindUsagesOptions {
     if (isOverridingMethods) {
       strings.add(FindBundle.message("find.usages.panel.title.overriding.methods"));
     }
+    if (isImplicitToString) {
+      strings.add(FindBundle.message("find.usages.panel.title.implicit.to.string.calls"));
+    }
 
   }
 }
index d73c075cab65cec69a82fc5e800935bebf3fb5b4..8080fb45d01cdabc252c6521fcbb5be42462daf7 100644 (file)
@@ -1,23 +1,10 @@
-/*
- * Copyright 2000-2009 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.find.findUsages;
 
 import com.intellij.find.FindBundle;
 import com.intellij.openapi.project.Project;
 import com.intellij.psi.*;
+import com.intellij.psi.impl.search.ImplicitToStringSearcher;
 import com.intellij.ui.IdeBorderFactory;
 import com.intellij.ui.StateRestoringCheckBox;
 import org.jetbrains.annotations.Nullable;
@@ -28,6 +15,7 @@ public class FindMethodUsagesDialog extends JavaFindUsagesDialog<JavaMethodFindU
   private StateRestoringCheckBox myCbUsages;
   private StateRestoringCheckBox myCbImplementingMethods;
   private StateRestoringCheckBox myCbOverridingMethods;
+  private StateRestoringCheckBox myCbImplicitToString;
   private boolean myHasFindWhatPanel;
 
   public FindMethodUsagesDialog(PsiElement element, Project project, FindUsagesOptions findUsagesOptions, boolean toShowInNewTab, boolean mustOpenInNewTab,
@@ -53,6 +41,9 @@ public class FindMethodUsagesDialog extends JavaFindUsagesDialog<JavaMethodFindU
     if (isToChange(myCbImplementingMethods)) {
       options.isImplementingMethods = isSelected(myCbImplementingMethods);
     }
+    if (isToChange(myCbImplicitToString)) {
+      options.isImplicitToString = isSelected(myCbImplicitToString);
+    }
     options.isCheckDeepInheritance = true;
   }
 
@@ -66,22 +57,33 @@ public class FindMethodUsagesDialog extends JavaFindUsagesDialog<JavaMethodFindU
 
     PsiMethod method = (PsiMethod) getPsiElement();
     PsiClass aClass = method.getContainingClass();
-    if (!method.isConstructor() &&
-            !method.hasModifierProperty(PsiModifier.STATIC) &&
-            !method.hasModifierProperty(PsiModifier.FINAL) &&
-            !method.hasModifierProperty(PsiModifier.PRIVATE) &&
-            aClass != null &&
-            !(aClass instanceof PsiAnonymousClass) &&
-            !aClass.hasModifierProperty(PsiModifier.FINAL)) {
-      if (method.hasModifierProperty(PsiModifier.ABSTRACT)) {
-        myCbImplementingMethods = addCheckboxToPanel(FindBundle.message("find.what.implementing.methods.checkbox"), getFindUsagesOptions().isImplementingMethods, findWhatPanel, true);
-      } else {
-        myCbOverridingMethods = addCheckboxToPanel(FindBundle.message("find.what.overriding.methods.checkbox"), getFindUsagesOptions().isOverridingMethods, findWhatPanel, true);
-      }
-    } else {
+    if (method.isConstructor() ||
+        method.hasModifierProperty(PsiModifier.STATIC) ||
+        method.hasModifierProperty(PsiModifier.FINAL) ||
+        method.hasModifierProperty(PsiModifier.PRIVATE) ||
+        aClass == null ||
+        aClass instanceof PsiAnonymousClass ||
+        aClass.hasModifierProperty(PsiModifier.FINAL)) {
       myHasFindWhatPanel = false;
-      return null; 
+      return null;
+    }
+
+    if (method.hasModifierProperty(PsiModifier.ABSTRACT)) {
+      myCbImplementingMethods =
+        addCheckboxToPanel(FindBundle.message("find.what.implementing.methods.checkbox"), getFindUsagesOptions().isImplementingMethods,
+                           findWhatPanel, true);
+    }
+    else {
+      myCbOverridingMethods =
+        addCheckboxToPanel(FindBundle.message("find.what.overriding.methods.checkbox"), getFindUsagesOptions().isOverridingMethods,
+                           findWhatPanel, true);
     }
+    if (ImplicitToStringSearcher.isToStringMethod(method)) {
+      myCbImplicitToString =
+        addCheckboxToPanel(FindBundle.message("find.what.implicit.to.string.checkbox"), getFindUsagesOptions().isImplicitToString,
+                           findWhatPanel, true);
+    }
+
     myHasFindWhatPanel = true;
     return findWhatPanel;
 
@@ -106,8 +108,10 @@ public class FindMethodUsagesDialog extends JavaFindUsagesDialog<JavaMethodFindU
     if (!myHasFindWhatPanel) {
       setOKActionEnabled(true);
     } else {
-
-      boolean hasSelected = isSelected(myCbUsages) || isSelected(myCbImplementingMethods) || isSelected(myCbOverridingMethods);
+      boolean hasSelected = isSelected(myCbUsages) ||
+                            isSelected(myCbImplementingMethods) ||
+                            isSelected(myCbOverridingMethods) ||
+                            isSelected(myCbImplicitToString);
       setOKActionEnabled(hasSelected);
     }
   }
diff --git a/java/java-impl/src/com/intellij/psi/impl/search/ImplicitToStringSearcher.java b/java/java-impl/src/com/intellij/psi/impl/search/ImplicitToStringSearcher.java
new file mode 100644 (file)
index 0000000..5a876fa
--- /dev/null
@@ -0,0 +1,200 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.psi.impl.search;
+
+import com.intellij.compiler.CompilerReferenceService;
+import com.intellij.openapi.application.QueryExecutorBase;
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.DumbService;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.java.JavaBinaryPlusExpressionIndex;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.GlobalSearchScopeUtil;
+import com.intellij.psi.search.searches.ImplicitToStringSearch;
+import com.intellij.psi.util.MethodSignatureUtil;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.util.PsiUtil;
+import com.intellij.psi.util.PsiUtilCore;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.ObjectUtils;
+import com.intellij.util.Processor;
+import com.intellij.util.indexing.FileBasedIndex;
+import gnu.trove.THashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Map;
+
+public class ImplicitToStringSearcher extends QueryExecutorBase<PsiExpression, ImplicitToStringSearch.SearchParameters> {
+  private static final Logger LOG = Logger.getInstance(ImplicitToStringSearcher.class);
+
+  @Override
+  public void processQuery(@NotNull ImplicitToStringSearch.SearchParameters parameters, @NotNull Processor<PsiExpression> consumer) {
+    PsiMethod targetMethod = parameters.getTargetMethod();
+    Project project = PsiUtilCore.getProjectInReadAction(targetMethod);
+    if (project == null) return;
+    PsiClass aClass = ReadAction.compute(() -> targetMethod.getContainingClass());
+    if (aClass == null) return;
+    DumbService dumbService = DumbService.getInstance(project);
+    Map<VirtualFile, int[]> fileOffsets = new THashMap<>();
+    dumbService.runReadActionInSmartMode(() -> {
+      CompilerReferenceService compilerReferenceService = CompilerReferenceService.getInstance(project);
+      GlobalSearchScope scopeWithoutToString = compilerReferenceService == null ? null : compilerReferenceService.getScopeWithoutImplicitToStringCodeReferences(aClass);
+      GlobalSearchScope filter = GlobalSearchScopeUtil.toGlobalSearchScope(scopeWithoutToString == null
+                                                                           ? parameters.getSearchScope()
+                                                                           : GlobalSearchScope.notScope(scopeWithoutToString).intersectWith(parameters.getSearchScope()), project);
+      FileBasedIndex.getInstance().processValues(JavaBinaryPlusExpressionIndex.INDEX_ID, Boolean.TRUE, null,
+                                                 (file, value) -> {
+                                                   ProgressManager.checkCanceled();
+                                                   fileOffsets.put(file, value.getOffsets());
+                                                   return true;
+                                                 }, filter);
+
+    });
+
+    PsiManager psiManager = PsiManager.getInstance(project);
+    for (Map.Entry<VirtualFile, int[]> entry : fileOffsets.entrySet()) {
+      VirtualFile file = entry.getKey();
+      int[] offsets = entry.getValue();
+      ProgressManager.checkCanceled();
+      if (!processFile(file, offsets, psiManager, targetMethod, consumer)) {
+        return;
+      }
+    }
+  }
+
+  public static boolean isToStringMethod(@NotNull PsiElement element) {
+    if (!(element instanceof PsiMethod)) {
+      return false;
+    }
+    PsiMethod method = (PsiMethod)element;
+    if (!"toString".equals(method.getName())) {
+      return false;
+    }
+    if (method.getParameters().length != 0) {
+      return false;
+    }
+    PsiType returnType = method.getReturnType();
+    return returnType != null && returnType.equalsToText(CommonClassNames.JAVA_LANG_STRING);
+  }
+
+  private static boolean processFile(VirtualFile file,
+                                     int[] offsets,
+                                     PsiManager manager,
+                                     PsiMethod targetMethod,
+                                     Processor<PsiExpression> consumer) {
+    return ReadAction.compute(() -> {
+      PsiFile psiFile = ObjectUtils.notNull(manager.findFile(file));
+      if (!(psiFile instanceof PsiJavaFile)) {
+        LOG.error("Non-java file " + psiFile + "; " + file);
+        return true;
+      }
+
+      for (int offset : offsets) {
+        PsiJavaToken plusToken = PsiTreeUtil.findElementOfClassAtOffset(psiFile, offset, PsiJavaToken.class, false);
+        if (plusToken == null) {
+          LOG.error("plusToken shouldn't be null in " + psiFile + " at " + offset);
+          continue;
+        }
+        PsiElement parent = plusToken.getParent();
+
+        if (parent instanceof PsiPolyadicExpression) {
+          PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)parent;
+          PsiType exprType = polyadicExpression.getType();
+          if (exprType == null || !exprType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
+            continue;
+          }
+          PsiExpression[] operands = polyadicExpression.getOperands();
+          for (PsiExpression operand : operands) {
+            if (!processPolyadicExprOperand(operand, consumer, targetMethod)) {
+              return false;
+            }
+          }
+        } else {
+          LOG.error(parent + " expected to be polyadic expression");
+        }
+      }
+      return true;
+    });
+  }
+
+  private static boolean processPolyadicExprOperand(@NotNull PsiExpression expr,
+                                                    @NotNull Processor<PsiExpression> consumer,
+                                                    @NotNull PsiMethod targetMethod) {
+    PsiType type = expr.getType();
+    if (type instanceof PsiPrimitiveType) {
+      type = ((PsiPrimitiveType)type).getBoxedType(expr);
+    }
+    PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(type);
+    if (aClass != null && !CommonClassNames.JAVA_LANG_STRING.equals(aClass.getQualifiedName())) {
+      PsiMethod implicitlyUsedMethod = aClass.findMethodBySignature(targetMethod, true);
+      if (implicitlyUsedMethod != null && (targetMethod == implicitlyUsedMethod || MethodSignatureUtil.isSuperMethod(targetMethod, implicitlyUsedMethod))) {
+        return consumer.process(expr);
+      }
+    }
+    return true;
+  }
+
+  private static class MyImplicitToStringReference implements PsiReference {
+    private final PsiElement myPlace;
+    private final PsiMethod myTargetMethod;
+
+    private MyImplicitToStringReference(PsiElement place, PsiMethod method) {
+      myPlace = place;
+      myTargetMethod = method;
+    }
+
+    @Override
+    public PsiElement getElement() {
+      return myPlace;
+    }
+
+    @Override
+    public TextRange getRangeInElement() {
+      return new TextRange(0, myPlace.getTextLength());
+    }
+
+    @Nullable
+    @Override
+    public PsiElement resolve() {
+      return myTargetMethod;
+    }
+
+    @NotNull
+    @Override
+    public String getCanonicalText() {
+      return myPlace.getText();
+    }
+
+    @Override
+    public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
+      return null;
+    }
+
+    @Override
+    public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
+      return null;
+    }
+
+    @Override
+    public boolean isReferenceTo(PsiElement element) {
+      return element == myTargetMethod;
+    }
+
+    @NotNull
+    @Override
+    public Object[] getVariants() {
+      return ArrayUtil.EMPTY_OBJECT_ARRAY;
+    }
+
+    @Override
+    public boolean isSoft() {
+      return false;
+    }
+  }
+}
diff --git a/java/java-indexing-api/src/com/intellij/psi/search/searches/ImplicitToStringSearch.java b/java/java-indexing-api/src/com/intellij/psi/search/searches/ImplicitToStringSearch.java
new file mode 100644 (file)
index 0000000..51a5295
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.psi.search.searches;
+
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.search.SearchScope;
+import com.intellij.util.Query;
+import org.jetbrains.annotations.NotNull;
+
+public class ImplicitToStringSearch extends ExtensibleQueryFactory<PsiExpression, ImplicitToStringSearch.SearchParameters> {
+  public static final ImplicitToStringSearch INSTANCE = new ImplicitToStringSearch();
+
+  public static class SearchParameters {
+    private final PsiMethod myTargetMethod;
+    @NotNull
+    private final SearchScope myScope;
+
+    public SearchParameters(@NotNull PsiMethod targetMethod, @NotNull SearchScope scope) {
+      myTargetMethod = targetMethod;
+      myScope = scope;
+    }
+
+    @NotNull
+    public PsiMethod getTargetMethod() {
+      return myTargetMethod;
+    }
+
+    @NotNull
+    public SearchScope getSearchScope() {
+      return myScope;
+    }
+  }
+
+  public static Query<PsiExpression> search(@NotNull PsiMethod targetMethod, @NotNull SearchScope scope) {
+    return INSTANCE.createUniqueResultsQuery(new SearchParameters(targetMethod, scope));
+  }
+}
index caab29e875e5df8ff1c8a0abce2d8e317e711963..39d9e3557e34c1043b6f179a97809f7692d0597c 100644 (file)
@@ -27,6 +27,7 @@
     <extensionPoint qualifiedName="com.intellij.annotatedElementsSearch" interface="com.intellij.util.QueryExecutor"/>
     <extensionPoint qualifiedName="com.intellij.annotatedPackagesSearch" interface="com.intellij.util.QueryExecutor"/>
     <extensionPoint qualifiedName="com.intellij.functionalExpressionSearch" interface="com.intellij.util.QueryExecutor"/>
+    <extensionPoint qualifiedName="com.intellij.implicitToStringSearch" interface="com.intellij.util.QueryExecutor"/>
     <extensionPoint qualifiedName="com.intellij.customPropertyScopeProvider" interface="com.intellij.psi.impl.search.CustomPropertyScopeProvider"/>
   </extensionPoints>
 
@@ -61,6 +62,7 @@
                     serviceImplementation="com.intellij.psi.impl.search.HighlightingCaches"/>
     <projectService serviceInterface="com.intellij.psi.impl.file.impl.JavaFileManager"
                     serviceImplementation="com.intellij.psi.impl.file.impl.JavaFileManagerImpl"/>
+    <implicitToStringSearch implementation="com.intellij.psi.impl.search.ImplicitToStringSearcher"/>
     <functionalExpressionSearch implementation="com.intellij.psi.impl.search.JavaFunctionalExpressionSearcher"/>
     <definitionsScopedSearch implementation="com.intellij.codeInsight.navigation.ClassImplementationsSearch"/>
     <definitionsScopedSearch implementation="com.intellij.codeInsight.navigation.MethodImplementationsSearch"/>
@@ -80,5 +82,6 @@
     <referencesSearch implementation="com.intellij.psi.impl.search.SPIReferencesSearcher"/>
     <referencesSearch implementation="com.intellij.psi.impl.search.ConstructorReferencesSearcher"/>
     <referencesSearch implementation="com.intellij.psi.impl.search.PsiAnnotationMethodReferencesSearcher"/>
+    <fileBasedIndex implementation="com.intellij.psi.impl.java.JavaBinaryPlusExpressionIndex"/>
   </extensions>
 </idea-plugin>
\ No newline at end of file
index c72d643f0d457e6f95f3bcb1cf3f0667496a3bec..e43c98f25603b9bd05d4a96d7bed6ed2bcd5fdf5 100644 (file)
@@ -1,18 +1,4 @@
-/*
- * Copyright 2000-2016 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.compiler;
 
 import com.intellij.openapi.components.AbstractProjectComponent;
@@ -49,6 +35,9 @@ public abstract class CompilerReferenceService extends AbstractProjectComponent
   @Nullable
   public abstract GlobalSearchScope getScopeWithoutCodeReferences(@NotNull PsiElement element);
 
+  @Nullable
+  public abstract GlobalSearchScope getScopeWithoutImplicitToStringCodeReferences(@NotNull PsiElement aClass);
+
   /**
    * @return a hierarchy of direct inheritors built on compilation time.
    * This hierarchy is restricted by searchFileType and searchScope.
diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/java/JavaBinaryPlusExpressionIndex.java b/java/java-indexing-impl/src/com/intellij/psi/impl/java/JavaBinaryPlusExpressionIndex.java
new file mode 100644 (file)
index 0000000..a1c9ad0
--- /dev/null
@@ -0,0 +1,150 @@
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.psi.impl.java;
+
+import com.intellij.ide.highlighter.JavaFileType;
+import com.intellij.lang.LighterAST;
+import com.intellij.lang.LighterASTNode;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.impl.source.JavaFileElementType;
+import com.intellij.psi.impl.source.tree.ElementType;
+import com.intellij.psi.impl.source.tree.JavaElementType;
+import com.intellij.psi.impl.source.tree.LightTreeUtil;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.indexing.*;
+import com.intellij.util.io.BooleanDataDescriptor;
+import com.intellij.util.io.DataExternalizer;
+import com.intellij.util.io.DataInputOutputUtil;
+import com.intellij.util.io.KeyDescriptor;
+import com.intellij.util.text.StringSearcher;
+import gnu.trove.THashMap;
+import gnu.trove.TIntArrayList;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+
+import static com.intellij.psi.impl.source.tree.JavaElementType.BINARY_EXPRESSION;
+import static com.intellij.psi.impl.source.tree.JavaElementType.POLYADIC_EXPRESSION;
+
+public class JavaBinaryPlusExpressionIndex extends FileBasedIndexExtension<Boolean, JavaBinaryPlusExpressionIndex.PlusOffsets> implements PsiDependentIndex {
+  public static final ID<Boolean, PlusOffsets> INDEX_ID = ID.create("java.binary.plus.expression");
+
+  @NotNull
+  @Override
+  public ID<Boolean, PlusOffsets> getName() {
+    return INDEX_ID;
+  }
+
+  @NotNull
+  @Override
+  public DataIndexer<Boolean, PlusOffsets, FileContent> getIndexer() {
+    return inputData -> {
+      CharSequence text = inputData.getContentAsText();
+      int[] offsets = new StringSearcher("+", true, true).findAllOccurrences(text);
+      if (offsets.length == 0) return Collections.emptyMap();
+
+      LighterAST tree = ((FileContentImpl)inputData).getLighterASTForPsiDependentIndex();
+      TIntArrayList result = new TIntArrayList();
+      for (int offset : offsets) {
+        LighterASTNode leaf = LightTreeUtil.findLeafElementAt(tree, offset);
+        LighterASTNode element = leaf == null ? null : tree.getParent(leaf);
+        if (element == null) continue;
+
+        if ((element.getTokenType() == BINARY_EXPRESSION || element.getTokenType() == POLYADIC_EXPRESSION) && !isStringConcatenation(element, tree)) {
+          result.add(offset);
+        }
+      }
+      THashMap<Boolean, PlusOffsets> resultMap = ContainerUtil.newTroveMap();
+      resultMap.put(Boolean.TRUE, new PlusOffsets(result.toNativeArray()));
+      return resultMap;
+    };
+  }
+
+  @NotNull
+  @Override
+  public KeyDescriptor<Boolean> getKeyDescriptor() {
+    return BooleanDataDescriptor.INSTANCE;
+  }
+
+  @NotNull
+  @Override
+  public DataExternalizer<PlusOffsets> getValueExternalizer() {
+    return new DataExternalizer<PlusOffsets>() {
+      @Override
+      public void save(@NotNull DataOutput out, PlusOffsets value) throws IOException {
+        int[] offsets = value.getOffsets();
+        DataInputOutputUtil.writeINT(out, offsets.length);
+        for (int i : offsets) {
+          DataInputOutputUtil.writeINT(out, i);
+        }
+      }
+
+      @Override
+      public PlusOffsets read(@NotNull DataInput in) throws IOException {
+        int[] result = new int[DataInputOutputUtil.readINT(in)];
+        for (int i = 0; i < result.length; i++) {
+          result[i] = DataInputOutputUtil.readINT(in);
+        }
+        return new PlusOffsets(result);
+      }
+    };
+  }
+
+  @Override
+  public int getVersion() {
+    return 1;
+  }
+
+  @NotNull
+  @Override
+  public FileBasedIndex.InputFilter getInputFilter() {
+    return new DefaultFileTypeSpecificInputFilter(JavaFileType.INSTANCE) {
+      @Override
+      public boolean acceptInput(@NotNull VirtualFile file) {
+        return super.acceptInput(file) && JavaFileElementType.isInSourceContent(file);
+      }
+    };
+  }
+
+  @Override
+  public boolean dependsOnFileContent() {
+    return true;
+  }
+
+  public static class PlusOffsets {
+    private final int[] offsets;
+
+    public PlusOffsets(int[] offsets) {this.offsets = offsets;}
+
+    public int[] getOffsets() {
+      return offsets;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      PlusOffsets offsets1 = (PlusOffsets)o;
+
+      if (!Arrays.equals(offsets, offsets1.offsets)) return false;
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      return Arrays.hashCode(offsets);
+    }
+  }
+
+  private static boolean isStringConcatenation(@NotNull LighterASTNode concatExpr, @NotNull LighterAST tree) {
+    return LightTreeUtil
+      .getChildrenOfType(tree, concatExpr, ElementType.EXPRESSION_BIT_SET)
+      .stream()
+      .allMatch(e -> e.getTokenType() == JavaElementType.LITERAL_EXPRESSION);
+  }
+}
diff --git a/java/java-tests/testData/compiler/compilerReferenceFindUsages/testImplicitToStringSearch/A.java b/java/java-tests/testData/compiler/compilerReferenceFindUsages/testImplicitToStringSearch/A.java
new file mode 100644 (file)
index 0000000..36cef21
--- /dev/null
@@ -0,0 +1,5 @@
+class A {
+  void m(FooImpl f) {
+    String s = "f = " + f.toString();
+  }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/compiler/compilerReferenceFindUsages/testImplicitToStringSearch/B.java b/java/java-tests/testData/compiler/compilerReferenceFindUsages/testImplicitToStringSearch/B.java
new file mode 100644 (file)
index 0000000..6c3be49
--- /dev/null
@@ -0,0 +1,5 @@
+class B {
+  void m(FooImpl f) {
+    String s = "f = " + f;
+  }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/compiler/compilerReferenceFindUsages/testImplicitToStringSearch/Foo.java b/java/java-tests/testData/compiler/compilerReferenceFindUsages/testImplicitToStringSearch/Foo.java
new file mode 100644 (file)
index 0000000..26a7cb4
--- /dev/null
@@ -0,0 +1,13 @@
+class FooImpl extends Foo {
+  @Override
+  public String toString() {
+    return "FooImpl instance";
+  }
+}
+
+class Foo {
+  @Override
+  public String toString() {
+    return "Foo instance";
+  }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/compiler/compilerReferenceFindUsages/testPrimitiveToStringSearch/Foo.java b/java/java-tests/testData/compiler/compilerReferenceFindUsages/testPrimitiveToStringSearch/Foo.java
new file mode 100644 (file)
index 0000000..2a692b2
--- /dev/null
@@ -0,0 +1,5 @@
+public class Foo {
+  public void bar(long l) {
+    System.out.println("xxx " + l + " yyy");
+  }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/psi/search/findUsages/implicitToString/A.java b/java/java-tests/testData/psi/search/findUsages/implicitToString/A.java
new file mode 100644 (file)
index 0000000..9121ad1
--- /dev/null
@@ -0,0 +1,11 @@
+class Foo {
+
+  public static void main(String[] args) {
+    System.out.println(new Foo() + "");
+  }
+
+  @Override
+  public String toString() {
+    return "Foo";
+  }
+}
\ No newline at end of file
index 475a895d0910342f88381f115c55fb5d20fbbc1c..c24153214d8b5976e0bf5b78364757e3afeaec97 100644 (file)
@@ -1,24 +1,11 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.java.compiler;
 
 import com.intellij.JavaTestUtil;
 import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
 import com.intellij.compiler.CompilerConfiguration;
 import com.intellij.compiler.CompilerReferenceService;
+import com.intellij.find.findUsages.JavaFindUsagesHandlerFactory;
 import com.intellij.lang.injection.InjectedLanguageManager;
 import com.intellij.openapi.compiler.options.ExcludeEntryDescription;
 import com.intellij.openapi.compiler.options.ExcludesConfiguration;
@@ -172,6 +159,17 @@ public class CompilerReferencesFindUsagesTest extends DaemonAnalyzerTestCase {
     Arrays.stream(findClass("Foo").findMethodsByName("bar", false)).forEach((m) -> assertSize(2, searchReferences(m)));
   }
 
+  public void testImplicitToStringSearch() {
+    configureByFiles(getName(), getName() + "/Foo.java", getName() + "/A.java", getName() + "/B.java");
+    Arrays.stream(findClass("FooImpl").findMethodsByName("toString", false)).forEach((m) -> assertEquals(2, searchUsages(m)));
+    Arrays.stream(findClass("Foo").findMethodsByName("toString", false)).forEach((m) -> assertEquals(2, searchUsages(m)));
+    Arrays.stream(myJavaFacade.findClass(CommonClassNames.JAVA_LANG_OBJECT).findMethodsByName("toString", false)).forEach((m) -> assertEquals(2, searchUsages(m)));
+    myCompilerTester.rebuild();
+    Arrays.stream(findClass("FooImpl").findMethodsByName("toString", false)).forEach((m) -> assertEquals(2, searchUsages(m)));
+    Arrays.stream(findClass("Foo").findMethodsByName("toString", false)).forEach((m) -> assertEquals(2, searchUsages(m)));
+    Arrays.stream(myJavaFacade.findClass(CommonClassNames.JAVA_LANG_OBJECT).findMethodsByName("toString", false)).forEach((m) -> assertEquals(2, searchUsages(m)));
+  }
+
   private void doTestRunnableFindUsagesWithExcludesConfiguration(@NotNull Consumer<ExcludesConfiguration> excludesConfigurationPatcher,
                                                                  int expectedUsagesCount,
                                                                  String... testFiles) {
@@ -187,6 +185,16 @@ public class CompilerReferencesFindUsagesTest extends DaemonAnalyzerTestCase {
     }
   }
 
+  private int searchUsages(@NotNull PsiMethod method) {
+    JavaFindUsagesHandlerFactory factory = JavaFindUsagesHandlerFactory.getInstance(getProject());
+    int[] count = {0};
+    factory.createFindUsagesHandler(method, false).processElementUsages(method, info -> {
+      count[0]++;
+      return true;
+    }, factory.getFindMethodOptions());
+    return count[0];
+  }
+
   private Collection<PsiReference> searchReferences(@NotNull PsiElement element) {
     if (element instanceof PsiMethod) {
       return MethodReferencesSearch.search((PsiMethod)element, GlobalSearchScope.projectScope(getProject()), false).findAll();
diff --git a/java/java-tests/testSrc/com/intellij/java/psi/impl/search/JavaBinaryPlusExpressionIndexTest.kt b/java/java-tests/testSrc/com/intellij/java/psi/impl/search/JavaBinaryPlusExpressionIndexTest.kt
new file mode 100644 (file)
index 0000000..3ef291b
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.java.psi.impl.search
+
+import com.intellij.openapi.fileTypes.StdFileTypes
+import com.intellij.psi.impl.java.JavaBinaryPlusExpressionIndex
+import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase
+import com.intellij.util.indexing.FileContentImpl
+import com.intellij.util.indexing.IndexingDataKeys
+import org.intellij.lang.annotations.Language
+
+class JavaBinaryPlusExpressionIndexTest : LightPlatformCodeInsightFixtureTestCase() {
+  fun testIndex() {
+    @Language("JAVA")
+    val file = myFixture.configureByText(StdFileTypes.JAVA, """
+            package org.some;
+
+            class Xyz {
+
+                void someMethod(Long o, String o2) {
+                  String s = "qwe" + "asd";
+                  String s1 = "qwe" + o;
+                  String s2 = "qwe" + o2 + "xxx";
+                }
+            }
+    """).virtualFile
+    val content = FileContentImpl.createByFile(file)
+    content.putUserData(IndexingDataKeys.PROJECT, project)
+    val data = JavaBinaryPlusExpressionIndex().indexer.map(content).entries.first().value.offsets!!
+
+    assertEquals(3, data.size)
+    assertEquals(190, data[0])
+    assertEquals(231, data[1])
+    assertEquals(236, data[2])
+  }
+}
index 48996d3be201224dd563b4be4c560d36201ef935..5cc803583fda2756401f69e53b3551b496554a41 100644 (file)
@@ -1,18 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.java.psi.search;
 
 import com.intellij.JavaTestUtil;
@@ -192,6 +178,20 @@ public class FindUsagesTest extends PsiTestCase{
     }
   }
 
+  public void testImplicitToString() {
+    PsiClass aClass = myJavaFacade.findClass("Foo");
+    assertNotNull(aClass);
+    PsiMethod toString = assertOneElement(aClass.findMethodsByName("toString", false));
+
+    JavaFindUsagesHandlerFactory factory = JavaFindUsagesHandlerFactory.getInstance(myProject);
+    int[] count = {0};
+    factory.createFindUsagesHandler(toString, false).processElementUsages(toString, info -> {
+      count[0]++;
+      return true;
+    }, factory.getFindMethodOptions());
+    assertEquals(1, count[0]);
+  }
+
   public static void doTest(PsiElement element, String[] fileNames, int[] starts, int[] ends) {
     final ArrayList<PsiFile> filesList = new ArrayList<>();
     final IntArrayList startsList = new IntArrayList();
index 3f458b15f24789e70537892e4aa08ee4674aa016..de250d5c747549c83c916a28ab12f997557e5a2b 100644 (file)
@@ -1,12 +1,11 @@
-// Copyright 2000-2017 JetBrains s.r.o.
-// Use of this source code is governed by the Apache 2.0 license that can be
-// found in the LICENSE file.
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package org.jetbrains.jps.javac.ast;
 
 import com.intellij.util.Consumer;
 import com.sun.source.tree.*;
 import com.sun.source.util.*;
 import com.sun.tools.javac.util.ClientCodeException;
+import gnu.trove.THashSet;
 import gnu.trove.TObjectIntHashMap;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.jps.javac.ast.api.*;
@@ -216,7 +215,8 @@ final class JavacReferenceCollectorListener implements TaskListener {
                                      createReferenceHolder(),
                                      myDivideImportRefs ? createReferenceHolder() : EMPTY_T_OBJ_INT_MAP,
                                      new ArrayList<JavacTypeCast>(),
-                                     createDefinitionHolder());
+                                     createDefinitionHolder(),
+                                     new THashSet<JavacRef>());
       myTreeHelper = new JavacTreeHelper(unitTree, myTreeUtility);
     }
 
@@ -228,6 +228,12 @@ final class JavacReferenceCollectorListener implements TaskListener {
      myFileData.getDefs().add(def);
     }
 
+    void sinkImplicitToString(@Nullable JavacRef ref) {
+      if (ref != null) {
+        myFileData.getImplicitToStringRefs().add(ref);
+      }
+    }
+
     public void sinkTypeCast(JavacTypeCast typeCast) {
       myFileData.getCasts().add(typeCast);
     }
index b621691eafa4986e21d59b88d2ccba4b8449fe16..9526c381567dba7b08637eebb45bc6e95be2d165 100644 (file)
@@ -1,33 +1,18 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package org.jetbrains.jps.javac.ast;
 
 import com.intellij.util.containers.Stack;
 import com.sun.source.tree.*;
 import com.sun.source.util.TreeScanner;
+import gnu.trove.THashSet;
 import org.jetbrains.jps.javac.ast.api.JavacDef;
 import org.jetbrains.jps.javac.ast.api.JavacNameTable;
 import org.jetbrains.jps.javac.ast.api.JavacRef;
 import org.jetbrains.jps.javac.ast.api.JavacTypeCast;
 
 import javax.lang.model.element.*;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.*;
+import javax.lang.model.util.Types;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.Set;
@@ -117,6 +102,25 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
     return super.visitMethod(node, refCollector);
   }
 
+  @Override
+  public Tree visitBinary(BinaryTree node, JavacReferenceCollectorListener.ReferenceCollector collector) {
+    Tree.Kind kind = node.getKind();
+    if (kind == Tree.Kind.PLUS) {
+      ExpressionTree lOp = node.getLeftOperand();
+      ExpressionTree rOp = node.getRightOperand();
+      Set<TypeElement> typeElements = extractImplicitToStringCalls(lOp, rOp, collector);
+      if (typeElements != null) {
+        for (TypeElement element : typeElements) {
+          JavacRef.JavacElementRefBase ref = collector.asJavacRef(element);
+          if (ref != null) {
+            collector.sinkImplicitToString(ref);
+          }
+        }
+      }
+    }
+    return super.visitBinary(node, collector);
+  }
+
   private void processMemberDefinition(JavacReferenceCollectorListener.ReferenceCollector refCollector,
                                        JavacRef.JavacElementRefBase ref,
                                        Element element,
@@ -336,4 +340,57 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
     }
     return false;
   }
+
+  private static Set<TypeElement> extractImplicitToStringCalls(Tree lOp,
+                                                               Tree rOp,
+                                                               JavacReferenceCollectorListener.ReferenceCollector collector) {
+    TypeMirror lTypeMirror = collector.getType(lOp);
+    if (lTypeMirror == null) return null;
+    TypeElement lType = asTypeElement(lTypeMirror, collector.getTypeUtility());
+    if (lType == null) return null;
+    TypeMirror rTypeMirror = collector.getType(rOp);
+    if (rTypeMirror == null) return null;
+    TypeElement rType = asTypeElement(rTypeMirror, collector.getTypeUtility());
+    if (rType == null) return null;
+
+    if (isToStringImplicitCall(lType, rType, collector)) {
+      Set<TypeElement> result = new THashSet<TypeElement>();
+      visitTypeHierarchy(rType, result, collector.getTypeUtility());
+      return result;
+    }
+    if (isToStringImplicitCall(rType, lType, collector)) {
+      Set<TypeElement> result = new THashSet<TypeElement>();
+      visitTypeHierarchy(lType, result, collector.getTypeUtility());
+      return result;
+    }
+    return null;
+  }
+
+  private static TypeElement asTypeElement(TypeMirror typeMirror, Types typeUtility) {
+    if (typeMirror instanceof PrimitiveType) {
+      return typeUtility.boxedClass((PrimitiveType)typeMirror);
+    }
+    Element element = typeUtility.asElement(typeMirror);
+    return element instanceof TypeElement ? (TypeElement)element : null;
+  }
+
+  private static boolean isToStringImplicitCall(TypeElement strElement, TypeElement element, JavacReferenceCollectorListener.ReferenceCollector collector) {
+    TypeElement stringEthalone = collector.getNameTable().getStringElement();
+    return strElement == stringEthalone && element != stringEthalone;
+  }
+
+  private static void visitTypeHierarchy(TypeElement element, Set<TypeElement> collector, Types typeUtility) {
+    if (collector.add(element)) {
+      TypeMirror superclass = element.getSuperclass();
+      Element superClass = typeUtility.asElement(superclass);
+      if (superClass instanceof TypeElement) {
+        visitTypeHierarchy((TypeElement)superClass, collector, typeUtility);
+      }
+      for (TypeMirror mirror : element.getInterfaces()) {
+        if (mirror instanceof TypeElement) {
+          visitTypeHierarchy((TypeElement)mirror, collector, typeUtility);
+        }
+      }
+    }
+  }
 }
index bdb0516d5621da220f2a7fb0ee8a486f7cf2f4e7..d46957a101db116b77295b88b7ca5d4e857bce96 100644 (file)
@@ -1,23 +1,10 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package org.jetbrains.jps.javac.ast.api;
 
 import com.intellij.openapi.util.ThrowableComputable;
 import com.intellij.openapi.util.io.DataInputOutputUtilRt;
 import com.intellij.util.ThrowableConsumer;
+import gnu.trove.THashSet;
 import gnu.trove.TObjectIntHashMap;
 import gnu.trove.TObjectIntProcedure;
 import org.jetbrains.annotations.NotNull;
@@ -37,17 +24,20 @@ public class JavacFileData {
   private final TObjectIntHashMap<JavacRef> myImportRefs;
   private final List<JavacTypeCast> myCasts;
   private final List<JavacDef> myDefs;
+  private final Set<JavacRef> myImplicitRefs;
 
   public JavacFileData(@NotNull String path,
                        @NotNull TObjectIntHashMap<JavacRef> refs,
                        @NotNull TObjectIntHashMap<JavacRef> importRefs,
                        @NotNull List<JavacTypeCast> casts,
-                       @NotNull List<JavacDef> defs) {
+                       @NotNull List<JavacDef> defs,
+                       @NotNull Set<JavacRef> implicitRefs) {
     myFilePath = path;
     myRefs = refs;
     myImportRefs = importRefs;
     myCasts = casts;
     myDefs = defs;
+    myImplicitRefs = implicitRefs;
   }
 
   @NotNull
@@ -56,6 +46,11 @@ public class JavacFileData {
   }
 
   @NotNull
+  public Set<JavacRef> getImplicitToStringRefs() {
+    return myImplicitRefs;
+  }
+
+  @NotNull
   public TObjectIntHashMap<JavacRef> getRefs() {
     return myRefs;
   }
@@ -85,6 +80,7 @@ public class JavacFileData {
       saveRefs(stream, getImportRefs());
       saveCasts(stream, getCasts());
       saveDefs(stream, getDefs());
+      saveImplicitToString(stream, getImplicitToStringRefs());
     }
     catch (IOException e) {
       throw new RuntimeException(e);
@@ -101,7 +97,8 @@ public class JavacFileData {
                                readRefs(in),
                                readRefs(in),
                                readCasts(in),
-                               readDefs(in));
+                               readDefs(in),
+                               readImplicitToString(in));
     }
     catch (IOException e) {
       throw new RuntimeException(e);
@@ -276,4 +273,21 @@ public class JavacFileData {
       }
     });
   }
+
+  @NotNull
+  private static Set<JavacRef> readImplicitToString(@NotNull DataInputStream in) throws IOException {
+    int size = DataInputOutputUtilRt.readINT(in);
+    THashSet<JavacRef> result = new THashSet<JavacRef>(size);
+    for (int i = 0; i < size; i++) {
+      result.add(readJavacRef(in));
+    }
+    return result;
+  }
+
+  private static void saveImplicitToString(@NotNull DataOutputStream out, @NotNull Set<JavacRef> refs) throws IOException {
+    DataInputOutputUtilRt.writeINT(out, refs.size());
+    for (JavacRef ref : refs) {
+      writeJavacRef(out, ref);
+    }
+  }
 }
index 0e07167e19bf2ab73ce03fa5288ef12ec6f423be..2f0448f8750a37acd28e34462c12d02db27c7c22 100644 (file)
@@ -1,18 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package org.jetbrains.jps.javac.ast.api;
 
 import com.intellij.util.containers.SLRUCache;
@@ -32,6 +18,7 @@ public class JavacNameTable {
   private TypeElement myStreamElement;
   private TypeElement myIteratorElement;
   private TypeElement myIterableElement;
+  private TypeElement myStringElement;
 
   public JavacNameTable(Elements elements) {
     myParsedNameCache = new SLRUCache<Name, String>(1000, 1000) {
@@ -91,4 +78,12 @@ public class JavacNameTable {
     }
     return myIterableElement;
   }
+
+  @NotNull
+  public TypeElement getStringElement() {
+    if (myStringElement == null) {
+      myStringElement = myElements.getTypeElement("java.lang.String");
+    }
+    return myStringElement;
+  }
 }
index b07f8163287309089b3376625e8e6fc11a0b6b53..e58918ab8e54519ddae0d970d453b46d0d6e5a5f 100644 (file)
@@ -1,18 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package org.jetbrains.jps.backwardRefs;
 
 import com.intellij.openapi.diagnostic.Logger;
@@ -38,6 +24,7 @@ public class BackwardReferenceIndexUtil {
                            TObjectIntHashMap<? extends JavacRef> refs,
                            Collection<JavacDef> defs,
                            Collection<JavacTypeCast> casts,
+                           Collection<JavacRef> implicitToString,
                            final BackwardReferenceIndexWriter writer) {
 
     try {
@@ -47,8 +34,8 @@ public class BackwardReferenceIndexUtil {
       Map<LightRef, Void> definitions = new HashMap<>(defs.size());
       Map<LightRef, Collection<LightRef>> backwardHierarchyMap = new HashMap<>();
       Map<SignatureData, Collection<LightRef>> signatureData = new THashMap<>();
-      THashMap<LightRef, Collection<LightRef>> castMap = new THashMap<>();
-
+      Map<LightRef, Collection<LightRef>> castMap = new THashMap<>();
+      Map<LightRef, Void> implicitToStringMap = new THashMap<>();
 
       final AnonymousClassEnumerator anonymousClassEnumerator = new AnonymousClassEnumerator();
 
@@ -124,7 +111,11 @@ public class BackwardReferenceIndexUtil {
         castMap.computeIfAbsent(enumeratedCastType, t -> new SmartList<>()).add(enumeratedOperandType);
       }
 
-      writer.writeData(fileId, new CompiledFileData(backwardHierarchyMap, castMap, convertedRefs, definitions, signatureData));
+      for (JavacRef ref : implicitToString) {
+        implicitToStringMap.put(writer.asClassUsage(ref), null);
+      }
+
+      writer.writeData(fileId, new CompiledFileData(backwardHierarchyMap, castMap, convertedRefs, definitions, signatureData, implicitToStringMap));
     }
     catch (IOException e) {
       writer.setRebuildCause(e);
index 018659aec2fbbd15b80883c53394eb72cd8ec791..7a99f3f417d9d643c283ef1d8feb5b74af287acc 100644 (file)
@@ -1,25 +1,11 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package org.jetbrains.jps.backwardRefs;
 
 import gnu.trove.TObjectIntHashMap;
-import org.jetbrains.jps.javac.ast.api.JavacTypeCast;
 import org.jetbrains.jps.javac.ast.api.JavacDef;
 import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar;
 import org.jetbrains.jps.javac.ast.api.JavacRef;
+import org.jetbrains.jps.javac.ast.api.JavacTypeCast;
 
 import java.util.Collection;
 
@@ -45,7 +31,8 @@ public class BackwardReferenceRegistrar implements JavacFileReferencesRegistrar
   public void registerFile(String filePath,
                            TObjectIntHashMap<JavacRef> refs,
                            Collection<JavacDef> defs,
-                           Collection<JavacTypeCast> casts) {
-    BackwardReferenceIndexUtil.registerFile(filePath, refs, defs, casts, myWriter);
+                           Collection<JavacTypeCast> casts,
+                           Collection<JavacRef> implicitToString) {
+    BackwardReferenceIndexUtil.registerFile(filePath, refs, defs, casts, implicitToString, myWriter);
   }
 }
index af8af9e6488a0eeed441a386452543920d1cdc24..7f5d977235ad130db59f3f22a15502f1269b2b5f 100644 (file)
@@ -1,18 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package org.jetbrains.jps.backwardRefs.index;
 
 import org.jetbrains.annotations.NotNull;
@@ -28,17 +14,20 @@ public class CompiledFileData {
   private final Map<LightRef, Integer> myReferences;
   private final Map<LightRef, Void> myDefinitions;
   private final Map<SignatureData, Collection<LightRef>> mySignatureData;
+  private final Map<LightRef, Void> myImplicitToString;
 
   public CompiledFileData(@NotNull Map<LightRef, Collection<LightRef>> backwardHierarchyMap,
                           @NotNull Map<LightRef, Collection<LightRef>> casts,
                           @NotNull Map<LightRef, Integer> references,
                           @NotNull Map<LightRef, Void> definitions,
-                          @NotNull Map<SignatureData, Collection<LightRef>> signatureData) {
+                          @NotNull Map<SignatureData, Collection<LightRef>> signatureData,
+                          @NotNull Map<LightRef, Void> implicitToString) {
     myBackwardHierarchyMap = backwardHierarchyMap;
     myCasts = casts;
     myReferences = references;
     myDefinitions = definitions;
     mySignatureData = signatureData;
+    myImplicitToString = implicitToString;
   }
 
   @NotNull
@@ -65,4 +54,9 @@ public class CompiledFileData {
   public Map<LightRef, Collection<LightRef>> getCasts() {
     return myCasts;
   }
+
+  @NotNull
+  public Map<LightRef, Void> getImplicitToString() {
+    return myImplicitToString;
+  }
 }
index 06908429ca476d5b7fa01c4ce0b7f794e08fd6dd..bd336606266d9b9257b631ba67cb8f4d94d92f8f 100644 (file)
@@ -1,18 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package org.jetbrains.jps.backwardRefs.index;
 
 import com.intellij.openapi.util.io.DataInputOutputUtilRt;
@@ -44,13 +30,48 @@ public class CompilerIndices {
   public final static IndexId<LightRef, Void> BACK_CLASS_DEF = IndexId.create("back.class.def");
   public final static IndexId<SignatureData, Collection<LightRef>> BACK_MEMBER_SIGN = IndexId.create("back.member.sign");
   public final static IndexId<LightRef, Collection<LightRef>> BACK_CAST = IndexId.create("back.cast");
+  public final static IndexId<LightRef, Void> IMPLICIT_TO_STRING = IndexId.create("implicit.to.string");
 
   public static List<IndexExtension<?, ?, CompiledFileData>> getIndices() {
     return Arrays.asList(createBackwardClassDefinitionExtension(),
                          createBackwardUsagesExtension(),
                          createBackwardHierarchyExtension(),
                          createBackwardSignatureExtension(),
-                         createBackwardCastExtension());
+                         createBackwardCastExtension(),
+                         createImplicitToStringExtension());
+  }
+
+  private static IndexExtension<LightRef, Void, CompiledFileData> createImplicitToStringExtension() {
+    return new IndexExtension<LightRef, Void, CompiledFileData>() {
+      @NotNull
+      @Override
+      public IndexId<LightRef, Void> getName() {
+        return IMPLICIT_TO_STRING;
+      }
+
+      @NotNull
+      @Override
+      public DataIndexer<LightRef, Void, CompiledFileData> getIndexer() {
+        return CompiledFileData::getImplicitToString;
+      }
+
+      @NotNull
+      @Override
+      public KeyDescriptor<LightRef> getKeyDescriptor() {
+        return LightRefDescriptor.INSTANCE;
+      }
+
+      @NotNull
+      @Override
+      public DataExternalizer<Void> getValueExternalizer() {
+        return VoidDataExternalizer.INSTANCE;
+      }
+
+      @Override
+      public int getVersion() {
+        return 0;
+      }
+    };
   }
 
   private static IndexExtension<LightRef, Collection<LightRef>, CompiledFileData> createBackwardCastExtension() {
index a355a525d6cff9ee856ea67e1e08b690af3cc1b0..446cabecda3b3b058a87862acfc3aa282187e84e 100644 (file)
@@ -1,18 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package org.jetbrains.jps.javac.ast;
 
 import com.intellij.util.Consumer;
@@ -78,7 +64,7 @@ public class InProcessRefCollectorCompilerToolExtension extends AbstractRefColle
     public void consume(JavacFileData data) {
       for (JavacFileReferencesRegistrar registrar : myRegistrars) {
         if (registrar.isEnabled()) {
-          registrar.registerFile(data.getFilePath(), registrar.onlyImports() ? data.getImportRefs() : data.getRefs(), data.getDefs(), data.getCasts());
+          registrar.registerFile(data.getFilePath(), registrar.onlyImports() ? data.getImportRefs() : data.getRefs(), data.getDefs(), data.getCasts(), data.getImplicitToStringRefs());
         }
       }
     }
index 54f7484968377abc7ccc479ed2734e27f0084cc4..e38f3c9ea2de03ed76c27544c074acb7af5f8ad4 100644 (file)
@@ -1,18 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package org.jetbrains.jps.javac.ast.api;
 
 import gnu.trove.TObjectIntHashMap;
@@ -29,5 +15,6 @@ public interface JavacFileReferencesRegistrar {
   void registerFile(String filePath,
                     TObjectIntHashMap<JavacRef> refs,
                     Collection<JavacDef> defs,
-                    Collection<JavacTypeCast> casts);
+                    Collection<JavacTypeCast> casts,
+                    Collection<JavacRef> implicitToString);
 }
diff --git a/jps/jps-builders/testData/referencesIndex/implicitToString/Foo.java b/jps/jps-builders/testData/referencesIndex/implicitToString/Foo.java
new file mode 100644 (file)
index 0000000..fb9837f
--- /dev/null
@@ -0,0 +1,5 @@
+class Foo {
+  void m(Object o) {
+    System.out.println("obj = " + o);
+  }
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/implicitToString/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/implicitToString/initialIndex.txt
new file mode 100644 (file)
index 0000000..2d01e58
--- /dev/null
@@ -0,0 +1,19 @@
+Backward Hierarchy:
+java.lang.Object -> Foo
+
+Backward References:
+Foo in Foo occurrences = 1
+Foo.m(1) in Foo occurrences = 1
+java.io.PrintStream.println(1) in Foo occurrences = 1
+java.lang.Object in Foo occurrences = 1
+java.lang.System in Foo occurrences = 1
+java.lang.System.out in Foo occurrences = 1
+
+Class Definitions:
+Foo in Foo
+
+Members Signatures:
+
+
+Implicit toString():
+java.lang.Object in Foo
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/implicitToStringHierarchy/Foo.java b/jps/jps-builders/testData/referencesIndex/implicitToStringHierarchy/Foo.java
new file mode 100644 (file)
index 0000000..9fd5c18
--- /dev/null
@@ -0,0 +1,15 @@
+class Foo {
+
+}
+
+class FooEx extends Foo {
+
+}
+
+class FooExEx extends FooEx {
+
+  public void m(FooExEx f) {
+    System.out.println(f + " <--- f value");
+  }
+
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/implicitToStringHierarchy/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/implicitToStringHierarchy/initialIndex.txt
new file mode 100644 (file)
index 0000000..8410046
--- /dev/null
@@ -0,0 +1,27 @@
+Backward Hierarchy:
+Foo -> FooEx
+FooEx -> FooExEx
+java.lang.Object -> Foo
+
+Backward References:
+Foo in Foo occurrences = 2
+FooEx in Foo occurrences = 2
+FooExEx in Foo occurrences = 2
+FooExEx.m(1) in Foo occurrences = 1
+java.io.PrintStream.println(1) in Foo occurrences = 1
+java.lang.System in Foo occurrences = 1
+java.lang.System.out in Foo occurrences = 1
+
+Class Definitions:
+Foo in Foo
+FooEx in Foo
+FooExEx in Foo
+
+Members Signatures:
+
+
+Implicit toString():
+Foo in Foo
+FooEx in Foo
+FooExEx in Foo
+java.lang.Object in Foo
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/implicitToStringLongObject/Foo.java b/jps/jps-builders/testData/referencesIndex/implicitToStringLongObject/Foo.java
new file mode 100644 (file)
index 0000000..5e7311b
--- /dev/null
@@ -0,0 +1,5 @@
+class A {
+  void m(Long value) {
+    System.out.println("value = " + value);
+  }
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/implicitToStringLongObject/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/implicitToStringLongObject/initialIndex.txt
new file mode 100644 (file)
index 0000000..49ebd66
--- /dev/null
@@ -0,0 +1,21 @@
+Backward Hierarchy:
+java.lang.Object -> A
+
+Backward References:
+A in Foo occurrences = 1
+A.m(1) in Foo occurrences = 1
+java.io.PrintStream.println(1) in Foo occurrences = 1
+java.lang.Long in Foo occurrences = 1
+java.lang.System in Foo occurrences = 1
+java.lang.System.out in Foo occurrences = 1
+
+Class Definitions:
+A in Foo
+
+Members Signatures:
+
+
+Implicit toString():
+java.lang.Long in Foo
+java.lang.Number in Foo
+java.lang.Object in Foo
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/implicitToStringPrimitives/Foo.java b/jps/jps-builders/testData/referencesIndex/implicitToStringPrimitives/Foo.java
new file mode 100644 (file)
index 0000000..f96e4eb
--- /dev/null
@@ -0,0 +1,10 @@
+class A {
+  void m(boolean b, char c, int i, long l, float f, double d) {
+    System.out.println(" value " + b + " !! ");
+    System.out.println(" value " + c);
+    System.out.println(" value " + i + " !! ");
+    System.out.println(" value " + l);
+    System.out.println(" value " + f + " !! ");
+    System.out.println(" value " + d);
+  }
+}
\ No newline at end of file
diff --git a/jps/jps-builders/testData/referencesIndex/implicitToStringPrimitives/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/implicitToStringPrimitives/initialIndex.txt
new file mode 100644 (file)
index 0000000..f2b92d1
--- /dev/null
@@ -0,0 +1,25 @@
+Backward Hierarchy:
+java.lang.Object -> A
+
+Backward References:
+A in Foo occurrences = 1
+A.m(6) in Foo occurrences = 1
+java.io.PrintStream.println(1) in Foo occurrences = 6
+java.lang.System in Foo occurrences = 6
+java.lang.System.out in Foo occurrences = 6
+
+Class Definitions:
+A in Foo
+
+Members Signatures:
+
+
+Implicit toString():
+java.lang.Boolean in Foo
+java.lang.Character in Foo
+java.lang.Double in Foo
+java.lang.Float in Foo
+java.lang.Integer in Foo
+java.lang.Long in Foo
+java.lang.Number in Foo
+java.lang.Object in Foo
\ No newline at end of file
index f14ba9239eadca8a4a72b0b4b2052eedfc94e346..2aa9cce732141d61150624b403eb025611076979 100644 (file)
@@ -1,18 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package org.jetbrains.references
 
 import com.intellij.openapi.application.ex.PathManagerEx
@@ -193,5 +179,21 @@ class ReferenceIndexTest : ReferenceIndexTestBase() {
   fun testCastDataGenerics() {
     assertIndexOnRebuild("Foo.java")
   }
+
+  fun testImplicitToString() {
+    assertIndexOnRebuild("Foo.java")
+  }
+
+  fun testImplicitToStringPrimitives() {
+    assertIndexOnRebuild("Foo.java")
+  }
+
+  fun testImplicitToStringLongObject() {
+    assertIndexOnRebuild("Foo.java")
+  }
+
+  fun testImplicitToStringHierarchy() {
+    assertIndexOnRebuild("Foo.java")
+  }
 }
 
index 56bb70ed631532b8d9582ba43ca1c96b4dad6c3a..7280e7cb759f8ce0bf82460ea2cbec774540b818 100644 (file)
@@ -1,18 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package org.jetbrains.references
 
 import com.intellij.openapi.util.io.FileUtil
@@ -179,6 +165,29 @@ abstract class ReferenceIndexTestBase : JpsBuildTestCase() {
         result.append(typeCasts.joinToString(separator = "\n"))
       }
 
+      val implicitToString = mutableListOf<String>()
+      storage(index, CompilerIndices.IMPLICIT_TO_STRING).processKeys {type ->
+        val callPlaceFiles = mutableListOf<String>()
+        val valueIt = index[CompilerIndices.IMPLICIT_TO_STRING].getData(type).valueIterator
+        while (valueIt.hasNext()) {
+          valueIt.next()
+          val files = valueIt.inputIdsIterator
+          while (files.hasNext()) {
+            callPlaceFiles.add(files.next().asFileName(fileEnumerator))
+          }
+        }
+        if (!callPlaceFiles.isEmpty()) {
+          callPlaceFiles.sort()
+          implicitToString.add(type.asText(nameEnumerator) + " in " + callPlaceFiles.joinToString(separator = " "))
+        }
+        true
+      }
+      if (implicitToString.isNotEmpty()) {
+        result.append("\n\nImplicit toString():\n")
+        implicitToString.sort()
+        result.append(implicitToString.joinToString(separator = "\n"))
+      }
+
       return result.toString()
     }
     finally {
index bfe029361de7ab63ad0c7d349c70e90da4be44ba..a7564674353575f9f5c4e9009a8c963e40038f3c 100644 (file)
@@ -19,6 +19,7 @@ find.what.derived.interfaces.checkbox=&Derived interfaces
 find.what.derived.classes.checkbox=&Derived classes
 find.what.implementing.methods.checkbox=&Implementing methods
 find.what.overriding.methods.checkbox=Over&riding methods
+find.what.implicit.to.string.checkbox=I&mplicit calls
 find.what.usages.of.classes.and.interfaces=Usages of &classes and interfaces
 
 
@@ -60,6 +61,7 @@ find.usages.panel.title.derived.interfaces=Derived Interfaces
 find.usages.panel.title.implementing.classes=Implementing Classes
 find.usages.panel.title.implementing.methods=Implementing Methods
 find.usages.panel.title.overriding.methods=Overriding Methods
+find.usages.panel.title.implicit.to.string.calls=Implicit Calls
 find.usages.of.element_type.element_name.not.found.message=Usages of {0} {1} not found
 find.searching.for.references.to.class.progress=Searching for references to class {0}...
 find.usage.view.no.usages.text=No usages found