IDEA-227219 Dataflow to here: filter by target value

GitOrigin-RevId: 4509bbb852b61262ac4160bebe31fd93976da286
This commit is contained in:
Tagir Valeev
2020-05-05 04:30:49 +00:00
committed by intellij-monorepo-bot
parent c54b3f79b3
commit 39ca5a8e57
22 changed files with 393 additions and 52 deletions
@@ -0,0 +1,40 @@
// Copyright 2000-2020 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.slicer;
import com.intellij.codeInspection.dataFlow.CommonDataflow;
import com.intellij.codeInspection.dataFlow.types.DfConstantType;
import com.intellij.codeInspection.dataFlow.types.DfType;
import com.intellij.codeInspection.dataFlow.types.DfTypes;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiLiteralValue;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public class JavaDfaSliceValueFilter implements SliceValueFilter {
private final @NotNull DfType myDfType;
public JavaDfaSliceValueFilter(@NotNull DfType type) {
myDfType = type;
}
@Override
public boolean allowed(PsiElement element) {
if (myDfType instanceof DfConstantType && element instanceof PsiLiteralValue) {
Object constValue = ((DfConstantType<?>)myDfType).getValue();
if (!(constValue instanceof PsiElement)) {
Object value = ((PsiLiteralValue)element).getValue();
return Objects.equals(value, constValue);
}
}
if (!(element instanceof PsiExpression)) return true;
DfType dfType = CommonDataflow.getDfType((PsiExpression)element);
return dfType.meet(myDfType) != DfTypes.BOTTOM;
}
@Override
public String toString() {
return myDfType.toString();
}
}
@@ -15,11 +15,18 @@
*/
package com.intellij.slicer;
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
import com.intellij.codeInspection.dataFlow.types.DfTypes;
import com.intellij.codeInspection.dataFlow.value.RelationType;
import com.intellij.ide.util.treeView.AbstractTreeStructure;
import com.intellij.java.JavaBundle;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.ig.psiutils.ExpressionUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -103,4 +110,95 @@ public class JavaSliceProvider implements SliceLanguageSupportProvider, SliceUsa
actionGroup.add(new CanItBeNullAction(sliceTreeBuilder));
}
}
@Override
public boolean supportValueFilters(@NotNull PsiElement expression) {
PsiType type = getType(expression);
return type != null && !PsiType.VOID.equals(type) && !PsiType.NULL.equals(type);
}
@Override
public @NotNull SliceValueFilter parseFilter(@NotNull PsiElement expression, @NotNull String filter) throws SliceFilterParseException {
PsiType type = getType(expression);
if (type == null) {
return SliceLanguageSupportProvider.super.parseFilter(expression, filter);
}
if (filter.equals("null")) {
if (type instanceof PsiPrimitiveType) {
throw new SliceFilterParseException(
JavaBundle.message("slice.filter.parse.error.null.filter.not.applicable.for.primitive.type", type.getPresentableText()));
}
return new JavaDfaSliceValueFilter(DfTypes.NULL);
}
if (filter.equals("!null")) {
if (type instanceof PsiPrimitiveType) {
throw new SliceFilterParseException(
JavaBundle.message("slice.filter.parse.error.not.null.filter.not.applicable.for.primitive.type", type.getPresentableText()));
}
return new JavaDfaSliceValueFilter(DfTypes.NOT_NULL_OBJECT);
}
RelationType relationType = RelationType.EQ;
if (PsiType.BYTE.equals(type) ||
PsiType.CHAR.equals(type) ||
PsiType.SHORT.equals(type) ||
PsiType.INT.equals(type) ||
PsiType.LONG.equals(type)) {
for (RelationType relType : RelationType.values()) {
if (filter.startsWith(relType.toString())) {
relationType = relType;
filter = filter.substring(relType.toString().length()).trim();
break;
}
}
}
PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(type);
if (psiClass != null && psiClass.isEnum()) {
PsiField enumConstant = psiClass.findFieldByName(filter, false);
if (enumConstant instanceof PsiEnumConstant) {
return new JavaDfaSliceValueFilter(DfTypes.constant(enumConstant, type));
} else {
throw new SliceFilterParseException(JavaBundle.message("slice.filter.parse.error.enum.constant.not.found", filter));
}
}
PsiExpression constant;
try {
constant = JavaPsiFacade.getElementFactory(expression.getProject()).createExpressionFromText(filter, expression);
}
catch (IncorrectOperationException ignore) {
throw new SliceFilterParseException(JavaBundle.message("slice.filter.parse.error.incorrect.expression", filter));
}
PsiType constantType = constant.getType();
if (constantType == null || !type.isAssignableFrom(constantType)) {
throw new SliceFilterParseException(JavaBundle.message("slice.filter.parse.error.incorrect.constant.type", type.getPresentableText()));
}
Object o = ExpressionUtils.computeConstantExpression(constant);
if (o == null) {
throw new SliceFilterParseException(JavaBundle.message("slice.filter.parse.error.expression.must.evaluate.to.constant", filter));
}
if (relationType != RelationType.EQ) {
if (!(o instanceof Number)) {
throw new SliceFilterParseException(JavaBundle.message("slice.filter.parse.error.incorrect.constant.expected.number", filter));
}
if (PsiType.LONG.equals(type)) {
LongRangeSet rangeSet = LongRangeSet.point(((Number)o).longValue()).fromRelation(relationType);
return new JavaDfaSliceValueFilter(DfTypes.longRange(rangeSet));
}
LongRangeSet rangeSet = LongRangeSet.point(((Number)o).intValue()).fromRelation(relationType);
return new JavaDfaSliceValueFilter(DfTypes.intRangeClamped(rangeSet));
}
return new JavaDfaSliceValueFilter(DfTypes.constant(o, type));
}
private static @Nullable PsiType getType(@NotNull PsiElement expression) {
if (expression instanceof PsiExpression) {
return ((PsiExpression)expression).getType();
}
else if (expression instanceof PsiVariable) {
return ((PsiVariable)expression).getType();
}
else if (expression instanceof PsiMethod) {
return ((PsiMethod)expression).getReturnType();
}
return null;
}
}
@@ -0,0 +1,21 @@
class Test {
void test(int <flown1>x) {
System.out.println(<caret>x);
}
void use(int <flown111>x) {
if (x > 0) {
throw new IllegalArgumentException();
}
test(<flown11>x);
}
void use2(int x) {
if (x >= 0) {
throw new IllegalArgumentException();
}
test(x);
test(<flown12>12);
test(-12);
}
}
@@ -0,0 +1,15 @@
class Test {
void test(String <flown1>x) {
System.out.println(<caret>x);
}
void use(String <flown111>x) {
test(<flown11>x);
System.out.println(x.trim());
test(x);
}
void use2(String x) {
test(x.trim());
}
}
@@ -21,6 +21,7 @@ import com.intellij.openapi.editor.RangeMarker;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.slicer.*;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Map;
@@ -30,10 +31,14 @@ import java.util.Map;
*/
public class SliceBackwardTest extends SliceTestCase {
private void doTest() throws Exception {
doTest("");
}
private void doTest(@NotNull String filter) throws Exception {
configureByFile("/codeInsight/slice/backward/"+getTestName(false)+".java");
Map<String, RangeMarker> sliceUsageName2Offset = SliceTestUtil.extractSliceOffsetsFromDocument(getEditor().getDocument());
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
PsiElement element = new SliceHandler(true).getExpressionAtCaret(getEditor(), getFile());
PsiElement element = SliceHandler.create(true).getExpressionAtCaret(getEditor(), getFile());
assertNotNull(element);
SliceTestUtil.Node tree = SliceTestUtil.buildTree(element, sliceUsageName2Offset);
Collection<HighlightInfo> errors = highlightErrors();
@@ -41,8 +46,12 @@ public class SliceBackwardTest extends SliceTestCase {
SliceAnalysisParams params = new SliceAnalysisParams();
params.scope = new AnalysisScope(getProject());
params.dataFlowToThis = true;
SliceLanguageSupportProvider provider = LanguageSlicing.getProvider(element);
if (!filter.isEmpty()) {
params.valueFilter = provider.parseFilter(element, filter);
}
SliceUsage usage = LanguageSlicing.getProvider(element).createRootUsage(element, params);
SliceUsage usage = provider.createRootUsage(element, params);
SliceTestUtil.checkUsages(usage, tree);
}
@@ -83,4 +92,6 @@ public class SliceBackwardTest extends SliceTestCase {
public void testSearchOverriddenMethodsInThisClassHierarchy() throws Exception { doTest();}
public void testAppend() throws Exception { doTest();}
public void testRequireNonNull() throws Exception { doTest();}
public void testFilterIntRange() throws Exception { doTest(">=0");}
public void testFilterNull() throws Exception { doTest("null");}
}
@@ -33,7 +33,7 @@ public class SliceForwardTest extends SliceTestCase {
configureByFile("/codeInsight/slice/forward/"+getTestName(false)+".java");
Map<String, RangeMarker> sliceUsageName2Offset = SliceTestUtil.extractSliceOffsetsFromDocument(getEditor().getDocument());
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
PsiElement element = new SliceForwardHandler().getExpressionAtCaret(getEditor(), getFile());
PsiElement element = SliceHandler.create(false).getExpressionAtCaret(getEditor(), getFile());
assertNotNull(element);
SliceTestUtil.Node tree = SliceTestUtil.buildTree(element, sliceUsageName2Offset);
Collection<HighlightInfo> errors = highlightErrors();
@@ -23,7 +23,7 @@ public class SliceTreeTest extends SliceTestCase {
private SliceTreeStructure configureTree(@NonNls final String name) throws Exception {
configureByFile("/codeInsight/slice/backward/"+ name +".java");
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
PsiElement element = new SliceHandler(true).getExpressionAtCaret(getEditor(), getFile());
PsiElement element = SliceHandler.create(true).getExpressionAtCaret(getEditor(), getFile());
assertNotNull(element);
Collection<HighlightInfo> errors = highlightErrors();
assertEmpty(errors);
@@ -1235,4 +1235,11 @@ inspection.excessive.range.check.display.name=Excessive range check
inspection.condition.covered.by.further.condition.display.name=Condition is covered by further condition
inspection.move.field.assignment.to.initializer.display.name=Field assignment can be moved to initializer
inspection.test.failed.line.display.name=Highlight problem line in test
inspection.frequently.used.inheritor.inspection.display.name=Class may extend a commonly used base class
inspection.frequently.used.inheritor.inspection.display.name=Class may extend a commonly used base class
slice.filter.parse.error.null.filter.not.applicable.for.primitive.type=''null'' filter is not applicable for primitive type {0}
slice.filter.parse.error.not.null.filter.not.applicable.for.primitive.type=''!null'' filter is not applicable for primitive type {0}
slice.filter.parse.error.enum.constant.not.found=Enum constant not found: {0}
slice.filter.parse.error.incorrect.expression=Incorrect expression: {0}
slice.filter.parse.error.incorrect.constant.type=Incorrect constant type (required: {0})
slice.filter.parse.error.expression.must.evaluate.to.constant=Expression must evaluate to a constant: {0}
slice.filter.parse.error.incorrect.constant.expected.number=Incorrect constant (expected number): {0}
@@ -301,3 +301,6 @@ action.UpDownAction.up.text=Up
action.UpDownAction.down.text=Down
button.override=Override
button.do.not.override=Do Not Override
label.filter.value=Filter value:
slice.filter.not.supported=Filtering is not supported
slice.analysis.title.filter=(filter: {0})
@@ -16,11 +16,22 @@
package com.intellij.slicer;
import com.intellij.analysis.AnalysisScope;
import org.jetbrains.annotations.Nullable;
public class SliceAnalysisParams {
public boolean dataFlowToThis = true; // to/from this
public boolean showInstanceDereferences = true; // show method calls or field access on the variable being analysed
/**
* Direction of flow: true = backward; false = forward
*/
public boolean dataFlowToThis = true;
/**
* show method calls or field access on the variable being analysed
*/
public boolean showInstanceDereferences = true;
public AnalysisScope scope;
/**
* If present filters the occurrences
*/
public @Nullable SliceValueFilter valueFilter;
public SliceAnalysisParams() {
}
@@ -30,7 +30,7 @@ public class SliceBackwardAction extends CodeInsightAction {
@NotNull
@Override
protected SliceHandler getHandler() {
return new SliceHandler(true);
return SliceHandler.create(true);
}
@Override
@@ -0,0 +1,102 @@
// Copyright 2000-2020 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.slicer;
import com.intellij.analysis.AnalysisScope;
import com.intellij.analysis.AnalysisUIOptions;
import com.intellij.analysis.BaseAnalysisActionDialog;
import com.intellij.analysis.dialog.ModelScopeItem;
import com.intellij.lang.LangBundle;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBTextField;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.util.List;
class SliceBackwardHandler extends SliceHandler {
SliceBackwardHandler() {
super(true);
}
@Override
public SliceAnalysisParams askForParams(PsiElement element,
SliceManager.StoredSettingsBean storedSettingsBean,
String dialogTitle) {
AnalysisScope analysisScope = new AnalysisScope(element.getContainingFile());
Module module = ModuleUtilCore.findModuleForPsiElement(element);
Project myProject = element.getProject();
AnalysisUIOptions analysisUIOptions = new AnalysisUIOptions();
analysisUIOptions.loadState(storedSettingsBean.analysisUIOptions);
List<ModelScopeItem> items = BaseAnalysisActionDialog.standardItems(myProject, analysisScope, module, element);
SliceLanguageSupportProvider provider = LanguageSlicing.getProvider(element);
boolean supportFilter = provider.supportValueFilters(element);
class BackwardHandlerDialog extends BaseAnalysisActionDialog {
JBTextField field;
BackwardHandlerDialog() {
super(dialogTitle, "Analyze scope", myProject, items, analysisUIOptions, true);
}
@Override
protected @Nullable JComponent getAdditionalActionSettings(Project project) {
if (!supportFilter) return null;
JPanel panel = new JPanel(new GridBagLayout());
JBLabel label = new JBLabel(LangBundle.message("label.filter.value") + " ");
panel.add(label);
field = new JBTextField();
Dimension size = field.getPreferredSize();
size.width = 400;
field.setPreferredSize(size);
panel.add(field);
label.setLabelFor(field);
field.getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(@NotNull DocumentEvent e) {
try {
getFilter();
setErrorText(null, field);
}
catch (SliceFilterParseException exception) {
setErrorText(exception.getMessage(), field);
}
}
});
return panel;
}
private @Nullable SliceValueFilter getFilter() throws SliceFilterParseException {
String text = field.getText().trim();
if (!text.isEmpty()) {
return provider.parseFilter(element, text);
}
return null;
}
}
BackwardHandlerDialog dialog = new BackwardHandlerDialog();
if (!dialog.showAndGet()) {
return null;
}
AnalysisScope scope = dialog.getScope(analysisScope);
storedSettingsBean.analysisUIOptions.loadState(analysisUIOptions);
SliceAnalysisParams params = new SliceAnalysisParams();
params.scope = scope;
params.dataFlowToThis = myDataFlowToThis;
try {
params.valueFilter = dialog.getFilter();
}
catch (SliceFilterParseException ignored) { }
return params;
}
}
@@ -0,0 +1,10 @@
// Copyright 2000-2020 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.slicer;
import com.intellij.openapi.util.NlsContexts;
public class SliceFilterParseException extends Exception {
public SliceFilterParseException(@NlsContexts.DialogMessage String message) {
super(message);
}
}
@@ -31,7 +31,7 @@ public class SliceForwardAction extends CodeInsightAction {
@NotNull
@Override
protected CodeInsightActionHandler getHandler() {
return new SliceForwardHandler();
return SliceHandler.create(false);
}
@Override
@@ -30,13 +30,15 @@ import java.util.List;
/**
* @author cdr
*/
public class SliceForwardHandler extends SliceHandler {
public SliceForwardHandler() {
class SliceForwardHandler extends SliceHandler {
SliceForwardHandler() {
super(false);
}
@Override
public SliceAnalysisParams askForParams(PsiElement element, boolean dataFlowToThis, SliceManager.StoredSettingsBean storedSettingsBean, String dialogTitle) {
public SliceAnalysisParams askForParams(PsiElement element,
SliceManager.StoredSettingsBean storedSettingsBean,
String dialogTitle) {
AnalysisScope analysisScope = new AnalysisScope(element.getContainingFile());
Module module = ModuleUtilCore.findModuleForPsiElement(element);
@@ -66,7 +68,7 @@ public class SliceForwardHandler extends SliceHandler {
SliceAnalysisParams params = new SliceAnalysisParams();
params.scope = scope;
params.dataFlowToThis = dataFlowToThis;
params.dataFlowToThis = myDataFlowToThis;
params.showInstanceDereferences = form.isToShowDerefs();
return params;
}
@@ -3,34 +3,26 @@
*/
package com.intellij.slicer;
import com.intellij.analysis.AnalysisScope;
import com.intellij.analysis.AnalysisUIOptions;
import com.intellij.analysis.BaseAnalysisActionDialog;
import com.intellij.analysis.dialog.ModelScopeItem;
import com.intellij.codeInsight.CodeInsightActionHandler;
import com.intellij.codeInsight.TargetElementUtil;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.lang.LangBundle;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author cdr
*/
public class SliceHandler implements CodeInsightActionHandler {
public abstract class SliceHandler implements CodeInsightActionHandler {
private static final Logger LOG = Logger.getInstance(SliceHandler.class);
private final boolean myDataFlowToThis;
final boolean myDataFlowToThis;
public SliceHandler(boolean dataFlowToThis) {
SliceHandler(boolean dataFlowToThis) {
myDataFlowToThis = dataFlowToThis;
}
@@ -66,34 +58,17 @@ public class SliceHandler implements CodeInsightActionHandler {
PsiElement atCaret = file.findElementAt(offset);
SliceLanguageSupportProvider provider = LanguageSlicing.getProvider(file);
if(provider == null || atCaret == null) {
if (provider == null || atCaret == null) {
return null;
}
return provider.getExpressionAtCaret(atCaret, myDataFlowToThis);
}
public SliceAnalysisParams askForParams(PsiElement element, boolean dataFlowToThis, SliceManager.StoredSettingsBean storedSettingsBean, String dialogTitle) {
AnalysisScope analysisScope = new AnalysisScope(element.getContainingFile());
Module module = ModuleUtilCore.findModuleForPsiElement(element);
public abstract SliceAnalysisParams askForParams(PsiElement element,
SliceManager.StoredSettingsBean storedSettingsBean,
String dialogTitle);
Project myProject = element.getProject();
AnalysisUIOptions analysisUIOptions = new AnalysisUIOptions();
analysisUIOptions.loadState(storedSettingsBean.analysisUIOptions);
List<ModelScopeItem> items = BaseAnalysisActionDialog.standardItems(myProject, analysisScope,
module, element);
BaseAnalysisActionDialog dialog =
new BaseAnalysisActionDialog(dialogTitle, "Analyze scope", myProject, items, analysisUIOptions, true);
if (!dialog.showAndGet()) {
return null;
}
AnalysisScope scope = dialog.getScope(analysisScope);
storedSettingsBean.analysisUIOptions.loadState(analysisUIOptions);
SliceAnalysisParams params = new SliceAnalysisParams();
params.scope = scope;
params.dataFlowToThis = dataFlowToThis;
return params;
public static SliceHandler create(boolean dataFlowToThis) {
return dataFlowToThis ? new SliceBackwardHandler() : new SliceForwardHandler();
}
}
@@ -16,6 +16,7 @@
package com.intellij.slicer;
import com.intellij.ide.util.treeView.AbstractTreeStructure;
import com.intellij.lang.LangBundle;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
@@ -31,6 +32,25 @@ public interface SliceLanguageSupportProvider {
@NotNull
PsiElement getElementForDescription(@NotNull PsiElement element);
/**
* @param expression expression (previously returned from {@link #getExpressionAtCaret(PsiElement, boolean)}.
* @return true if value filters are supported
*/
default boolean supportValueFilters(@NotNull PsiElement expression) {
return false;
}
/**
* @param expression expression (previously returned from {@link #getExpressionAtCaret(PsiElement, boolean)}.
* @param filter user-entered filter string
* @return parsed {@link SliceValueFilter}
* @throws SliceFilterParseException if string cannot be parsed or filtering is not supported
*/
default @NotNull SliceValueFilter parseFilter(@NotNull PsiElement expression, @NotNull String filter)
throws SliceFilterParseException {
throw new SliceFilterParseException(LangBundle.message("slice.filter.not.supported"));
}
@NotNull
SliceUsageCellRendererBase getRenderer();
@@ -3,6 +3,7 @@ package com.intellij.slicer;
import com.intellij.analysis.AnalysisUIOptions;
import com.intellij.ide.impl.ContentManagerWatcher;
import com.intellij.lang.LangBundle;
import com.intellij.openapi.components.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
@@ -62,13 +63,16 @@ public class SliceManager implements PersistentStateComponent<SliceManager.Store
String dialogTitle = getElementDescription((dataFlowToThis ? BACK_TOOLWINDOW_ID : FORTH_TOOLWINDOW_ID) + " ", element, null);
dialogTitle = Pattern.compile("(<style>.*</style>)|<[^<>]*>", Pattern.DOTALL).matcher(dialogTitle).replaceAll("");
SliceAnalysisParams params = handler.askForParams(element, dataFlowToThis, myStoredSettings, StringUtil.unescapeXmlEntities(dialogTitle));
SliceAnalysisParams params = handler.askForParams(element, myStoredSettings, StringUtil.unescapeXmlEntities(dialogTitle));
if (params == null) return;
SliceRootNode rootNode = new SliceRootNode(myProject, new DuplicateMap(),
LanguageSlicing.getProvider(element).createRootUsage(element, params));
createToolWindow(dataFlowToThis, rootNode, false, getElementDescription(null, element, null));
String suffix = null;
if (params.valueFilter != null) {
suffix = " " + StringUtil.escapeXmlEntities(LangBundle.message("slice.analysis.title.filter", params.valueFilter));
}
createToolWindow(dataFlowToThis, rootNode, false, getElementDescription(null, element, suffix));
}
public void createToolWindow(boolean dataFlowToThis, @NotNull SliceRootNode rootNode, boolean splitByLeafExpressions, @NotNull String displayName) {
@@ -81,6 +81,10 @@ public abstract class SliceUsage extends UsageInfo2UsageAdapter {
}) {
@Override
public boolean process(SliceUsage usage) {
SliceValueFilter filter = usage.params.valueFilter;
if (filter != null && !filter.allowed(usage.getElement())) {
return true;
}
return transformToLanguageSpecificUsage(usage).stream().allMatch(super::process);
}
};
@@ -0,0 +1,18 @@
// Copyright 2000-2020 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.slicer;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.Nls;
public interface SliceValueFilter {
/**
* @param element to test
* @return true if this element passes the filter
*/
boolean allowed(PsiElement element);
/**
* @return String representation
*/
@Nls String toString();
}
@@ -51,7 +51,7 @@ abstract class GroovySliceTestCase(private val isDataFlowToThis: Boolean) : Daem
psiDocumentManager.commitAllDocuments()
val element = SliceHandler(isDataFlowToThis).getExpressionAtCaret(editor, file)!!
val element = SliceHandler.create(isDataFlowToThis).getExpressionAtCaret(editor, file)!!
val tree = SliceTestUtil.buildTree(element, sliceUsageName2Offset)
val errors = highlightErrors()
@@ -37,7 +37,7 @@ class GroovySliceTreeGroupingTest : DaemonAnalyzerTestCase() {
configureByFiles(null, *getTestFiles(getTestName(false)).toTypedArray())
PsiDocumentManager.getInstance(project).commitAllDocuments()
val element = SliceHandler(true).getExpressionAtCaret(editor, file)!!
val element = SliceHandler.create(true).getExpressionAtCaret(editor, file)!!
val errors = highlightErrors()
UsefulTestCase.assertEmpty(errors)