boolean (@NotNull NodeIterator patternNodes, @NotNull NodeIterator matchedNodes, @NotNull MatchContext context) { if (patternNodes.hasNext() && !matchedNodes.hasNext()) { return validateSatisfactionOfHandlers(patternNodes, context); } Set<PsiElement> matchedElements = null; while (patternNodes.hasNext()) { final PsiElement patternNode = patternNodes.current(); patternNodes.advance(); final CompiledPattern pattern = context.getPattern(); final MatchingHandler handler = pattern.getHandler(patternNode); matchedNodes.reset(); boolean allElementsMatched = true; int matchedOccurs = 0; do { final PsiElement pinnedNode = handler.getPinnedNode(); final PsiElement matchedNode = (pinnedNode != null) ? pinnedNode : matchedNodes.current(); if (pinnedNode == null) matchedNodes.advance(); if (matchedElements == null || !matchedElements.contains(matchedNode)) { allElementsMatched = false; if (handler.match(patternNode, matchedNode, context)) { matchedOccurs++; if (matchedElements == null) matchedElements = new HashSet<>(); matchedElements.add(matchedNode); if (handler.shouldAdvanceThePatternFor(patternNode, matchedNode)) { break; } } else if (pinnedNode != null) { return false; } // clear state of dependent objects clearingVisitor.clearState(pattern, patternNode); } if (!matchedNodes.hasNext() || pinnedNode != null) { if (!handler.validate(context, matchedOccurs)) return false; if (allElementsMatched || !patternNodes.hasNext()) { final boolean result = validateSatisfactionOfHandlers(patternNodes, context); if (result && matchedElements != null) { context.notifyMatchedElements(matchedElements); } return result; } break; } } while(true); if (!handler.validate(context, matchedOccurs)) return false; } final boolean result = validateSatisfactionOfHandlers(patternNodes, context); if (result && matchedElements != null) { context.notifyMatchedElements(matchedElements); } return result; }