mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-240404 Analyze dataflow to here: stack-trace based scope
GitOrigin-RevId: 33b4b1cdf2198e9c15fc2d52e0113ffe97c3dde9
This commit is contained in:
committed by
intellij-monorepo-bot
parent
19bed5ea77
commit
0c336ffda4
@@ -15,6 +15,7 @@ import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.ClassUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
@@ -318,17 +319,18 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
|
||||
private @Nullable AnAction createAction(@Nullable AnalysisStartingPoint analysis,
|
||||
@NotNull Supplier<List<StackLine>> nextFramesSupplier) {
|
||||
if (analysis == null) return null;
|
||||
String text = JavaDfaSliceValueFilter.getPresentationText(analysis.myDfType, analysis.myAnchor.getType());
|
||||
String text = DfaBasedFilter.getPresentationText(analysis.myDfType, analysis.myAnchor.getType());
|
||||
if (text.isEmpty()) return null;
|
||||
return new AnAction(null, JavaBundle.message("action.dfa.from.stacktrace.text", analysis.myAnchor.getText(), text), null) {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
List<StackLine> nextFrames = nextFramesSupplier.get();
|
||||
StackFilter stackFilter = StackFilter.from(nextFrames);
|
||||
SliceAnalysisParams params = new SliceAnalysisParams();
|
||||
params.dataFlowToThis = true;
|
||||
params.scope = new AnalysisScope(myProject);
|
||||
params.scope = new AnalysisScope(GlobalSearchScope.allScope(myProject), myProject);
|
||||
params.scope.setSearchInLibraries(true);
|
||||
params.valueFilter = new JavaDfaSliceValueFilter(analysis.myDfType);
|
||||
params.valueFilter = new JavaValueFilter(new DfaBasedFilter(analysis.myDfType), stackFilter);
|
||||
SliceManager.getInstance(myProject).createToolWindow(analysis.myAnchor, params);
|
||||
}
|
||||
};
|
||||
|
||||
+10
-12
@@ -17,17 +17,17 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class JavaDfaSliceValueFilter implements SliceValueFilter {
|
||||
private final @Nullable JavaDfaSliceValueFilter myNextFilter;
|
||||
final class DfaBasedFilter {
|
||||
private final @Nullable DfaBasedFilter myNextFilter;
|
||||
|
||||
private final @NotNull DfType myDfType;
|
||||
|
||||
private JavaDfaSliceValueFilter(@Nullable JavaDfaSliceValueFilter nextFilter, @NotNull DfType type) {
|
||||
private DfaBasedFilter(@Nullable DfaBasedFilter nextFilter, @NotNull DfType type) {
|
||||
myNextFilter = nextFilter;
|
||||
myDfType = type;
|
||||
}
|
||||
|
||||
public JavaDfaSliceValueFilter(@NotNull DfType type) {
|
||||
DfaBasedFilter(@NotNull DfType type) {
|
||||
this(null, type);
|
||||
}
|
||||
|
||||
@@ -35,16 +35,15 @@ public class JavaDfaSliceValueFilter implements SliceValueFilter {
|
||||
return myDfType;
|
||||
}
|
||||
|
||||
JavaDfaSliceValueFilter wrap() {
|
||||
return new JavaDfaSliceValueFilter(this, DfTypes.TOP);
|
||||
DfaBasedFilter wrap() {
|
||||
return new DfaBasedFilter(this, DfTypes.TOP);
|
||||
}
|
||||
|
||||
JavaDfaSliceValueFilter unwrap() {
|
||||
DfaBasedFilter unwrap() {
|
||||
return myNextFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowed(@NotNull PsiElement element) {
|
||||
boolean allowed(@NotNull PsiElement element) {
|
||||
return allowed(element, true);
|
||||
}
|
||||
|
||||
@@ -64,7 +63,7 @@ public class JavaDfaSliceValueFilter implements SliceValueFilter {
|
||||
return dfType.meet(myDfType) != DfTypes.BOTTOM;
|
||||
}
|
||||
|
||||
@Nullable JavaDfaSliceValueFilter mergeFilter(@NotNull PsiElement element) {
|
||||
@Nullable DfaBasedFilter mergeFilter(@NotNull PsiElement element) {
|
||||
DfType type = getElementDfType(element, true);
|
||||
if (type instanceof DfReferenceType) {
|
||||
type = ((DfReferenceType)type).dropLocality().dropMutability();
|
||||
@@ -72,7 +71,7 @@ public class JavaDfaSliceValueFilter implements SliceValueFilter {
|
||||
DfType meet = type.meet(myDfType);
|
||||
if (meet == DfTypes.TOP && myNextFilter == null) return null;
|
||||
if (meet == DfTypes.BOTTOM || meet.equals(myDfType)) return this;
|
||||
return new JavaDfaSliceValueFilter(myNextFilter, meet);
|
||||
return new DfaBasedFilter(myNextFilter, meet);
|
||||
}
|
||||
|
||||
private @NotNull DfType getElementDfType(@NotNull PsiElement element, boolean assertionsDisabled) {
|
||||
@@ -96,7 +95,6 @@ public class JavaDfaSliceValueFilter implements SliceValueFilter {
|
||||
return myDfType.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull @Nls String getPresentationText(@NotNull PsiElement element) {
|
||||
if (element instanceof PsiLiteralExpression ||
|
||||
element instanceof PsiExpression && JavaPsiMathUtil.getNumberFromLiteral((PsiExpression)element) != null) {
|
||||
@@ -1,11 +1,13 @@
|
||||
// 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.codeInspection.dataFlow.types.DfTypes;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.DummyHolder;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import gnu.trove.THashMap;
|
||||
@@ -16,6 +18,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* A helper class to build new {@link JavaSliceUsage} and pass it to the processor
|
||||
@@ -25,30 +28,32 @@ final class JavaSliceBuilder {
|
||||
private final @NotNull PsiSubstitutor mySubstitutor;
|
||||
private final @Range(from = 0, to = Integer.MAX_VALUE) int myIndexNesting;
|
||||
private final @NotNull String mySyntheticField;
|
||||
private final @NotNull SliceAnalysisParams myAnalysisParams;
|
||||
private final @NotNull JavaValueFilter myFilter;
|
||||
|
||||
private JavaSliceBuilder(@NotNull SliceUsage parent,
|
||||
@NotNull PsiSubstitutor substitutor,
|
||||
@Range(from = 0, to = Integer.MAX_VALUE) int indexNesting,
|
||||
@NotNull String syntheticField,
|
||||
@NotNull SliceAnalysisParams analysisParams) {
|
||||
@Nullable SliceValueFilter filter) {
|
||||
assert indexNesting >= 0 : indexNesting;
|
||||
myParent = parent;
|
||||
mySubstitutor = substitutor;
|
||||
myIndexNesting = indexNesting;
|
||||
mySyntheticField = syntheticField;
|
||||
myAnalysisParams = analysisParams;
|
||||
myFilter = filter instanceof JavaValueFilter
|
||||
? (JavaValueFilter)filter
|
||||
: JavaValueFilter.ALLOW_EVERYTHING;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
@NotNull JavaSliceBuilder withSubstitutor(@NotNull PsiSubstitutor substitutor) {
|
||||
return new JavaSliceBuilder(myParent, substitutor, myIndexNesting, mySyntheticField, myAnalysisParams);
|
||||
return new JavaSliceBuilder(myParent, substitutor, myIndexNesting, mySyntheticField, myFilter);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
@NotNull JavaSliceBuilder withSyntheticField(@NotNull String syntheticField) {
|
||||
if (syntheticField.equals(mySyntheticField)) return this;
|
||||
return new JavaSliceBuilder(myParent, mySubstitutor, myIndexNesting, syntheticField, myAnalysisParams);
|
||||
return new JavaSliceBuilder(myParent, mySubstitutor, myIndexNesting, syntheticField, myFilter);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
@@ -59,28 +64,16 @@ final class JavaSliceBuilder {
|
||||
@Contract(pure = true)
|
||||
@NotNull JavaSliceBuilder dropNesting() {
|
||||
if (myIndexNesting == 0) return this;
|
||||
return new JavaSliceBuilder(myParent, mySubstitutor, 0, mySyntheticField, myAnalysisParams)
|
||||
.withFilter(null);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
@NotNull JavaSliceBuilder withFilter(@Nullable SliceValueFilter filter) {
|
||||
if (filter == myAnalysisParams.valueFilter) return this;
|
||||
SliceAnalysisParams params = new SliceAnalysisParams(myAnalysisParams);
|
||||
params.valueFilter = filter;
|
||||
return new JavaSliceBuilder(myParent, mySubstitutor, myIndexNesting, mySyntheticField, params);
|
||||
return new JavaSliceBuilder(myParent, mySubstitutor, 0, mySyntheticField, myFilter.withType(DfTypes.TOP));
|
||||
}
|
||||
|
||||
boolean process(PsiElement element, Processor<? super SliceUsage> processor) {
|
||||
final PsiElement realExpression = element.getParent() instanceof DummyHolder ? element.getParent().getContext() : element;
|
||||
assert realExpression != null;
|
||||
if (!(realExpression instanceof PsiCompiledElement)) {
|
||||
JavaDfaSliceValueFilter curFilter = myAnalysisParams.valueFilter instanceof JavaDfaSliceValueFilter
|
||||
? (JavaDfaSliceValueFilter)myAnalysisParams.valueFilter
|
||||
: new JavaDfaSliceValueFilter(DfTypes.TOP);
|
||||
JavaDfaSliceValueFilter filter = curFilter.mergeFilter(realExpression);
|
||||
SliceAnalysisParams params = myAnalysisParams;
|
||||
if (filter != curFilter) {
|
||||
JavaValueFilter filter = myFilter.mergeFilter(realExpression);
|
||||
SliceAnalysisParams params = myParent.params;
|
||||
if (filter != params.valueFilter) {
|
||||
params = new SliceAnalysisParams(params);
|
||||
params.valueFilter = filter;
|
||||
}
|
||||
@@ -106,20 +99,26 @@ final class JavaSliceBuilder {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
@NotNull JavaSliceBuilder withFilter(UnaryOperator<JavaValueFilter> filterTransformer) {
|
||||
JavaValueFilter filter = filterTransformer.apply(myFilter);
|
||||
if (filter == myFilter) return this;
|
||||
return new JavaSliceBuilder(myParent, mySubstitutor, myIndexNesting, mySyntheticField, filter);
|
||||
}
|
||||
|
||||
@NotNull SearchScope getSearchScope() {
|
||||
AnalysisScope scope = myParent.getScope();
|
||||
return myFilter.correctScope(scope.getProject(), scope.toSearchScope());
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
@NotNull JavaSliceBuilder incrementNesting() {
|
||||
SliceValueFilter filter = myAnalysisParams.valueFilter;
|
||||
filter = filter instanceof JavaDfaSliceValueFilter ? ((JavaDfaSliceValueFilter)filter).wrap() : null;
|
||||
return new JavaSliceBuilder(myParent, mySubstitutor, myIndexNesting + 1, mySyntheticField, myAnalysisParams)
|
||||
.withFilter(filter);
|
||||
return new JavaSliceBuilder(myParent, mySubstitutor, myIndexNesting + 1, mySyntheticField, myFilter.wrap());
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
@NotNull JavaSliceBuilder decrementNesting() {
|
||||
SliceValueFilter filter = myAnalysisParams.valueFilter;
|
||||
filter = filter instanceof JavaDfaSliceValueFilter ? ((JavaDfaSliceValueFilter)filter).unwrap() : null;
|
||||
return new JavaSliceBuilder(myParent, mySubstitutor, myIndexNesting - 1, mySyntheticField, myAnalysisParams)
|
||||
.withFilter(filter);
|
||||
return new JavaSliceBuilder(myParent, mySubstitutor, myIndexNesting - 1, mySyntheticField, myFilter.unwrap());
|
||||
}
|
||||
|
||||
boolean hasNesting() {
|
||||
@@ -136,6 +135,7 @@ final class JavaSliceBuilder {
|
||||
return mySubstitutor;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
PsiType substitute(@Nullable PsiType type) {
|
||||
return mySubstitutor.substitute(type);
|
||||
}
|
||||
@@ -145,17 +145,23 @@ final class JavaSliceBuilder {
|
||||
return mySyntheticField;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
@NotNull JavaValueFilter getFilter() {
|
||||
return myFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parent parent usage
|
||||
* @return new JavaSliceBuilder that inherits the parent properties
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
static @NotNull JavaSliceBuilder create(SliceUsage parent) {
|
||||
static @NotNull JavaSliceBuilder create(@NotNull SliceUsage parent) {
|
||||
SliceValueFilter filter = parent.params.valueFilter;
|
||||
if (parent instanceof JavaSliceUsage) {
|
||||
JavaSliceUsage javaParent = (JavaSliceUsage)parent;
|
||||
return new JavaSliceBuilder(parent, javaParent.getSubstitutor(), javaParent.indexNesting, javaParent.syntheticField, parent.params);
|
||||
return new JavaSliceBuilder(parent, javaParent.getSubstitutor(), javaParent.indexNesting, javaParent.syntheticField, filter);
|
||||
}
|
||||
return new JavaSliceBuilder(parent, PsiSubstitutor.EMPTY, 0, "", parent.params);
|
||||
return new JavaSliceBuilder(parent, PsiSubstitutor.EMPTY, 0, "", filter);
|
||||
}
|
||||
|
||||
@Nullable JavaSliceBuilder combineSubstitutor(@NotNull PsiSubstitutor substitutor, @NotNull Project project) {
|
||||
|
||||
@@ -128,14 +128,14 @@ public class JavaSliceProvider implements SliceLanguageSupportProvider, SliceUsa
|
||||
throw new SliceFilterParseException(
|
||||
JavaBundle.message("slice.filter.parse.error.null.filter.not.applicable.for.primitive.type", type.getPresentableText()));
|
||||
}
|
||||
return new JavaDfaSliceValueFilter(DfTypes.NULL);
|
||||
return new JavaValueFilter(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);
|
||||
return new JavaValueFilter(DfTypes.NOT_NULL_OBJECT);
|
||||
}
|
||||
RelationType relationType = RelationType.EQ;
|
||||
if (PsiType.BYTE.equals(type) ||
|
||||
@@ -155,7 +155,7 @@ public class JavaSliceProvider implements SliceLanguageSupportProvider, SliceUsa
|
||||
if (psiClass != null && psiClass.isEnum()) {
|
||||
PsiField enumConstant = psiClass.findFieldByName(filter, false);
|
||||
if (enumConstant instanceof PsiEnumConstant) {
|
||||
return new JavaDfaSliceValueFilter(DfTypes.constant(enumConstant, type));
|
||||
return new JavaValueFilter(DfTypes.constant(enumConstant, type));
|
||||
} else {
|
||||
throw new SliceFilterParseException(JavaBundle.message("slice.filter.parse.error.enum.constant.not.found", filter));
|
||||
}
|
||||
@@ -181,12 +181,12 @@ public class JavaSliceProvider implements SliceLanguageSupportProvider, SliceUsa
|
||||
}
|
||||
if (PsiType.LONG.equals(type)) {
|
||||
LongRangeSet rangeSet = LongRangeSet.point(((Number)o).longValue()).fromRelation(relationType);
|
||||
return new JavaDfaSliceValueFilter(DfTypes.longRange(rangeSet));
|
||||
return new JavaValueFilter(DfTypes.longRange(rangeSet));
|
||||
}
|
||||
LongRangeSet rangeSet = LongRangeSet.point(((Number)o).intValue()).fromRelation(relationType);
|
||||
return new JavaDfaSliceValueFilter(DfTypes.intRangeClamped(rangeSet));
|
||||
return new JavaValueFilter(DfTypes.intRangeClamped(rangeSet));
|
||||
}
|
||||
return new JavaDfaSliceValueFilter(DfTypes.constant(o, type));
|
||||
return new JavaValueFilter(DfTypes.constant(o, type));
|
||||
}
|
||||
|
||||
private static @Nullable PsiType getType(@NotNull PsiElement expression) {
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
// 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.types.DfType;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfTypes;
|
||||
import com.intellij.execution.filters.ExceptionAnalysisProvider;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class JavaValueFilter implements SliceValueFilter {
|
||||
public static final JavaValueFilter ALLOW_EVERYTHING = new JavaValueFilter(null, null);
|
||||
|
||||
private final @Nullable DfaBasedFilter myDfaFilter;
|
||||
private final @Nullable StackFilter myStackFilter;
|
||||
|
||||
JavaValueFilter(@Nullable DfaBasedFilter filter,
|
||||
@Nullable StackFilter stackFilter) {
|
||||
myDfaFilter = filter;
|
||||
myStackFilter = stackFilter;
|
||||
}
|
||||
|
||||
JavaValueFilter(@NotNull DfType dfType) {
|
||||
this(new DfaBasedFilter(dfType), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowed(@NotNull PsiElement element) {
|
||||
return (myDfaFilter == null || myDfaFilter.allowed(element)) &&
|
||||
(myStackFilter == null || myStackFilter.isAcceptable(element));
|
||||
}
|
||||
|
||||
public @NotNull JavaValueFilter withStack(List<ExceptionAnalysisProvider.StackLine> lines) {
|
||||
return new JavaValueFilter(myDfaFilter, StackFilter.from(lines));
|
||||
}
|
||||
|
||||
@NotNull JavaValueFilter withType(DfType type) {
|
||||
return new JavaValueFilter(new DfaBasedFilter(type), myStackFilter);
|
||||
}
|
||||
|
||||
@NotNull JavaValueFilter pushFrame() {
|
||||
if (myStackFilter == null) return this;
|
||||
return new JavaValueFilter(myDfaFilter, myStackFilter.pushFrame());
|
||||
}
|
||||
|
||||
@NotNull JavaValueFilter popFrame() {
|
||||
if (myStackFilter == null) return this;
|
||||
return new JavaValueFilter(myDfaFilter, myStackFilter.popFrame());
|
||||
}
|
||||
|
||||
@NotNull JavaValueFilter dropFrameFilter() {
|
||||
if (myStackFilter == null) return this;
|
||||
return new JavaValueFilter(myDfaFilter, null);
|
||||
}
|
||||
|
||||
@NotNull JavaValueFilter wrap() {
|
||||
if (myDfaFilter == null) return this;
|
||||
return new JavaValueFilter(myDfaFilter.wrap(), myStackFilter);
|
||||
}
|
||||
|
||||
@NotNull JavaValueFilter unwrap() {
|
||||
if (myDfaFilter == null) return this;
|
||||
return new JavaValueFilter(myDfaFilter.unwrap(), myStackFilter);
|
||||
}
|
||||
|
||||
@NotNull SearchScope correctScope(@NotNull Project project, @NotNull SearchScope scope) {
|
||||
return myStackFilter == null ? scope : myStackFilter.correctScope(project, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull @Nls String getPresentationText(@NotNull PsiElement element) {
|
||||
// For now, stack filter doesn't contribute to the presentation text
|
||||
return myDfaFilter == null ? "" : myDfaFilter.getPresentationText(element);
|
||||
}
|
||||
|
||||
JavaValueFilter mergeFilter(PsiElement expression) {
|
||||
DfaBasedFilter dfaFilter = myDfaFilter == null ? new DfaBasedFilter(DfTypes.TOP) : myDfaFilter;
|
||||
DfaBasedFilter newFilter = dfaFilter.mergeFilter(expression);
|
||||
return dfaFilter == newFilter ? this : new JavaValueFilter(newFilter, myStackFilter);
|
||||
}
|
||||
|
||||
DfType getDfType() {
|
||||
return myDfaFilter == null ? DfTypes.TOP : myDfaFilter.getDfType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ((myDfaFilter == null ? "" : myDfaFilter.toString()) +
|
||||
" " + (myStackFilter == null ? "" : myStackFilter.toString())).trim();
|
||||
}
|
||||
|
||||
public boolean requiresAssertionViolation(PsiElement element) {
|
||||
return myDfaFilter != null && myDfaFilter.requiresAssertionViolation(element);
|
||||
}
|
||||
}
|
||||
@@ -99,8 +99,8 @@ class SliceUsageCellRenderer extends SliceUsageCellRendererBase {
|
||||
String message = LangBundle.message("slice.analysis.title.filter", filterText);
|
||||
append(" " + message, SimpleTextAttributes.GRAY_ATTRIBUTES);
|
||||
}
|
||||
if (filter instanceof JavaDfaSliceValueFilter && element != null &&
|
||||
((JavaDfaSliceValueFilter)filter).requiresAssertionViolation(element)) {
|
||||
if (filter instanceof JavaValueFilter && element != null &&
|
||||
((JavaValueFilter)filter).requiresAssertionViolation(element)) {
|
||||
append(" " + JavaBundle.message("slice.usage.message.assertion.violated"), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,10 @@ package com.intellij.slicer;
|
||||
|
||||
import com.intellij.analysis.AnalysisScope;
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
|
||||
import com.intellij.codeInspection.dataFlow.DfaUtil;
|
||||
import com.intellij.codeInspection.dataFlow.JavaMethodContractUtil;
|
||||
import com.intellij.codeInsight.JavaTargetElementEvaluator;
|
||||
import com.intellij.codeInspection.dataFlow.*;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfType;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfTypes;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
@@ -35,19 +36,21 @@ import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.MethodSignatureUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.refactoring.util.RefactoringChangeUtil;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.CommonProcessors;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import gnu.trove.THashMap;
|
||||
import gnu.trove.THashSet;
|
||||
import org.intellij.lang.annotations.Flow;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author cdr
|
||||
@@ -106,6 +109,9 @@ class SliceUtil {
|
||||
addContainerReferences((PsiVariable)resolved, processor, builder);
|
||||
|
||||
needToReportDeclaration = true;
|
||||
if (resolved instanceof PsiField || StackFilter.getElementContext(resolved) != StackFilter.getElementContext(expression)) {
|
||||
builder = builder.withFilter(JavaValueFilter::dropFrameFilter);
|
||||
}
|
||||
expression = resolved;
|
||||
}
|
||||
if (expression instanceof PsiVariable) {
|
||||
@@ -138,7 +144,7 @@ class SliceUtil {
|
||||
return processFieldUsages((PsiField)variable, builder.dropSyntheticField(), processor);
|
||||
}
|
||||
else if (variable instanceof PsiParameter) {
|
||||
return processParameterUsages((PsiParameter)variable, builder, processor);
|
||||
return processParameterUsages((PsiParameter)variable, builder.withFilter(JavaValueFilter::popFrame), processor);
|
||||
}
|
||||
}
|
||||
if (expression instanceof PsiMethodCallExpression) { // ctr call can't return value or be container get, so don't use PsiCall here
|
||||
@@ -182,11 +188,11 @@ class SliceUtil {
|
||||
return processUsagesFlownDownTo(rExpression, processor, builder);
|
||||
}
|
||||
}
|
||||
JavaDfaSliceValueFilter filter = ObjectUtils.tryCast(builder.getParent().params.valueFilter, JavaDfaSliceValueFilter.class);
|
||||
if (filter != null && expression instanceof PsiExpression) {
|
||||
AnalysisStartingPoint analysis = AnalysisStartingPoint.propagateThroughExpression(expression, filter.getDfType());
|
||||
DfType filterDfType = builder.getFilter().getDfType();
|
||||
if (filterDfType != DfTypes.TOP && expression instanceof PsiExpression) {
|
||||
AnalysisStartingPoint analysis = AnalysisStartingPoint.propagateThroughExpression(expression, filterDfType);
|
||||
if (analysis != null) {
|
||||
return builder.withFilter(new JavaDfaSliceValueFilter(analysis.myDfType)).process(analysis.myAnchor, processor);
|
||||
return builder.withFilter(filter -> filter.withType(analysis.myDfType)).process(analysis.myAnchor, processor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,7 +259,7 @@ class SliceUtil {
|
||||
final PsiType parentType = builder.substitute(methodCallExpr.getType());
|
||||
final PsiSubstitutor substitutor = resolved.getSubstitutor().putAll(builder.getSubstitutor());
|
||||
return processMethodReturnValue(processor, builder.getSubstitutor(), qualifierClass, methodCalled, parentType,
|
||||
builder.withSubstitutor(substitutor).dropSyntheticField());
|
||||
builder.withSubstitutor(substitutor).dropSyntheticField().withFilter(JavaValueFilter::pushFrame));
|
||||
}
|
||||
|
||||
private static boolean processMethodReturnValue(@NotNull Processor<? super SliceUsage> processor,
|
||||
@@ -263,15 +269,11 @@ class SliceUtil {
|
||||
@Nullable PsiType parentType,
|
||||
@NotNull JavaSliceBuilder builder) {
|
||||
Collection<PsiMethod> overrides = new THashSet<>();
|
||||
AnalysisScope parentScope = builder.getParent().getScope();
|
||||
OverridingMethodsSearch.search(methodCalled, parentScope.toSearchScope(), true).forEach((PsiMethod override) -> {
|
||||
PsiClass containingClass = override.getContainingClass();
|
||||
if (containingClass == null) return true;
|
||||
if (qualifierClass == null || containingClass.isInheritor(qualifierClass, true)) {
|
||||
overrides.add(override);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
SearchScope scope = builder.getSearchScope();
|
||||
if (qualifierClass != null && qualifierClass != methodCalled.getContainingClass()) {
|
||||
scope = JavaTargetElementEvaluator.getHierarchyScope(qualifierClass, scope);
|
||||
}
|
||||
overrides.addAll(OverridingMethodsSearch.search(methodCalled, scope, true).findAll());
|
||||
overrides.add(methodCalled);
|
||||
|
||||
final boolean[] result = {true};
|
||||
@@ -280,7 +282,7 @@ class SliceUtil {
|
||||
if (override instanceof PsiCompiledElement) {
|
||||
override = (PsiMethod)override.getNavigationElement();
|
||||
}
|
||||
if (!parentScope.contains(override)) continue;
|
||||
if (!builder.getSearchScope().contains(PsiUtil.preferCompiledElement(override).getContainingFile().getVirtualFile())) continue;
|
||||
|
||||
Language language = override.getLanguage();
|
||||
if (language != JavaLanguage.INSTANCE) {
|
||||
@@ -322,28 +324,17 @@ class SliceUtil {
|
||||
}
|
||||
|
||||
private static PsiClass resolveQualifier(@NotNull PsiMethodCallExpression expr) {
|
||||
PsiExpression qualifier = expr.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null) {
|
||||
PsiMethodCallExpression copy = (PsiMethodCallExpression)expr.copy();
|
||||
PsiReferenceExpression methodExpression = copy.getMethodExpression();
|
||||
|
||||
PsiThisExpression thisExpression = RefactoringChangeUtil.createThisExpression(expr.getManager(), null);
|
||||
methodExpression.setQualifierExpression(thisExpression);
|
||||
qualifier = methodExpression.getQualifierExpression();
|
||||
PsiExpression qualifier = ExpressionUtils.getEffectiveQualifier(expr.getMethodExpression());
|
||||
if (qualifier == null) return null;
|
||||
PsiType psiType = null;
|
||||
CommonDataflow.DataflowResult result = CommonDataflow.getDataflowResult(qualifier);
|
||||
if (result != null) {
|
||||
psiType = TypeConstraint.fromDfType(result.getDfTypeNoAssertions(qualifier)).getPsiType(qualifier.getProject());
|
||||
}
|
||||
if (qualifier != null) {
|
||||
if (qualifier instanceof PsiReferenceExpression) {
|
||||
PsiElement resolved = ((PsiReferenceExpression)qualifier).resolve();
|
||||
if (resolved instanceof PsiClass) return (PsiClass)resolved;
|
||||
}
|
||||
else if (qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression) {
|
||||
PsiType type = qualifier.getType();
|
||||
if (type instanceof PsiClassType) {
|
||||
return ((PsiClassType)type).resolve();
|
||||
}
|
||||
}
|
||||
if (psiType == null) {
|
||||
psiType = qualifier.getType();
|
||||
}
|
||||
return null;
|
||||
return PsiUtil.resolveClassInClassTypeOnly(psiType);
|
||||
}
|
||||
|
||||
private static boolean processFieldUsages(@NotNull final PsiField field,
|
||||
@@ -356,7 +347,7 @@ class SliceUtil {
|
||||
}
|
||||
}
|
||||
AnalysisScope scope = builder.getParent().getScope();
|
||||
SearchScope searchScope = scope.toSearchScope();
|
||||
SearchScope searchScope = builder.getSearchScope();
|
||||
return ReferencesSearch.search(field, searchScope).forEach(reference -> {
|
||||
ProgressManager.checkCanceled();
|
||||
PsiElement element = reference.getElement();
|
||||
@@ -370,10 +361,10 @@ class SliceUtil {
|
||||
if (PsiUtil.isOnAssignmentLeftHand(referenceExpression)) {
|
||||
PsiExpression rExpression = ((PsiAssignmentExpression)parentExpr).getRExpression();
|
||||
if (rExpression != null) {
|
||||
PsiType rtype = rExpression.getType();
|
||||
PsiType ftype = field.getType();
|
||||
PsiType subFType = builder.substitute(ftype);
|
||||
PsiType subRType = builder.substitute(rtype);
|
||||
PsiType rType = rExpression.getType();
|
||||
PsiType fType = field.getType();
|
||||
PsiType subFType = builder.substitute(fType);
|
||||
PsiType subRType = builder.substitute(rType);
|
||||
if (subFType != null && subRType != null && TypeConversionUtil.isAssignable(subFType, subRType)) {
|
||||
return builder.process(rExpression, processor);
|
||||
}
|
||||
@@ -422,7 +413,7 @@ class SliceUtil {
|
||||
|
||||
final Set<PsiReference> processed = new THashSet<>(); //usages of super method and overridden method can overlap
|
||||
for (final PsiMethod superMethod : superMethods) {
|
||||
if (!MethodReferencesSearch.search(superMethod, builder.getParent().getScope().toSearchScope(), true).forEach(reference -> {
|
||||
if (!MethodReferencesSearch.search(superMethod, builder.getSearchScope(), true).forEach(reference -> {
|
||||
ProgressManager.checkCanceled();
|
||||
synchronized (processed) {
|
||||
if (!processed.add(reference)) return true;
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
// 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.execution.filters.ExceptionAnalysisProvider;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.siyeh.ig.psiutils.ClassUtils;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Allows to narrow dataflow-to-here results based on known stacktrace.
|
||||
* TODO: support bridge methods
|
||||
*/
|
||||
class StackFilter {
|
||||
final int myExtraFrames;
|
||||
final @NotNull String myClassName;
|
||||
final @NotNull String myMethodName;
|
||||
final @Nullable StackFilter myNext;
|
||||
|
||||
private StackFilter(int frames,
|
||||
@NotNull String className,
|
||||
@NotNull String methodName,
|
||||
@Nullable StackFilter next) {
|
||||
myExtraFrames = frames;
|
||||
myClassName = className;
|
||||
myMethodName = methodName;
|
||||
myNext = next;
|
||||
}
|
||||
|
||||
SearchScope correctScope(Project project, SearchScope base) {
|
||||
if (base instanceof GlobalSearchScope && myExtraFrames == 0) {
|
||||
PsiManager instance = PsiManager.getInstance(project);
|
||||
String packageName = StringUtil.getPackageName(myClassName);
|
||||
return new GlobalSearchScope() {
|
||||
@Override
|
||||
public boolean isSearchInModuleContent(@NotNull Module aModule) {
|
||||
return ((GlobalSearchScope)base).isSearchInModuleContent(aModule);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSearchInLibraries() {
|
||||
return ((GlobalSearchScope)base).isSearchInLibraries();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull VirtualFile file) {
|
||||
if (!base.contains(file)) return false;
|
||||
PsiFile psiFile = instance.findFile(file);
|
||||
if (!(psiFile instanceof PsiClassOwner)) return false;
|
||||
// Do not filter by exact class for now
|
||||
return ((PsiClassOwner)psiFile).getPackageName().equals(packageName);
|
||||
}
|
||||
};
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
boolean isAcceptable(PsiElement element) {
|
||||
if (myExtraFrames > 0) return true;
|
||||
PsiElement parent = getElementContext(element);
|
||||
if (parent instanceof PsiMember) {
|
||||
return myMethodName.equals(getExpectedName((PsiMember)parent)) && classMatches(((PsiMember)parent).getContainingClass());
|
||||
}
|
||||
if (parent instanceof PsiLambdaExpression) {
|
||||
return myMethodName.startsWith("lambda$") && classMatches(ClassUtils.getContainingClass(parent));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static PsiElement getElementContext(PsiElement element) {
|
||||
PsiElement parent;
|
||||
while(true) {
|
||||
parent = PsiTreeUtil.getParentOfType(element, PsiMember.class, PsiLambdaExpression.class);
|
||||
if (parent instanceof PsiAnonymousClass && PsiTreeUtil.isAncestor(((PsiAnonymousClass)parent).getArgumentList(), element, true)) {
|
||||
element = parent;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
private static @Nullable String getExpectedName(PsiMember member) {
|
||||
if (member instanceof PsiMethod) {
|
||||
return ((PsiMethod)member).isConstructor() ? "<init>" : member.getName();
|
||||
}
|
||||
if (member instanceof PsiField || member instanceof PsiClassInitializer) {
|
||||
return member.hasModifierProperty(PsiModifier.STATIC) ? "<clinit>" : "<init>";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean classMatches(PsiClass aClass) {
|
||||
if (aClass == null) return false;
|
||||
PsiFile file = aClass.getContainingFile();
|
||||
if (!(file instanceof PsiClassOwner)) return false;
|
||||
String packageName = StringUtil.getPackageName(myClassName);
|
||||
if (!((PsiClassOwner)file).getPackageName().matches(packageName)) return false;
|
||||
String shortName = StringUtil.getShortName(myClassName);
|
||||
return classNameMatches(aClass, shortName);
|
||||
}
|
||||
|
||||
private static boolean classNameMatches(PsiClass aClass, String shortName) {
|
||||
String actualName = aClass.getName();
|
||||
if (shortName.equals(actualName)) return true;
|
||||
String afterDollar = StringUtil.getShortName(shortName, '$');
|
||||
if (actualName != null) {
|
||||
if (!actualName.equals(afterDollar)) return false;
|
||||
} else {
|
||||
if (!afterDollar.matches("\\d+")) return false;
|
||||
}
|
||||
PsiClass containingClass = ClassUtils.getContainingClass(aClass);
|
||||
String prefix = StringUtil.substringBefore(shortName, "$");
|
||||
if (prefix == null) {
|
||||
return containingClass == null;
|
||||
}
|
||||
return containingClass != null && classNameMatches(containingClass, prefix);
|
||||
}
|
||||
|
||||
@NotNull StackFilter pushFrame() {
|
||||
return new StackFilter(myExtraFrames + 1, myClassName, myMethodName, myNext);
|
||||
}
|
||||
|
||||
@Nullable StackFilter popFrame() {
|
||||
return myExtraFrames == 0 ? myNext :
|
||||
new StackFilter(myExtraFrames - 1, myClassName, myMethodName, myNext);
|
||||
}
|
||||
|
||||
static @Nullable StackFilter from(List<ExceptionAnalysisProvider.StackLine> list) {
|
||||
return StreamEx.of(list).foldRight(null, (line, prev) ->
|
||||
new StackFilter(0, line.getClassName(), line.getMethodName(), prev));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (myExtraFrames == 0 ? "" : myExtraFrames + "+") + myClassName + "." + myMethodName;
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
interface I {
|
||||
String getValue();
|
||||
}
|
||||
|
||||
interface J extends I {
|
||||
|
||||
}
|
||||
|
||||
class X implements I {
|
||||
public String getValue() {return "X";}
|
||||
}
|
||||
|
||||
class Y implements J {
|
||||
public String getValue() {return <flown11>"Y";}
|
||||
}
|
||||
|
||||
class Test {
|
||||
void foo(J j) {
|
||||
String <caret>s = <flown1>j.getValue();
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
interface I {
|
||||
String getValue();
|
||||
}
|
||||
|
||||
interface J extends I {
|
||||
|
||||
}
|
||||
|
||||
class X implements I {
|
||||
public String getValue() {return "X";}
|
||||
}
|
||||
|
||||
class Y implements J {
|
||||
public String getValue() {return <flown11>"Y";}
|
||||
}
|
||||
|
||||
class Test {
|
||||
void foo(I i) {
|
||||
if (!(i instanceof J)) return;
|
||||
String <caret>s = <flown1>i.getValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
class MainTest {
|
||||
static void test(String <flown1>str) {
|
||||
System.out.println(<caret>str.trim());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
foo("xyz");
|
||||
foo(<flown1111>null);
|
||||
bar("xyz");
|
||||
bar(null);
|
||||
}
|
||||
|
||||
private static void bar(String s) {
|
||||
test(s);
|
||||
}
|
||||
|
||||
private static void foo(String <flown111>s) {
|
||||
test(<flown11>s);
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,17 @@ package com.intellij.java.slicer;
|
||||
|
||||
import com.intellij.analysis.AnalysisScope;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.execution.filters.ExceptionAnalysisProvider;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.slicer.*;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -35,6 +39,10 @@ public class SliceBackwardTest extends SliceTestCase {
|
||||
}
|
||||
|
||||
private void doTest(@NotNull String filter) throws Exception {
|
||||
doTest(filter, ArrayUtil.EMPTY_STRING_ARRAY);
|
||||
}
|
||||
|
||||
private void doTest(@NotNull String filter, @NotNull String @NotNull... stack) throws Exception {
|
||||
configureByFile("/codeInsight/slice/backward/"+getTestName(false)+".java");
|
||||
Map<String, RangeMarker> sliceUsageName2Offset = SliceTestUtil.extractSliceOffsetsFromDocument(getEditor().getDocument());
|
||||
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
|
||||
@@ -47,9 +55,13 @@ public class SliceBackwardTest extends SliceTestCase {
|
||||
params.scope = new AnalysisScope(getProject());
|
||||
params.dataFlowToThis = true;
|
||||
SliceLanguageSupportProvider provider = LanguageSlicing.getProvider(element);
|
||||
if (!filter.isEmpty()) {
|
||||
params.valueFilter = provider.parseFilter(element, filter);
|
||||
}
|
||||
params.valueFilter = filter.isEmpty() ? JavaValueFilter.ALLOW_EVERYTHING : provider.parseFilter(element, filter);
|
||||
List<ExceptionAnalysisProvider.StackLine> lines = StreamEx.of(stack).map(line -> {
|
||||
String[] parts = line.split(":");
|
||||
return new ExceptionAnalysisProvider.StackLine(parts[0], parts[1]);
|
||||
}).toList();
|
||||
assertTrue(params.valueFilter instanceof JavaValueFilter);
|
||||
params.valueFilter = ((JavaValueFilter)params.valueFilter).withStack(lines);
|
||||
|
||||
SliceUsage usage = provider.createRootUsage(element, params);
|
||||
SliceTestUtil.checkUsages(usage, tree);
|
||||
@@ -90,8 +102,11 @@ public class SliceBackwardTest extends SliceTestCase {
|
||||
public void testFinalVarAssignedBeforePassingToAnonymous() throws Exception { doTest();}
|
||||
public void testLocalVarDeclarationAndAssignment() throws Exception { doTest();}
|
||||
public void testSearchOverriddenMethodsInThisClassHierarchy() throws Exception { doTest();}
|
||||
public void testSearchOverriddenMethodsInThisClassHierarchyParam() throws Exception { doTest();}
|
||||
public void testSearchOverriddenMethodsInThisClassHierarchyParamDfa() throws Exception { doTest();}
|
||||
public void testAppend() throws Exception { doTest();}
|
||||
public void testRequireNonNull() throws Exception { doTest();}
|
||||
|
||||
public void testFilterIntRange() throws Exception { doTest(">=0");}
|
||||
public void testFilterIntRangeArray() throws Exception { doTest(">=0");}
|
||||
public void testFilterNull() throws Exception { doTest("null");}
|
||||
@@ -101,4 +116,7 @@ public class SliceBackwardTest extends SliceTestCase {
|
||||
public void testFilterPropagateBoolean2() throws Exception { doTest("false");}
|
||||
public void testFilterAssertionViolation() throws Exception { doTest("-1");}
|
||||
public void testReturnParameter() throws Exception { doTest(); }
|
||||
|
||||
public void testStackFilterSimple() throws Exception { doTest("null",
|
||||
"MainTest:test", "MainTest:foo", "MainTest:main");}
|
||||
}
|
||||
|
||||
@@ -438,14 +438,17 @@ public class ExceptionWorker {
|
||||
if (originalEditor != null) {
|
||||
Document origDocument = originalEditor.getDocument();
|
||||
supplier = () -> {
|
||||
List<StackLine> nextLines = new ArrayList<>();
|
||||
int stackLineNumber = origDocument.getLineNumber(myTextEndOffset);
|
||||
if (stackLineNumber < 1) return Collections.emptyList();
|
||||
int lineCount = Math.min(origDocument.getLineCount(), stackLineNumber + 100);
|
||||
for (int i = stackLineNumber + 1; i < lineCount; i++) {
|
||||
List<StackLine> nextLines = new ArrayList<>();
|
||||
for (int i = stackLineNumber - 1; i < lineCount; i++) {
|
||||
String traceLine = origDocument.getText(TextRange.create(origDocument.getLineStartOffset(i), origDocument.getLineEndOffset(i)));
|
||||
ParsedLine line = parseExceptionLine(traceLine);
|
||||
if (line == null) break;
|
||||
StackLine stackLine = new StackLine(line.classFqnRange.substring(traceLine), line.methodNameRange.substring(traceLine));
|
||||
String methodName = line.methodNameRange.substring(traceLine);
|
||||
if (methodName.startsWith("access$")) continue;
|
||||
StackLine stackLine = new StackLine(line.classFqnRange.substring(traceLine), methodName);
|
||||
nextLines.add(stackLine);
|
||||
}
|
||||
return nextLines;
|
||||
|
||||
Reference in New Issue
Block a user