mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Similar usages: Feature collection for fields
(cherry picked from commit de9b492eb32220d016223baee65c3956502d9ef4) IJ-CR-101246 GitOrigin-RevId: fa893906877a34d3242b8cc43bb10ac4d749819b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
b881c7cb31
commit
ef63d90876
+99
@@ -8,9 +8,13 @@ import com.intellij.psi.util.MethodSignature;
|
||||
import com.intellij.usages.similarity.bag.Bag;
|
||||
import com.intellij.usages.similarity.features.UsageSimilarityFeaturesRecorder;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static com.intellij.psi.JavaTokenType.FINAL_KEYWORD;
|
||||
@@ -18,11 +22,13 @@ import static com.intellij.psi.JavaTokenType.FINAL_KEYWORD;
|
||||
public class JavaSimilarityFeaturesExtractor extends JavaRecursiveElementVisitor {
|
||||
private final @NotNull UsageSimilarityFeaturesRecorder myUsageSimilarityFeaturesRecorder;
|
||||
private final @NotNull PsiElement myContext;
|
||||
private final @NotNull HashSet<String> myVariableNames;
|
||||
private final @NotNull PsiElement myUsage;
|
||||
|
||||
public JavaSimilarityFeaturesExtractor(@NotNull PsiElement usage, @NotNull PsiElement context) {
|
||||
myUsageSimilarityFeaturesRecorder = new UsageSimilarityFeaturesRecorder(context, usage);
|
||||
myContext = context;
|
||||
myVariableNames = collectVariableNames();
|
||||
myUsage = usage;
|
||||
}
|
||||
|
||||
@@ -135,6 +141,9 @@ public class JavaSimilarityFeaturesExtractor extends JavaRecursiveElementVisitor
|
||||
|
||||
@Override
|
||||
public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {
|
||||
if (Registry.is("similarity.find.usages.add.features.for.fields") && isField(expression)) {
|
||||
myUsageSimilarityFeaturesRecorder.addAllFeatures(expression, "FIELD: " + expression.getReferenceName());
|
||||
}
|
||||
if (!(expression instanceof PsiMethodReferenceExpression)) {
|
||||
if (!Registry.is("similarity.find.usages.fast.clustering")) {
|
||||
myUsageSimilarityFeaturesRecorder.addAllFeatures(expression, getTypeRepresentation(expression));
|
||||
@@ -236,6 +245,96 @@ public class JavaSimilarityFeaturesExtractor extends JavaRecursiveElementVisitor
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isField(@NotNull PsiReferenceExpression referenceExpression) {
|
||||
return !isMethod(referenceExpression) &&
|
||||
(ContainerUtil.exists(referenceExpression.getChildren(), child -> child.getText().equals(".")) ||
|
||||
!myVariableNames.contains(referenceExpression.getReferenceName()));
|
||||
}
|
||||
|
||||
private @NotNull HashSet<String> collectVariableNames() {
|
||||
PsiMethod containingMethod = getWrappingMethodForUsage(myContext);
|
||||
if (containingMethod == null) return new HashSet<>();
|
||||
PsiCodeBlock body = containingMethod.getBody();
|
||||
if (body == null) return new HashSet<>();
|
||||
HashSet<String> allVariables = new HashSet<>();
|
||||
allVariables.addAll(processFunctionParameters(containingMethod));
|
||||
allVariables.addAll(processFunctionStatements(body));
|
||||
return allVariables;
|
||||
}
|
||||
|
||||
private static boolean isMethod(@NotNull PsiReferenceExpression expression) {
|
||||
return expression.getParent() instanceof PsiMethodCallExpression &&
|
||||
expression.getNextSibling() instanceof PsiExpressionList;
|
||||
}
|
||||
|
||||
private static @Nullable PsiMethod getWrappingMethodForUsage(@NotNull PsiElement usage) {
|
||||
while (!(usage instanceof PsiMethod)) {
|
||||
usage = usage.getParent();
|
||||
if(usage instanceof PsiFile || usage == null) return null;
|
||||
}
|
||||
return (PsiMethod)usage;
|
||||
}
|
||||
|
||||
private static @NotNull HashSet<String> processFunctionParameters(@NotNull PsiMethod containingMethod) {
|
||||
HashSet<String> variableNames = new HashSet<>();
|
||||
Arrays.stream(containingMethod.getParameterList().getParameters())
|
||||
.forEach(parameter -> variableNames.add(parameter.getName()));
|
||||
return variableNames;
|
||||
}
|
||||
|
||||
private static @NotNull HashSet<String> processFunctionStatements(@NotNull PsiCodeBlock body) {
|
||||
HashSet<String> variableNames = new HashSet<>();
|
||||
for (PsiStatement statement : body.getStatements()) {
|
||||
if (statement instanceof PsiDeclarationStatement) {
|
||||
variableNames.addAll(processDeclarationStatement((PsiDeclarationStatement)statement));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (statement instanceof PsiLoopStatement) {
|
||||
variableNames.addAll(processLoopStatement(statement));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (statement instanceof PsiIfStatement) {
|
||||
Arrays.stream(statement.getChildren())
|
||||
.filter(child -> child instanceof PsiBlockStatement)
|
||||
.forEach(blockStatement -> variableNames
|
||||
.addAll(processFunctionStatements(((PsiBlockStatement)blockStatement).getCodeBlock())));
|
||||
}
|
||||
}
|
||||
return variableNames;
|
||||
}
|
||||
|
||||
private static @NotNull HashSet<String> processLoopStatement(@NotNull PsiStatement statement) {
|
||||
HashSet<String> variableNames = new HashSet<>();
|
||||
if (statement instanceof PsiForeachStatement) {
|
||||
PsiParameter declaration = ((PsiForeachStatement)statement).getIterationParameter();
|
||||
variableNames.add((declaration).getName());
|
||||
}
|
||||
|
||||
if (statement instanceof PsiForStatement) {
|
||||
PsiStatement initStatement = ((PsiForStatement)statement).getInitialization();
|
||||
if (initStatement instanceof PsiDeclarationStatement) {
|
||||
variableNames.addAll(processDeclarationStatement((PsiDeclarationStatement)initStatement));
|
||||
}
|
||||
}
|
||||
|
||||
PsiStatement statementBody = ((PsiLoopStatement)statement).getBody();
|
||||
if (!(statementBody instanceof PsiBlockStatement)) return variableNames;
|
||||
variableNames.addAll(processFunctionStatements(((PsiBlockStatement)statementBody).getCodeBlock()));
|
||||
return variableNames;
|
||||
}
|
||||
|
||||
private static @NotNull HashSet<String> processDeclarationStatement(@NotNull PsiDeclarationStatement statement) {
|
||||
HashSet<String> variableNames = new HashSet<>();
|
||||
Arrays.stream(statement.getDeclaredElements()).forEach(element -> {
|
||||
if (element instanceof PsiLocalVariable) {
|
||||
variableNames.add(((PsiLocalVariable)element).getName());
|
||||
}
|
||||
});
|
||||
return variableNames;
|
||||
}
|
||||
|
||||
private static @Nullable String viaResolve(@NotNull PsiMethodReferenceExpression expression) {
|
||||
final PsiElement resolve = expression.resolve();
|
||||
final PsiMethod method = ObjectUtils.tryCast(resolve, PsiMethod.class);
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
public class Test {
|
||||
int a;
|
||||
|
||||
Test() {
|
||||
this.field = 0;
|
||||
}
|
||||
|
||||
private int getInteger(int num) {
|
||||
return num;
|
||||
}
|
||||
|
||||
private int runTest(){
|
||||
boolean localVar = true;
|
||||
for(int forLoopVariable = 0; forLoopVariable < 0; ++forLoopVariable){
|
||||
localVar += 1;
|
||||
|
||||
if (localVar) {
|
||||
int nestedIfVar = 0;
|
||||
}
|
||||
}
|
||||
|
||||
while(localVar){
|
||||
boolean whileVariable = true;
|
||||
localVar = false;
|
||||
}
|
||||
|
||||
if(true){
|
||||
int ifVar = 5;
|
||||
}
|
||||
else{
|
||||
int elseVar = 7;
|
||||
}
|
||||
|
||||
ArrayList<String> list = new ArrayList();
|
||||
for(String forEachVar: list){
|
||||
|
||||
}
|
||||
|
||||
get<caret>Integer(2 + field);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Test {
|
||||
int field;
|
||||
Test(){
|
||||
this.field = 0;
|
||||
}
|
||||
|
||||
private int test(int num){
|
||||
return num;
|
||||
}
|
||||
|
||||
private void testFields(){
|
||||
int a = 1;
|
||||
test(a);
|
||||
test(field);
|
||||
}
|
||||
|
||||
private void testIfWithFields(){
|
||||
int a = 1;
|
||||
|
||||
if(tets(a) == 5){
|
||||
System.out.println("variable");
|
||||
}
|
||||
|
||||
if(test(field) == 5){
|
||||
System.out.println("field");
|
||||
}
|
||||
|
||||
if(test(a) == 5){
|
||||
System.out.println("field");
|
||||
}
|
||||
|
||||
if(test(field) != 5){
|
||||
System.out.println("field");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
[{
|
||||
15|test|(a);}
|
||||
, {
|
||||
16|test|(field);}
|
||||
, {
|
||||
26|if|(|test|(field) == |5|){}
|
||||
, {
|
||||
30|if|(|test|(a) == |5|){}
|
||||
, {
|
||||
34|if|(|test|(field) != |5|){}
|
||||
]
|
||||
+8
-2
@@ -79,7 +79,13 @@ public class JavaFindUsagesGroupsTest extends JavaPsiTestCase {
|
||||
|
||||
public void testArrayAccess() { doTest(getMethod("Market", "getGoods")); }
|
||||
|
||||
public void testForEach() {
|
||||
doTest(getMethod("java.lang.Iterable", "forEach"));
|
||||
public void testField() {
|
||||
try {
|
||||
Registry.get("similarity.find.usages.add.features.for.fields").setValue(true);
|
||||
doTest(getMethod("Test", "test"));
|
||||
}
|
||||
finally {
|
||||
Registry.get("similarity.find.usages.add.features.for.fields").resetToDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
@@ -152,6 +152,27 @@ public class JavaUsagesBySimilarityTest extends JavaCodeInsightFixtureTestCase {
|
||||
assertEquals(1, features.get("USAGE: FOR"));
|
||||
}
|
||||
|
||||
public void testFieldFeatures() throws ExecutionException, InterruptedException {
|
||||
try {
|
||||
Registry.get("similarity.find.usages.add.features.for.fields").setValue(true);
|
||||
myFixture.configureByFile("Field.java");
|
||||
PsiElement elementAtCaret = myFixture.getReferenceAtCaretPosition().getElement();
|
||||
final Bag features = getFeatures(elementAtCaret);
|
||||
assertEquals(1, features.get("CONTEXT: FIELD: field"));
|
||||
assertEquals(0, features.get("CONTEXT: FIELD: localVariable"));
|
||||
assertEquals(0, features.get("CONTEXT: FIELD: forLoopVariable"));
|
||||
assertEquals(0, features.get("CONTEXT: FIELD: nestedIfVar"));
|
||||
assertEquals(0, features.get("CONTEXT: FIELD: whileVariable"));
|
||||
assertEquals(0, features.get("CONTEXT: FIELD: ifVar"));
|
||||
assertEquals(0, features.get("CONTEXT: FIELD: elseVar"));
|
||||
assertEquals(0, features.get("CONTEXT: FIELD: list"));
|
||||
assertEquals(0, features.get("CONTEXT: FIELD: forEachVar"));
|
||||
}
|
||||
finally {
|
||||
Registry.get("similarity.find.usages.add.features.for.fields").resetToDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public void testBag() {
|
||||
final Bag bag = new Bag("a", "b");
|
||||
assertEquals("""
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<registryKey key="similarity.find.usages.use.parent.features.with.child.number" defaultValue="false"
|
||||
description="Take in account what child number is in parent node"/>
|
||||
<registryKey defaultValue="true" description="Use information from parent nodes" key="similarity.find.usages.use.parent.features"/>
|
||||
<registryKey defaultValue="true" description="Add features for fields" key="similarity.find.usages.add.features.for.fields"/>
|
||||
<registryKey defaultValue="true" description="Enable similarity clustering for Java usages" key="similarity.find.usages.java.clustering.enable"/>
|
||||
<registryKey defaultValue="true" description="Enable similarity clustering for Kotlin usages"
|
||||
key="similarity.find.usages.kotlin.clustering.enable"/>
|
||||
|
||||
Reference in New Issue
Block a user