Merge branch 'db/implicit-to-string'

# Conflicts:
#	java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java
#	java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java
#	jps/jps-builders-6/src/org/jetbrains/jps/javac/ast/JavacTreeRefScanner.java
#	jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt
This commit is contained in:
Dmitry Batkovich
2018-04-24 16:08:53 +03:00
39 changed files with 973 additions and 257 deletions
@@ -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
*
@@ -162,12 +162,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");
}
}
@@ -516,8 +533,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(
@@ -527,7 +543,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;
if (!myReadDataLock.tryLock()) return null;
@@ -536,14 +564,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 {
@@ -845,4 +873,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;
}
}
@@ -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;
@@ -196,6 +182,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) {
@@ -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"));
}
}
}
@@ -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);
}
}
@@ -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;
}
}
}
@@ -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));
}
}
@@ -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>
@@ -37,6 +37,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.
@@ -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);
}
}
@@ -0,0 +1,5 @@
class A {
void m(FooImpl f) {
String s = "f = " + f.toString();
}
}
@@ -0,0 +1,5 @@
class B {
void m(FooImpl f) {
String s = "f = " + f;
}
}
@@ -0,0 +1,13 @@
class FooImpl extends Foo {
@Override
public String toString() {
return "FooImpl instance";
}
}
class Foo {
@Override
public String toString() {
return "Foo instance";
}
}
@@ -0,0 +1,5 @@
public class Foo {
public void bar(long l) {
System.out.println("xxx " + l + " yyy");
}
}
@@ -0,0 +1,11 @@
class Foo {
public static void main(String[] args) {
System.out.println(new Foo() + "");
}
@Override
public String toString() {
return "Foo";
}
}
@@ -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();
@@ -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])
}
}
@@ -173,6 +173,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();
@@ -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);
}
@@ -4,16 +4,15 @@ 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;
@@ -116,6 +115,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,
@@ -164,7 +182,6 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
private final Stack<TypeElement> myCurrentEnclosingElement = new Stack<TypeElement>(1);
private final Stack<Long> myCurrentEnclosingElementOffset = new Stack<Long>(1);
private final Stack<NewClassTree> myCurrentAnonymousTree = new Stack<NewClassTree>(1);
@Override
public Tree visitClass(ClassTree node, JavacReferenceCollectorListener.ReferenceCollector refCollector) {
@@ -339,4 +356,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);
}
}
}
}
}
@@ -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
@@ -55,6 +45,11 @@ public class JavacFileData {
return myFilePath;
}
@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);
}
}
}
@@ -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;
}
}
@@ -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);
@@ -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);
}
}
@@ -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;
}
}
@@ -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() {
@@ -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());
}
}
}
@@ -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);
}
@@ -0,0 +1,5 @@
class Foo {
void m(Object o) {
System.out.println("obj = " + o);
}
}
@@ -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
@@ -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");
}
}
@@ -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
@@ -0,0 +1,5 @@
class A {
void m(Long value) {
System.out.println("value = " + value);
}
}
@@ -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
@@ -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);
}
}
@@ -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
@@ -183,5 +183,21 @@ class ReferenceIndexTest : ReferenceIndexTestBase() {
fun testNestedAnonymouses() {
assertIndexOnRebuild("Anonymouses.java", "Classes.java")
}
fun testImplicitToString() {
assertIndexOnRebuild("Foo.java")
}
fun testImplicitToStringPrimitives() {
assertIndexOnRebuild("Foo.java")
}
fun testImplicitToStringLongObject() {
assertIndexOnRebuild("Foo.java")
}
fun testImplicitToStringHierarchy() {
assertIndexOnRebuild("Foo.java")
}
}
@@ -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 {
@@ -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