diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java index 524b95e38275..8e9bcd990eb2 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java @@ -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; } diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java index 18f5a32af82a..79a09cf643b1 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java @@ -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(); diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java index 3cf7471871e1..6c862a02d923 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java @@ -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()); diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java index f138d151ff39..98048f1a8a61 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java @@ -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;")); + } }