mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-227219 Dataflow to here: filter by target value
GitOrigin-RevId: 4509bbb852b61262ac4160bebe31fd93976da286
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c54b3f79b3
commit
39ca5a8e57
@@ -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()
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user