SSR: new reference predicate (IDEA-81235)

This commit is contained in:
Bas Leijdekkers
2017-10-09 12:54:17 +02:00
parent 066d01d5b0
commit baf02134e4
9 changed files with 205 additions and 311 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.structuralsearch.impl.matcher.handlers;
import com.intellij.dupLocator.iterators.ArrayBackedNodeIterator;
@@ -39,8 +25,10 @@ public class DeclarationStatementHandler extends MatchingHandler {
if (patternNode instanceof PsiComment) {
return myCommentHandler.match(patternNode, matchedNode, context);
}
if (!super.match(patternNode, matchedNode,context)) {
return false;
}
if (!super.match(patternNode,matchedNode,context)) return false;
final PsiDeclarationStatement dcl = (PsiDeclarationStatement)patternNode;
if (matchedNode instanceof PsiDeclarationStatement) {
return context.getMatcher().matchSequentially(new SsrFilteringNodeIterator(patternNode.getFirstChild()),
@@ -49,7 +37,7 @@ public class DeclarationStatementHandler extends MatchingHandler {
final PsiElement[] declared = dcl.getDeclaredElements();
// declaration statement could wrap class or dcl
if (declared.length > 0 && !(matchedNode.getParent() instanceof PsiDeclarationStatement) /* skip twice matching for child*/) {
if (declared.length > 0 && (!context.shouldRecursivelyMatch() || !(matchedNode.getParent() instanceof PsiDeclarationStatement)) /* skip twice matching for child*/) {
if (!(matchedNode instanceof PsiField)) {
return context.getMatcher().matchSequentially(
new ArrayBackedNodeIterator(declared),
@@ -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.structuralsearch;
import org.jdom.Element;
@@ -34,9 +20,8 @@ public class MatchVariableConstraint extends NamedScriptableDefinition {
private int minCount = 1;
private int maxCount = 1;
private boolean greedy = true;
private boolean reference;
private boolean invertReference;
private String nameOfReferenceVar = "";
private String referenceConstraint = "";
private boolean partOfSearchResults;
private String nameOfExprType = "";
private boolean invertExprType;
@@ -52,7 +37,7 @@ public class MatchVariableConstraint extends NamedScriptableDefinition {
private boolean invertWithinConstraint;
private final boolean artificial;
@NonNls private static final String NAME_OF_REFEENCE_VAR = "nameOfReferenceVar";
@NonNls private static final String REFERENCE_CONDITION = "reference";
@NonNls private static final String NAME_OF_EXPRTYPE = "nameOfExprType";
@NonNls private static final String NAME_OF_FORMALTYPE = "nameOfFormalType";
@NonNls private static final String REGEXP = "regexp";
@@ -68,6 +53,7 @@ public class MatchVariableConstraint extends NamedScriptableDefinition {
@NonNls private static final String NEGATE_FORMALTYPE_CONDITION = "negateFormalType";
@NonNls private static final String NEGATE_CONTAINS_CONDITION = "negateContains";
@NonNls private static final String NEGATE_WITHIN_CONDITION = "negateWithin";
@NonNls private static final String NEGATE_REFERENCE_CONDITION = "negateReference";
@NonNls private static final String WITHIN_CONDITION = "within";
@NonNls private static final String CONTAINS_CONDITION = "contains";
@NonNls private static final String TARGET = "target";
@@ -134,14 +120,6 @@ public class MatchVariableConstraint extends NamedScriptableDefinition {
this.partOfSearchResults = partOfSearchResults;
}
public boolean isReference() {
return reference;
}
public void setReference(boolean reference) {
this.reference = reference;
}
public boolean isInvertReference() {
return invertReference;
}
@@ -150,12 +128,12 @@ public class MatchVariableConstraint extends NamedScriptableDefinition {
this.invertReference = invertReference;
}
public String getNameOfReferenceVar() {
return nameOfReferenceVar;
public String getReferenceConstraint() {
return referenceConstraint;
}
public void setNameOfReferenceVar(String nameOfReferenceVar) {
this.nameOfReferenceVar = nameOfReferenceVar;
public void setReferenceConstraint(String nameOfReferenceVar) {
this.referenceConstraint = nameOfReferenceVar;
}
public boolean isStrictlyWithinHierarchy() {
@@ -239,13 +217,12 @@ public class MatchVariableConstraint extends NamedScriptableDefinition {
if (maxCount != matchVariableConstraint.maxCount) return false;
if (minCount != matchVariableConstraint.minCount) return false;
if (partOfSearchResults != matchVariableConstraint.partOfSearchResults) return false;
if (reference != matchVariableConstraint.reference) return false;
if (strictlyWithinHierarchy != matchVariableConstraint.strictlyWithinHierarchy) return false;
if (wholeWordsOnly != matchVariableConstraint.wholeWordsOnly) return false;
if (withinHierarchy != matchVariableConstraint.withinHierarchy) return false;
if (!nameOfExprType.equals(matchVariableConstraint.nameOfExprType)) return false;
if (!nameOfFormalArgType.equals(matchVariableConstraint.nameOfFormalArgType)) return false;
if (!nameOfReferenceVar.equals(matchVariableConstraint.nameOfReferenceVar)) return false;
if (!referenceConstraint.equals(matchVariableConstraint.referenceConstraint)) return false;
if (!regExp.equals(matchVariableConstraint.regExp)) return false;
if (!withinConstraint.equals(matchVariableConstraint.withinConstraint)) return false;
if (!containsConstraint.equals(matchVariableConstraint.containsConstraint)) return false;
@@ -256,8 +233,7 @@ public class MatchVariableConstraint extends NamedScriptableDefinition {
}
public int hashCode() {
int result;
result = super.hashCode();
int result = super.hashCode();
result = 29 * result + regExp.hashCode();
result = 29 * result + (invertRegExp ? 1 : 0);
result = 29 * result + (withinHierarchy ? 1 : 0);
@@ -266,9 +242,8 @@ public class MatchVariableConstraint extends NamedScriptableDefinition {
result = 29 * result + minCount;
result = 29 * result + maxCount;
result = 29 * result + (greedy ? 1 : 0);
result = 29 * result + (reference ? 1 : 0);
result = 29 * result + (invertReference ? 1 : 0);
result = 29 * result + nameOfReferenceVar.hashCode();
result = 29 * result + referenceConstraint.hashCode();
result = 29 * result + (partOfSearchResults ? 1 : 0);
result = 29 * result + nameOfExprType.hashCode();
result = 29 * result + (invertExprType ? 1 : 0);
@@ -284,99 +259,39 @@ public class MatchVariableConstraint extends NamedScriptableDefinition {
return result;
}
@Override
public void readExternal(Element element) {
super.readExternal(element);
Attribute attribute;
attribute = element.getAttribute(REGEXP);
Attribute attribute = element.getAttribute(REGEXP);
if (attribute != null) {
regExp = attribute.getValue();
}
withinHierarchy = readBoolean(element, WITHIN_HIERARCHY);
invertRegExp = readBoolean(element, NEGATE_NAME_CONDITION);
wholeWordsOnly = readBoolean(element, WHOLE_WORDS_ONLY);
attribute = element.getAttribute(NAME_OF_EXPRTYPE);
if (attribute != null) {
nameOfExprType = attribute.getValue();
}
exprTypeWithinHierarchy = readBoolean(element, EXPRTYPE_WITHIN_HIERARCHY);
invertExprType = readBoolean(element, NEGATE_EXPRTYPE_CONDITION);
attribute = element.getAttribute(NAME_OF_FORMALTYPE);
if (attribute != null) {
nameOfFormalArgType = attribute.getValue();
}
attribute = element.getAttribute(NAME_OF_REFEENCE_VAR);
if (attribute != null) {
nameOfReferenceVar = attribute.getValue();
}
attribute = element.getAttribute(WITHIN_HIERARCHY);
if (attribute != null) {
try {
withinHierarchy = attribute.getBooleanValue();
}
catch (DataConversionException ex) {
}
}
attribute = element.getAttribute(EXPRTYPE_WITHIN_HIERARCHY);
if (attribute != null) {
try {
exprTypeWithinHierarchy = attribute.getBooleanValue();
}
catch (DataConversionException ex) {
}
}
attribute = element.getAttribute(FORMALTYPE_WITHIN_HIERARCHY);
if (attribute != null) {
try {
formalArgTypeWithinHierarchy = attribute.getBooleanValue();
}
catch (DataConversionException ex) {
}
}
attribute = element.getAttribute(NEGATE_NAME_CONDITION);
if (attribute != null) {
try {
invertRegExp = attribute.getBooleanValue();
}
catch (DataConversionException ex) {
}
}
attribute = element.getAttribute(NEGATE_EXPRTYPE_CONDITION);
if (attribute != null) {
try {
invertExprType = attribute.getBooleanValue();
}
catch (DataConversionException ex) {
}
}
attribute = element.getAttribute(NEGATE_FORMALTYPE_CONDITION);
if (attribute != null) {
try {
invertFormalType = attribute.getBooleanValue();
}
catch (DataConversionException ex) {
}
}
attribute = element.getAttribute(TARGET);
if (attribute != null) {
try {
partOfSearchResults = attribute.getBooleanValue();
}
catch (DataConversionException ex) {
}
}
formalArgTypeWithinHierarchy = readBoolean(element, FORMALTYPE_WITHIN_HIERARCHY);
invertFormalType = readBoolean(element, NEGATE_FORMALTYPE_CONDITION);
attribute = element.getAttribute(MIN_OCCURS);
if (attribute != null) {
try {
minCount = attribute.getIntValue();
}
catch (DataConversionException ex) {
catch (DataConversionException ignored) {
}
}
@@ -385,63 +300,61 @@ public class MatchVariableConstraint extends NamedScriptableDefinition {
try {
maxCount = attribute.getIntValue();
}
catch (DataConversionException ex) {
catch (DataConversionException ignored) {
}
}
attribute = element.getAttribute(WHOLE_WORDS_ONLY);
if (attribute != null) {
try {
wholeWordsOnly = attribute.getBooleanValue();
}
catch (DataConversionException ex) {
}
}
attribute = element.getAttribute(NEGATE_WITHIN_CONDITION);
if (attribute != null) {
try {
invertWithinConstraint = attribute.getBooleanValue();
} catch (DataConversionException ex) {}
}
attribute = element.getAttribute(NEGATE_CONTAINS_CONDITION);
if (attribute != null) {
try {
invertContainsConstraint = attribute.getBooleanValue();
} catch (DataConversionException ex) {}
}
attribute = element.getAttribute(REFERENCE_CONDITION);
if (attribute != null) referenceConstraint = attribute.getValue();
invertReference = readBoolean(element, NEGATE_REFERENCE_CONDITION);
attribute = element.getAttribute(CONTAINS_CONDITION);
if(attribute != null) containsConstraint = attribute.getValue();
if (attribute != null) containsConstraint = attribute.getValue();
invertContainsConstraint = readBoolean(element, NEGATE_CONTAINS_CONDITION);
attribute = element.getAttribute(WITHIN_CONDITION);
if(attribute != null) withinConstraint = attribute.getValue();
if (attribute != null) withinConstraint = attribute.getValue();
invertWithinConstraint = readBoolean(element, NEGATE_WITHIN_CONDITION);
partOfSearchResults = readBoolean(element, TARGET);
}
private static boolean readBoolean(Element element, String attributeName) {
final Attribute attribute = element.getAttribute(attributeName);
if (attribute != null) {
try {
return attribute.getBooleanValue();
}
catch (DataConversionException ignored) {}
}
return false;
}
@Override
public void writeExternal(Element element) {
super.writeExternal(element);
if (regExp.length() > 0) element.setAttribute(REGEXP,regExp);
if (nameOfExprType.length() > 0) element.setAttribute(NAME_OF_EXPRTYPE,nameOfExprType);
if (nameOfReferenceVar.length() > 0) element.setAttribute(NAME_OF_REFEENCE_VAR,nameOfReferenceVar);
if (nameOfFormalArgType.length() > 0) element.setAttribute(NAME_OF_FORMALTYPE,nameOfFormalArgType);
if (!regExp.isEmpty()) element.setAttribute(REGEXP, regExp);
if (!nameOfExprType.isEmpty()) element.setAttribute(NAME_OF_EXPRTYPE, nameOfExprType);
if (!referenceConstraint.isEmpty()) element.setAttribute(REFERENCE_CONDITION, referenceConstraint);
if (!nameOfFormalArgType.isEmpty()) element.setAttribute(NAME_OF_FORMALTYPE, nameOfFormalArgType);
if (withinHierarchy) element.setAttribute(WITHIN_HIERARCHY,TRUE);
if (exprTypeWithinHierarchy) element.setAttribute(EXPRTYPE_WITHIN_HIERARCHY,TRUE);
if (formalArgTypeWithinHierarchy) element.setAttribute(FORMALTYPE_WITHIN_HIERARCHY,TRUE);
if (withinHierarchy) element.setAttribute(WITHIN_HIERARCHY, TRUE);
if (exprTypeWithinHierarchy) element.setAttribute(EXPRTYPE_WITHIN_HIERARCHY, TRUE);
if (formalArgTypeWithinHierarchy) element.setAttribute(FORMALTYPE_WITHIN_HIERARCHY, TRUE);
if (minCount!=1) element.setAttribute(MIN_OCCURS,String.valueOf(minCount));
if (maxCount!=1) element.setAttribute(MAX_OCCURS,String.valueOf(maxCount));
if (partOfSearchResults) element.setAttribute(TARGET,TRUE);
if (minCount != 1) element.setAttribute(MIN_OCCURS,String.valueOf(minCount));
if (maxCount != 1) element.setAttribute(MAX_OCCURS,String.valueOf(maxCount));
if (partOfSearchResults) element.setAttribute(TARGET, TRUE);
if (invertRegExp) element.setAttribute(NEGATE_NAME_CONDITION,TRUE);
if (invertExprType) element.setAttribute(NEGATE_EXPRTYPE_CONDITION,TRUE);
if (invertFormalType) element.setAttribute(NEGATE_FORMALTYPE_CONDITION,TRUE);
if (invertRegExp) element.setAttribute(NEGATE_NAME_CONDITION, TRUE);
if (invertExprType) element.setAttribute(NEGATE_EXPRTYPE_CONDITION, TRUE);
if (invertFormalType) element.setAttribute(NEGATE_FORMALTYPE_CONDITION, TRUE);
if (invertReference) element.setAttribute(NEGATE_REFERENCE_CONDITION, TRUE);
if (wholeWordsOnly) element.setAttribute(WHOLE_WORDS_ONLY,TRUE);
if (invertContainsConstraint) element.setAttribute(NEGATE_CONTAINS_CONDITION,TRUE);
if (invertWithinConstraint) element.setAttribute(NEGATE_WITHIN_CONDITION,TRUE);
if (wholeWordsOnly) element.setAttribute(WHOLE_WORDS_ONLY, TRUE);
if (invertContainsConstraint) element.setAttribute(NEGATE_CONTAINS_CONDITION, TRUE);
if (invertWithinConstraint) element.setAttribute(NEGATE_WITHIN_CONDITION, TRUE);
element.setAttribute(WITHIN_CONDITION, withinConstraint);
element.setAttribute(CONTAINS_CONDITION, containsConstraint);
}
@@ -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.structuralsearch;
import com.intellij.dupLocator.iterators.ArrayBackedNodeIterator;
@@ -28,6 +14,7 @@ import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentIterator;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
@@ -42,6 +29,7 @@ import com.intellij.structuralsearch.impl.matcher.iterators.SingleNodeIterator;
import com.intellij.structuralsearch.impl.matcher.iterators.SsrFilteringNodeIterator;
import com.intellij.structuralsearch.impl.matcher.strategies.MatchingStrategy;
import com.intellij.structuralsearch.plugin.ui.Configuration;
import com.intellij.structuralsearch.plugin.ui.ConfigurationManager;
import com.intellij.structuralsearch.plugin.util.CollectingMatchResultSink;
import com.intellij.structuralsearch.plugin.util.DuplicateFilteringResultSink;
import com.intellij.util.IncorrectOperationException;
@@ -51,16 +39,17 @@ import com.intellij.util.indexing.FileBasedIndex;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* This class makes program structure tree matching:
*/
public class Matcher {
private static final Logger LOG = Logger.getInstance("#com.intellij.structuralsearch.impl.matcher.MatcherImpl");
@SuppressWarnings("SSBasedInspection")
private static final ThreadLocal<Set<String>> ourRecursionGuard = ThreadLocal.withInitial(() -> new HashSet<>());
// project being worked on
private final Project project;
private final DumbService myDumbService;
@@ -93,6 +82,36 @@ public class Matcher {
myDumbService = DumbService.getInstance(project);
}
public static Matcher buildMatcher(Project project, FileType fileType, String constraint) {
if (StringUtil.isQuotedString(constraint)) {
// keep old configurations working, also useful for testing
final MatchOptions myMatchOptions = new MatchOptions();
myMatchOptions.setLooseMatching(true);
myMatchOptions.setFileType(fileType);
myMatchOptions.fillSearchCriteria(StringUtil.unquoteString(constraint));
return new Matcher(project, myMatchOptions);
}
else {
final Set<String> set = ourRecursionGuard.get();
if (!set.add(constraint)) {
throw new MalformedPatternException("Pattern recursively references itself");
}
try {
final Configuration configuration = ConfigurationManager.getInstance(project).findConfigurationByName(constraint);
if (configuration == null) {
throw new MalformedPatternException("Configuration '" + constraint + "' not found");
}
return new Matcher(project, configuration.getMatchOptions());
} finally {
set.remove(constraint);
if (set.isEmpty()) {
// we're finished with this thread local
ourRecursionGuard.remove();
}
}
}
}
static class LastMatchData {
CompiledPattern lastPattern;
MatchOptions lastOptions;
@@ -140,7 +159,8 @@ public class Matcher {
}
}
public void processMatchesInElement(MatchContext context, Configuration configuration,
public void processMatchesInElement(MatchContext context,
Configuration configuration,
NodeIterator matchedNodes,
PairProcessor<MatchResult, Configuration> processor) {
try {
@@ -153,6 +173,18 @@ public class Matcher {
}
}
public boolean matchNode(PsiElement element) {
final CollectingMatchResultSink sink = new CollectingMatchResultSink();
final MatchOptions options = matchContext.getOptions();
final CompiledPattern compiledPattern = prepareMatching(sink, options);
if (compiledPattern == null) {
return false;
}
matchContext.setShouldRecursivelyMatch(false);
visitor.matchContext(new SingleNodeIterator(element));
return !sink.getMatches().isEmpty();
}
public void clearContext() {
matchContext.clear();
}
@@ -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.structuralsearch.impl.matcher.compiler;
import com.intellij.codeInsight.template.Template;
@@ -358,7 +344,7 @@ public class PatternCompiler {
final StringBuilder buf = new StringBuilder();
Template template = TemplateManager.getInstance(project).createTemplate("","",options.getSearchPattern());
Template template = TemplateManager.getInstance(project).createTemplate("", "", options.getSearchPattern());
int segmentsCount = template.getSegmentsCount();
String text = template.getTemplateText();
@@ -373,7 +359,7 @@ public class PatternCompiler {
throw new MalformedPatternException();
}
buf.append(text.substring(prevOffset,offset));
buf.append(text.substring(prevOffset, offset));
buf.append(prefix);
buf.append(name);
@@ -381,7 +367,7 @@ public class PatternCompiler {
if (constraint==null) {
// we do not edited the constraints
constraint = new MatchVariableConstraint();
constraint.setName( name );
constraint.setName(name);
options.addVariableConstraint(constraint);
}
@@ -415,16 +401,16 @@ public class PatternCompiler {
if (constraint.isInvertRegExp()) {
predicate = new NotPredicate(predicate);
}
addPredicate(handler,predicate);
addPredicate(handler, predicate);
}
if (constraint.isReference()) {
predicate = new ReferencePredicate( constraint.getNameOfReferenceVar() );
if (!StringUtil.isEmptyOrSpaces(constraint.getReferenceConstraint())) {
predicate = new ReferencePredicate(constraint.getReferenceConstraint(), options.getFileType(), project);
if (constraint.isInvertReference()) {
predicate = new NotPredicate(predicate);
}
addPredicate(handler,predicate);
addPredicate(handler, predicate);
}
addExtensionPredicates(options, constraint, handler);
@@ -435,7 +421,7 @@ public class PatternCompiler {
if (constraint.isInvertContainsConstraint()) {
predicate = new NotPredicate(predicate);
}
addPredicate(handler,predicate);
addPredicate(handler, predicate);
}
if (!StringUtil.isEmptyOrSpaces(constraint.getWithinConstraint())) {
@@ -461,7 +447,7 @@ public class PatternCompiler {
if (constraint.isInvertWithinConstraint()) {
predicate = new NotPredicate(predicate);
}
addPredicate(handler,predicate);
addPredicate(handler, predicate);
}
addExtensionPredicates(options, constraint, handler);
@@ -352,11 +352,9 @@ public class StringToConstraintsTransformer {
boolean invert) {
argument = argument.trim();
if (option.equalsIgnoreCase(REF)) {
constraint.setReference(true);
if (argument.isEmpty()) throw new MalformedPatternException(SSRBundle.message("error.pattern.argument.expected", option));
constraint.setReferenceConstraint(argument);
constraint.setInvertReference(invert);
if (argument.isEmpty() || argument.charAt(0) != '\'')
throw new MalformedPatternException(SSRBundle.message("error.reference.variable.name.expected", option));
constraint.setNameOfReferenceVar(argument.substring(1));
}
else if (option.equalsIgnoreCase(REGEX) || option.equalsIgnoreCase(REGEXW)) {
if (argument.isEmpty()) throw new MalformedPatternException(SSRBundle.message("error.regular.expression.argument.expected", option));
@@ -1,47 +1,29 @@
/*
* 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.s
package com.intellij.structuralsearch.impl.matcher.predicates;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.structuralsearch.StructuralSearchProfile;
import com.intellij.psi.PsiReference;
import com.intellij.structuralsearch.Matcher;
import com.intellij.structuralsearch.StructuralSearchUtil;
import com.intellij.structuralsearch.impl.matcher.MatchContext;
import com.intellij.structuralsearch.impl.matcher.MatchResultImpl;
import com.intellij.structuralsearch.impl.matcher.MatchUtils;
import com.intellij.structuralsearch.plugin.util.SmartPsiPointer;
public final class ReferencePredicate extends MatchPredicate {
private final String myName;
private final Matcher matcher;
public ReferencePredicate(String name) {
myName = name;
public ReferencePredicate(String constraint, FileType fileType, Project project) {
matcher = Matcher.buildMatcher(project, fileType, constraint);
}
@Override
public boolean match(PsiElement match, int start, int end, MatchContext context) {
match = StructuralSearchUtil.getParentIfIdentifier(match);
final PsiElement target = MatchUtils.getReferencedElement(match);
if (target != null) {
final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByPsiElement(target);
assert profile != null;
final String image = profile.getText(target, 0, -1);
context.getResult().addSon(new MatchResultImpl(myName, image, new SmartPsiPointer(target), true));
public boolean match(PsiElement matchedNode, int start, int end, MatchContext context) {
matchedNode = StructuralSearchUtil.getParentIfIdentifier(matchedNode);
if (!(matchedNode instanceof PsiReference)) {
return false;
}
return true;
final PsiElement target = ((PsiReference)matchedNode).resolve();
return target != null && matcher.matchNode(target);
}
}
@@ -3,58 +3,23 @@ package com.intellij.structuralsearch.impl.matcher.predicates;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.structuralsearch.MalformedPatternException;
import com.intellij.structuralsearch.MatchOptions;
import com.intellij.structuralsearch.MatchResult;
import com.intellij.structuralsearch.Matcher;
import com.intellij.structuralsearch.impl.matcher.MatchContext;
import com.intellij.structuralsearch.plugin.ui.Configuration;
import com.intellij.structuralsearch.plugin.ui.ConfigurationManager;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author Maxim.Mossienko
*/
public class WithinPredicate extends MatchPredicate {
@SuppressWarnings("SSBasedInspection")
private static final ThreadLocal<Set<String>> ourRecursionGuard = ThreadLocal.withInitial(() -> new HashSet<>());
private final Matcher matcher;
public WithinPredicate(String within, FileType fileType, Project project) {
if (StringUtil.isQuotedString(within)) {
// keep old configurations working
final MatchOptions myMatchOptions = new MatchOptions();
myMatchOptions.setLooseMatching(true);
myMatchOptions.setFileType(fileType);
myMatchOptions.fillSearchCriteria(StringUtil.unquoteString(within));
matcher = new Matcher(project, myMatchOptions);
}
else {
final Set<String> set = ourRecursionGuard.get();
if (!set.add(within)) {
throw new MalformedPatternException("Pattern recursively contained within itself");
}
try {
final Configuration configuration = ConfigurationManager.getInstance(project).findConfigurationByName(within);
if (configuration == null) {
throw new MalformedPatternException("Configuration '" + within + "' not found");
}
matcher = new Matcher(project, configuration.getMatchOptions());
} finally {
set.remove(within);
if (set.isEmpty()) {
// we're finished with this thread local
ourRecursionGuard.remove();
}
}
}
matcher = Matcher.buildMatcher(project, fileType, within);
}
@Override
@@ -1,14 +1,15 @@
// 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.structuralsearch.plugin.util;
import com.intellij.structuralsearch.DefaultMatchResultSink;
import com.intellij.structuralsearch.MatchResult;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class CollectingMatchResultSink extends DefaultMatchResultSink {
private final List<MatchResult> matches = new ArrayList<>();
private final List<MatchResult> matches = new SmartList<>();
@Override
public void newMatch(MatchResult result) {
@@ -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.structuralsearch;
import com.intellij.openapi.fileTypes.StdFileTypes;
@@ -1289,19 +1275,31 @@ public class StructuralSearchTest extends StructuralSearchTestCase {
}
public void testHierarchy() {
final String s105 = "class B {} class A extends B { }";
assertEquals("extends match", 1, findMatchesCount(s105, "class '_ extends '_:[ref('T)] {}"));
final String s105 = "class B {} class A extends B { } class C {} class D extends C {}";
assertEquals("extends match", 1, findMatchesCount(s105, "class '_ extends '_:[ref( \"class B {}\" )] {}"));
final String s107 = "interface IA {} interface IB extends IA { } interface IC extends IB {} interface ID extends IC {}" +
"class A implements IA {} class B extends A { } class C extends B implements IC {} class D extends C {}";
assertEquals("extends navigation match", 2, findMatchesCount(s107, "class '_ extends 'Type:+A {}"));
assertEquals("implements navigation match", 3, findMatchesCount(s107, "class '_ implements 'Type:+IA {}"));
final String s109 = "interface I {} interface I2 extends I {} class A implements I2 {} class B extends A { } class C extends B {} class D { void e() { C c; B b; A a;} }";
final String s109 = "interface I {}" +
"interface I2 extends I {}" +
"class A implements I2 {}" +
"class B extends A {}" +
"class C extends B {}" +
"class D {" +
" void e() {" +
" D d;" +
" C c;" +
" B b;" +
" A a;" +
" }" +
"}";
assertEquals("extends navigation match in definition", 3, findMatchesCount(s109, "'_:*A '_;"));
assertEquals("implements navigation match in definition 2", 3, findMatchesCount(s109, "'_:*I '_;"));
assertEquals("implements navigation match in definition 2 with nested conditions", 1,
findMatchesCount(s109, "'_:*[regex( I ) && ref('T)] '_;"));
assertEquals("implements navigation match in definition 2 with nested conditions", 3,
findMatchesCount(s109, "'_:[ref( \"class '_A:*[regex( I )] {}\" )] '_;"));
try {
findMatchesCount(s109, "'_:*[regex( I ) && ref2('T)] '_;");
fail("implements navigation match in definition 2 with nested conditions - incorrect cond");
@@ -1408,19 +1406,33 @@ public class StructuralSearchTest extends StructuralSearchTestCase {
assertEquals("body of method by block search", 2, findMatchesCount(s79, " { '_T 'T3 = '_T2?; '_*; } "));
assertEquals("first matches, next not", 2, findMatchesCount(s95, " class '_ {private int 'T+:field.* ;}"));
final String s97 = "class A { int c; void b() { C d; } } class C { C() { A a; a.b(); a.c=1; } }";
assertEquals("method predicate match", 1, findMatchesCount(s97, "'_.'_:[ref('T)] ()"));
assertEquals("field predicate match", 1, findMatchesCount(s97, "'_.'_:[ref('T)]"));
assertEquals("dcl predicate match", 1, findMatchesCount(s97, "'_:[ref('T)].'_ ();"));
final String s97 = "class A {" +
" int c;" +
" int d;" +
" void b(){}" +
" void x(){" +
" C d;" +
" }" +
"}" +
"class C {" +
" C() {" +
" A a;" +
" A z;" +
" z.b();" +
" a.x();" +
" z.c=1;" +
" a.d=2;" +
" }" +
"}";
assertEquals("method predicate match", 1, findMatchesCount(s97, "'_.'_:[ref( \"void b(){}\" )] ()"));
assertEquals("field predicate match", 1, findMatchesCount(s97, "'_.'_:[ref( \"int c;\" )]"));
assertEquals("dcl predicate match", 1, findMatchesCount(s97, "'_:[ref( \"A a;\" )].'_ ();"));
final String s99 = "class X {{ char s = '\\u1111'; char s1 = '\\n'; }}";
assertEquals("char constants in pattern", 1, findMatchesCount(s99, " char 'var = '\\u1111'; "));
assertEquals("char constants in pattern 2", 1, findMatchesCount(s99, " char 'var = '\\n'; "));
assertEquals("class predicate match (from definition)", 3, findMatchesCount(s97, "'_:[ref('T)] '_;"));
final String s101 = "class A { void b() { String d; String e; String[] f; f.length=1; f.length=1; } }";
assertEquals("distinct match", 1, findMatchesCount(s101, "'_:[ref('T)] '_;"));
assertEquals("class predicate match (from definition)", 3, findMatchesCount(s97, "'_:[ref( \"class '_A{}\" )] '_;"));
String s107 = "class A {\n" +
" /* */\n" +
@@ -2667,4 +2679,21 @@ public class StructuralSearchTest extends StructuralSearchTestCase {
assertEquals(1, findMatchesCount(source6, "'x:[exprtype( List<List<String>> )]"));
assertEquals(2, findMatchesCount(source6, "'x:[exprtype( List<Garbage> )]"));
}
public void testSearchReferences() {
String source = "class X {" +
" @Deprecated" +
" void a() {}" +
" void b() {}" +
" void c() {" +
" a();" +
" b();" +
" b();" +
" }" +
"}";
assertEquals("find calls to deprecated methods", 1,
findMatchesCount(source, "'_instance?.'_call:[ref( \"@Deprecated void '_x();\" )] ()"));
assertEquals("find calls to non-deprecated methods", 2,
findMatchesCount(source, "'_instance?.'_call:[ref( \"@'_Anno{0,0} void '_x();\" )] ()"));
}
}