SSR: fix for loop matching (IDEA-208475)

This commit is contained in:
Bas Leijdekkers
2019-03-08 11:32:41 +01:00
parent 6ff473be61
commit 2a7d38f141
4 changed files with 55 additions and 15 deletions
@@ -874,6 +874,7 @@ public class JavaStructuralSearchProfile extends StructuralSearchProfile {
if (grandParent instanceof PsiAssertStatement) return ((PsiAssertStatement)grandParent).getAssertDescription() == parent;
if (grandParent instanceof PsiNameValuePair) return ((PsiNameValuePair)grandParent).getValue() == parent;
if (grandParent instanceof PsiBreakStatement) return true;
if (grandParent instanceof PsiForStatement) return true;
}
if (grandParent instanceof PsiExpressionList) {
PsiElement label = grandParent.getParent();
@@ -894,11 +895,18 @@ public class JavaStructuralSearchProfile extends StructuralSearchProfile {
return type instanceof PsiWildcardType && ((PsiWildcardType)type).isExtends();
}
}
if (grandParent instanceof PsiExpressionStatement && hasSemicolon(grandParent)) {
if (grandParent instanceof PsiExpressionStatement) {
final PsiElement greatGrandParent = grandParent.getParent();
return !(greatGrandParent instanceof PsiCodeBlock) ||
!(greatGrandParent.getParent() instanceof JavaDummyHolder) ||
PsiTreeUtil.getChildrenOfAnyType(greatGrandParent, PsiStatement.class, PsiComment.class).size() > 1;
if (greatGrandParent instanceof PsiForStatement &&
!PsiTreeUtil.isAncestor(((PsiForStatement)greatGrandParent).getBody(), variableNode, true)) {
return true;
}
if (hasSemicolon(grandParent)) {
if (greatGrandParent instanceof PsiLoopStatement || greatGrandParent instanceof PsiIfStatement) return false;
return !(greatGrandParent instanceof PsiCodeBlock) ||
!(greatGrandParent.getParent() instanceof JavaDummyHolder) ||
PsiTreeUtil.getChildrenOfAnyType(greatGrandParent, PsiStatement.class, PsiComment.class).size() > 1;
}
}
return false;
}
@@ -916,11 +924,14 @@ public class JavaStructuralSearchProfile extends StructuralSearchProfile {
final PsiElement grandParent = parent.getParent();
if (grandParent instanceof PsiPolyadicExpression) return true;
if (grandParent instanceof PsiExpressionList && grandParent.getParent() instanceof PsiSwitchLabelStatementBase) return true;
if (grandParent instanceof PsiExpressionStatement && hasSemicolon(grandParent)) return true;
if (grandParent instanceof PsiExpressionStatement && hasSemicolon(grandParent)) {
final PsiElement greatGrandParent = grandParent.getParent();
return greatGrandParent instanceof PsiCodeBlock;
}
if (grandParent instanceof PsiReferenceList) {
final PsiElement greatGrandParent = grandParent.getParent();
return !(greatGrandParent instanceof PsiClass) || ((PsiClass)greatGrandParent).getExtendsList() != grandParent ||
greatGrandParent instanceof PsiTypeParameter;
greatGrandParent instanceof PsiTypeParameter;
}
return false;
}
@@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2019 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.structuralsearch.impl.matcher;
import com.intellij.dupLocator.iterators.ArrayBackedNodeIterator;
@@ -47,7 +47,7 @@ public class JavaMatchingVisitor extends JavaElementVisitor {
}
public JavaMatchingVisitor(GlobalMatchingVisitor matchingVisitor) {
this.myMatchingVisitor = matchingVisitor;
myMatchingVisitor = matchingVisitor;
}
@Override
@@ -1195,12 +1195,16 @@ public class JavaMatchingVisitor extends JavaElementVisitor {
public void visitForStatement(final PsiForStatement for1) {
final PsiForStatement for2 = (PsiForStatement)myMatchingVisitor.getElement();
myMatchingVisitor.setResult(myMatchingVisitor.match(for1.getInitialization(), for2.getInitialization()) &&
myMatchingVisitor.match(for1.getCondition(), for2.getCondition()) &&
myMatchingVisitor.match(for1.getUpdate(), for2.getUpdate()) &&
myMatchingVisitor.setResult(myMatchingVisitor.matchOptionally(notEmpty(for1.getInitialization()), notEmpty(for2.getInitialization())) &&
myMatchingVisitor.matchOptionally(for1.getCondition(), for2.getCondition()) &&
myMatchingVisitor.matchOptionally(for1.getUpdate(), for2.getUpdate()) &&
matchBody(for1.getBody(), for2.getBody()));
}
private static PsiStatement notEmpty(PsiStatement statement) {
return statement instanceof PsiEmptyStatement ? null : statement;
}
@Override
public void visitForeachStatement(PsiForeachStatement for1) {
final PsiForeachStatement for2 = (PsiForeachStatement)myMatchingVisitor.getElement();
@@ -6,6 +6,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.source.JavaDummyHolder;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.search.GlobalSearchScope;
@@ -482,11 +483,13 @@ public class JavaCompilingVisitor extends JavaRecursiveElementWalkingVisitor {
public void visitExpressionStatement(PsiExpressionStatement expressionStatement) {
super.visitExpressionStatement(expressionStatement);
final CompiledPattern pattern = myCompilingVisitor.getContext().getPattern();
final PsiElement child = expressionStatement.getLastChild();
if (!(child instanceof PsiJavaToken) && !(child instanceof PsiComment)) {
final PsiElement parent = expressionStatement.getParent();
if (!(child instanceof PsiJavaToken) && !(child instanceof PsiComment) &&
parent instanceof PsiCodeBlock && parent.getParent() instanceof JavaDummyHolder) {
// search for expression or symbol
final PsiElement reference = expressionStatement.getFirstChild();
final CompiledPattern pattern = myCompilingVisitor.getContext().getPattern();
MatchingHandler referenceHandler = pattern.getHandler(reference);
if (referenceHandler instanceof SubstitutionHandler && (reference instanceof PsiReferenceExpression)) {
@@ -510,10 +513,9 @@ public class JavaCompilingVisitor extends JavaRecursiveElementWalkingVisitor {
}
}
else {
final CompiledPattern pattern = myCompilingVisitor.getContext().getPattern();
if (expressionStatement.getExpression() instanceof PsiReferenceExpression && pattern.isRealTypedVar(expressionStatement)) {
// search for statement
final MatchingHandler handler = myCompilingVisitor.getContext().getPattern().getHandler(expressionStatement);
final MatchingHandler handler = pattern.getHandler(expressionStatement);
if (handler instanceof SubstitutionHandler) {
final SubstitutionHandler substitutionHandler = (SubstitutionHandler)handler;
substitutionHandler.setFilter(new StatementFilter());
@@ -2490,6 +2490,12 @@ public class StructuralSearchTest extends StructuralSearchTestCase {
options.fillSearchCriteria("void '_a:* ();");
assertEquals("TEXT HIERARCHY not applicable for a", checkApplicableConstraints(options));
options.fillSearchCriteria("if (true) '_st{0,0};");
assertEquals("MINIMUM ZERO not applicable for st", checkApplicableConstraints(options));
options.fillSearchCriteria("while (true) '_st+;");
assertEquals("MAXIMUM UNLIMITED not applicable for st", checkApplicableConstraints(options));
}
public void testFindInnerClass() {
@@ -3159,4 +3165,21 @@ public class StructuralSearchTest extends StructuralSearchTestCase {
" '_FieldType2 'field2 = '_init2?;\n" +
"}"));
}
public void testForStatement() {
String in = "class X {{" +
" for (int i = 0; i < 10; i++) {}" +
" " +
" for (;;) {}" +
" for (int i = 0; ;) {}" +
" for (int i = 0; true; ) {}" +
"}}";
assertEquals("find all for loops", 4, findMatchesCount(in, "for(;;) '_st;"));
assertEquals("find loops without initializers", 1, findMatchesCount(in, "for('_init{0,0};;) '_st;"));
assertEquals("find loops without condition", 2, findMatchesCount(in, "for(;'_cond{0,0};) '_st;"));
assertEquals("find loops without update", 3, findMatchesCount(in, "for(;;'_update{0,0}) '_st;"));
assertEquals("find all for loops 2", 4, findMatchesCount(in, "for('_init?;;) '_st;"));
assertEquals("find all for loops 3", 4, findMatchesCount(in, "for(;;'_update?) '_st;"));
assertEquals("find all for loops 4", 4, findMatchesCount(in, "for(;'_cond?;) '_st;"));
}
}