dataflow to/from containers support

This commit is contained in:
Alexey Kudravtsev
2013-10-22 12:33:55 +04:00
parent 999a2e8e52
commit 6bcad287da
50 changed files with 1303 additions and 301 deletions
@@ -30,6 +30,7 @@ import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.generate.tostring.util.StringUtil;
@@ -75,8 +76,8 @@ public class EditContractIntention extends BaseIntentionAction {
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
final PsiMethod method = getTargetMethod(project, editor, file);
assert method != null;
PsiAnnotation existingAnno = ControlFlowAnalyzer.findContractAnnotation(method);
String oldContract = existingAnno != null ? AnnotationUtil.getStringAttributeValue(existingAnno, null) : null;
Contract existingAnno = AnnotationUtil.findAnnotationInHierarchy(method, Contract.class);
String oldContract = existingAnno == null ? null : existingAnno.value();
String prompt =
"<html>Please specify the contract text<p>" +
"Example: <code>_, null -> false</code><br>" +
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@ import javax.swing.*;
*/
public class SliceDereferenceUsage extends SliceUsage {
public SliceDereferenceUsage(@NotNull PsiElement element, @NotNull SliceUsage parent, @NotNull PsiSubstitutor substitutor) {
super(element, parent, substitutor);
super(element, parent, substitutor,0,"");
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package com.intellij.slicer;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.MethodReferencesSearch;
@@ -62,7 +63,7 @@ public class SliceForwardUtil {
if (parameters.length <= parameterIndex) return true;
PsiParameter actualParam = parameters[parameterIndex];
SliceUsage usage = SliceUtil.createSliceUsage(actualParam, parent, superSubstitutor);
SliceUsage usage = SliceUtil.createSliceUsage(actualParam, parent, superSubstitutor,parent.indexNesting, "");
return processor.process(usage);
}
};
@@ -71,7 +72,7 @@ public class SliceForwardUtil {
}
}
SliceUsage usage = SliceUtil.createSliceUsage(target, parent, parent.getSubstitutor());
SliceUsage usage = SliceUtil.createSliceUsage(target, parent, parent.getSubstitutor(),parent.indexNesting, "");
return processor.process(usage);
}
@@ -91,8 +92,10 @@ public class SliceForwardUtil {
return true;
}
private static boolean processAssignedFrom(final PsiElement from, final PsiElement context, final SliceUsage parent,
final Processor<SliceUsage> processor) {
private static boolean processAssignedFrom(final PsiElement from,
final PsiElement context,
final SliceUsage parent,
@NotNull final Processor<SliceUsage> processor) {
if (from instanceof PsiLocalVariable) {
return searchReferencesAndProcessAssignmentTarget(from, context, parent, processor);
}
@@ -107,7 +110,7 @@ public class SliceForwardUtil {
Collection<PsiMethod> superMethods = new THashSet<PsiMethod>(Arrays.asList(method.findDeepestSuperMethods()));
superMethods.add(method);
for (Iterator<PsiMethod> iterator = superMethods.iterator(); iterator.hasNext(); ) {
SliceManager.getInstance(method.getProject()).checkCanceled();
ProgressManager.checkCanceled();
PsiMethod superMethod = iterator.next();
if (!parent.params.scope.contains(superMethod)) {
iterator.remove();
@@ -116,18 +119,18 @@ public class SliceForwardUtil {
final THashSet<PsiMethod> implementors = new THashSet<PsiMethod>(superMethods);
for (PsiMethod superMethod : superMethods) {
SliceManager.getInstance(method.getProject()).checkCanceled();
ProgressManager.checkCanceled();
if (!OverridingMethodsSearch.search(superMethod, parent.getScope().toSearchScope(), true).forEach(new Processor<PsiMethod>() {
@Override
public boolean process(PsiMethod sub) {
SliceManager.getInstance(method.getProject()).checkCanceled();
ProgressManager.checkCanceled();
implementors.add(sub);
return true;
}
})) return false;
}
for (PsiMethod implementor : implementors) {
SliceManager.getInstance(method.getProject()).checkCanceled();
ProgressManager.checkCanceled();
if (!parent.params.scope.contains(implementor)) continue;
if (implementor instanceof PsiCompiledElement) implementor = (PsiMethod)implementor.getNavigationElement();
@@ -141,7 +144,7 @@ public class SliceForwardUtil {
parametersToAnalyze.add(parameter);
}
for (final PsiParameter psiParameter : parametersToAnalyze) {
SliceManager.getInstance(from.getProject()).checkCanceled();
ProgressManager.checkCanceled();
if (!searchReferencesAndProcessAssignmentTarget(psiParameter, null, parent, processor)) return false;
}
@@ -161,7 +164,7 @@ public class SliceForwardUtil {
if (!MethodReferencesSearch.search(containingMethod, parent.getScope().toSearchScope(), true).forEach(new Processor<PsiReference>() {
@Override
public boolean process(final PsiReference reference) {
SliceManager.getInstance(from.getProject()).checkCanceled();
ProgressManager.checkCanceled();
synchronized (processed) {
if (!processed.add(reference)) return true;
}
@@ -194,7 +197,7 @@ public class SliceForwardUtil {
if (element instanceof PsiCompiledElement) element = element.getNavigationElement();
Pair<PsiElement, PsiSubstitutor> pair = getAssignmentTarget(element, parent);
if (pair != null) {
SliceUsage usage = SliceUtil.createSliceUsage(element, parent, pair.getSecond());
SliceUsage usage = SliceUtil.createSliceUsage(element, parent, pair.getSecond(),parent.indexNesting, "");
return processor.process(usage);
}
if (parent.params.showInstanceDereferences && isDereferenced(element)) {
@@ -261,6 +264,7 @@ public class SliceForwardUtil {
return target == null ? null : Pair.create(target, substitutor);
}
@NotNull
public static PsiElement complexify(@NotNull PsiElement element) {
PsiElement parent = element.getParent();
if (parent instanceof PsiParenthesizedExpression && element.equals(((PsiParenthesizedExpression)parent).getExpression())) {
@@ -78,7 +78,9 @@ public class SliceLeafAnalyzer {
}
};
static SliceNode filterTree(SliceNode oldRoot, NullableFunction<SliceNode, SliceNode> filter, PairProcessor<SliceNode, List<SliceNode>> postProcessor){
static SliceNode filterTree(SliceNode oldRoot,
NullableFunction<SliceNode, SliceNode> filter,
PairProcessor<SliceNode, List<SliceNode>> postProcessor) {
SliceNode filtered = filter.fun(oldRoot);
if (filtered == null) return null;
@@ -257,10 +259,12 @@ public class SliceLeafAnalyzer {
PsiElement value = ApplicationManager.getApplication().runReadAction(new Computable<PsiElement>() {
@Override
public PsiElement compute() {
return sliceUsage.getElement();
return sliceUsage.indexNesting == 0 ? sliceUsage.getElement() : null;
}
});
node(element, map).addAll(ContainerUtil.singleton(value, LEAF_ELEMENT_EQUALITY));
if (value != null) {
node(element, map).addAll(ContainerUtil.singleton(value, LEAF_ELEMENT_EQUALITY));
}
}
super.visit(element);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,12 +17,13 @@ package com.intellij.slicer;
import com.intellij.analysis.AnalysisUIOptions;
import com.intellij.ide.impl.ContentManagerWatcher;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.components.*;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.impl.ProgressManagerImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
@@ -45,7 +46,6 @@ public class SliceManager implements PersistentStateComponent<SliceManager.Store
private final Project myProject;
private ContentManager myBackContentManager;
private ContentManager myForthContentManager;
private volatile boolean myCanceled;
private final StoredSettingsBean myStoredSettings = new StoredSettingsBean();
private static final String BACK_TOOLWINDOW_ID = "Analyze Dataflow to";
private static final String FORTH_TOOLWINDOW_ID = "Analyze Dataflow from";
@@ -61,38 +61,43 @@ public class SliceManager implements PersistentStateComponent<SliceManager.Store
public SliceManager(@NotNull Project project, PsiManager psiManager) {
myProject = project;
}
psiManager.addPsiTreeChangeListener(new PsiTreeChangeAdapter() {
@NotNull
private Disposable addPsiListener(@NotNull final ProgressIndicator indicator) {
Disposable disposable = Disposer.newDisposable();
PsiManager.getInstance(myProject).addPsiTreeChangeListener(new PsiTreeChangeAdapter() {
@Override
public void beforeChildAddition(@NotNull PsiTreeChangeEvent event) {
cancel();
indicator.cancel();
}
@Override
public void beforeChildRemoval(@NotNull PsiTreeChangeEvent event) {
cancel();
indicator.cancel();
}
@Override
public void beforeChildReplacement(@NotNull PsiTreeChangeEvent event) {
cancel();
indicator.cancel();
}
@Override
public void beforeChildMovement(@NotNull PsiTreeChangeEvent event) {
cancel();
indicator.cancel();
}
@Override
public void beforeChildrenChange(@NotNull PsiTreeChangeEvent event) {
cancel();
indicator.cancel();
}
@Override
public void beforePropertyChange(@NotNull PsiTreeChangeEvent event) {
cancel();
indicator.cancel();
}
}, project);
}, disposable);
return disposable;
}
private ContentManager getContentManager(boolean dataFlowToThis) {
@@ -113,10 +118,6 @@ public class SliceManager implements PersistentStateComponent<SliceManager.Store
return myForthContentManager;
}
private void cancel() {
myCanceled = true;
}
public void slice(@NotNull PsiElement element, boolean dataFlowToThis, @NotNull SliceHandler handler) {
String dialogTitle = getElementDescription((dataFlowToThis ? BACK_TOOLWINDOW_ID : FORTH_TOOLWINDOW_ID) + " ", element, null);
@@ -179,25 +180,23 @@ public class SliceManager implements PersistentStateComponent<SliceManager.Store
"</body></html>";
}
public void checkCanceled() throws ProcessCanceledException {
if (myCanceled) {
throw new ProcessCanceledException();
}
}
public void runInterruptibly(Runnable runnable, Runnable onCancel, ProgressIndicator progress) throws ProcessCanceledException {
myCanceled = false;
public void runInterruptibly(@NotNull ProgressIndicator progress,
@NotNull Runnable onCancel,
@NotNull Runnable runnable) throws ProcessCanceledException {
Disposable disposable = addPsiListener(progress);
try {
progress.checkCanceled();
((ProgressManagerImpl)ProgressManager.getInstance()).executeProcessUnderProgress(runnable, progress);
ProgressManager.getInstance().executeProcessUnderProgress(runnable, progress);
}
catch (ProcessCanceledException e) {
cancel();
progress.cancel();
//reschedule for later
onCancel.run();
throw e;
}
finally {
Disposer.dispose(disposable);
}
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -90,29 +90,11 @@ public class SliceNode extends AbstractTreeNode<SliceUsage> implements Duplicate
}
@NotNull
protected List<? extends AbstractTreeNode> getChildrenUnderProgress(ProgressIndicator progress) {
protected List<? extends AbstractTreeNode> getChildrenUnderProgress(@NotNull final ProgressIndicator progress) {
if (isUpToDate()) return myCachedChildren == null ? Collections.<AbstractTreeNode>emptyList() : myCachedChildren;
final List<SliceNode> children = new ArrayList<SliceNode>();
final SliceManager manager = SliceManager.getInstance(getProject());
manager.runInterruptibly(new Runnable() {
@Override
public void run() {
Processor<SliceUsage> processor = new Processor<SliceUsage>() {
@Override
public boolean process(SliceUsage sliceUsage) {
manager.checkCanceled();
SliceNode node = new SliceNode(myProject, sliceUsage, targetEqualUsages);
synchronized (children) {
node.index = children.size();
children.add(node);
}
return true;
}
};
getValue().processChildren(processor);
}
}, new Runnable(){
manager.runInterruptibly(progress, new Runnable() {
@Override
public void run() {
changed = true;
@@ -126,7 +108,26 @@ public class SliceNode extends AbstractTreeNode<SliceUsage> implements Duplicate
// }
//});
}
}, progress);
}, new Runnable() {
@Override
public void run() {
Processor<SliceUsage> processor = new Processor<SliceUsage>() {
@Override
public boolean process(SliceUsage sliceUsage) {
progress.checkCanceled();
SliceNode node = new SliceNode(myProject, sliceUsage, targetEqualUsages);
synchronized (children) {
node.index = children.size();
children.add(node);
}
return true;
}
};
getValue().processChildren(processor);
}
}
);
synchronized (children) {
myCachedChildren = children;
@@ -156,9 +157,6 @@ public class SliceNode extends AbstractTreeNode<SliceUsage> implements Duplicate
@Override
protected void update(PresentationData presentation) {
if (presentation != null) {
if (duplicate != null) {
presentation.setTooltip("Duplicate node");
}
presentation.setChanged(presentation.isChanged() || changed);
changed = false;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@ public class SliceNullnessAnalyzer {
SliceManager.getInstance(root.getProject()).createToolWindow(true, root, true, SliceManager.getElementDescription(null, rootUsage.getElement(), " Grouped by Nullness") );
}
@NotNull
public static SliceRootNode createNewTree(NullAnalysisResult result, SliceRootNode oldRoot, final Map<SliceNode, NullAnalysisResult> map) {
SliceRootNode root = oldRoot.copy();
assert oldRoot.myCachedChildren.size() == 1;
@@ -65,10 +66,13 @@ public class SliceNullnessAnalyzer {
return root;
}
private static void createValueRootNode(NullAnalysisResult result, SliceRootNode oldRoot,
private static void createValueRootNode(NullAnalysisResult result,
SliceRootNode oldRoot,
final Map<SliceNode, NullAnalysisResult> map,
SliceRootNode root,
SliceNode oldRootStart, String nodeName, final int group) {
SliceNode oldRootStart,
String nodeName,
final int group) {
Collection<PsiElement> groupedByValue = result.groupedByValue[group];
if (groupedByValue.isEmpty()) {
return;
@@ -152,16 +156,17 @@ public class SliceNullnessAnalyzer {
};
}
private static NullAnalysisResult node(SliceNode node, Map<SliceNode, NullAnalysisResult> nulls) {
private static NullAnalysisResult node(@NotNull SliceNode node, @NotNull Map<SliceNode, NullAnalysisResult> nulls) {
return nulls.get(node);
}
private static Collection<PsiElement> group(SliceNode node, Map<SliceNode, NullAnalysisResult> nulls, int group) {
private static Collection<PsiElement> group(@NotNull SliceNode node, @NotNull Map<SliceNode, NullAnalysisResult> nulls, int group) {
return nulls.get(node).groupedByValue[group];
}
@NotNull
public static NullAnalysisResult calcNullableLeaves(@NotNull final SliceNode root, @NotNull AbstractTreeStructure treeStructure,
final Map<SliceNode, NullAnalysisResult> map) {
public static NullAnalysisResult calcNullableLeaves(@NotNull final SliceNode root,
@NotNull AbstractTreeStructure treeStructure,
@NotNull final Map<SliceNode, NullAnalysisResult> map) {
final SliceLeafAnalyzer.SliceNodeGuide guide = new SliceLeafAnalyzer.SliceNodeGuide(treeStructure);
WalkingState<SliceNode> walkingState = new WalkingState<SliceNode>(guide) {
@Override
@@ -263,7 +268,7 @@ public class SliceNullnessAnalyzer {
return Nullness.UNKNOWN;
}
public static class NullAnalysisResult {
static class NullAnalysisResult {
public static int NULLS = 0;
public static int NOT_NULLS = 1;
public static int UNKNOWNS = 2;
@@ -275,7 +280,7 @@ public class SliceNullnessAnalyzer {
}
}
public void add(NullAnalysisResult duplicate) {
private void add(NullAnalysisResult duplicate) {
for (int i = 0; i < groupedByValue.length; i++) {
Collection<PsiElement> elements = groupedByValue[i];
Collection<PsiElement> other = duplicate.groupedByValue[i];
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -63,7 +63,7 @@ public class SliceRootNode extends SliceNode {
@NotNull
@Override
public List<? extends AbstractTreeNode> getChildrenUnderProgress(ProgressIndicator progress) {
public List<? extends AbstractTreeNode> getChildrenUnderProgress(@NotNull ProgressIndicator progress) {
return (List<? extends AbstractTreeNode>)getChildren();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,7 +33,7 @@ import java.awt.*;
*/
public class SliceTooComplexDFAUsage extends SliceUsage {
public SliceTooComplexDFAUsage(@NotNull PsiElement element, @NotNull SliceUsage parent, @NotNull PsiSubstitutor substitutor) {
super(element, parent, substitutor);
super(element, parent, substitutor,0,"");
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,21 +35,34 @@ public class SliceUsage extends UsageInfo2UsageAdapter {
private final SliceUsage myParent;
public final SliceAnalysisParams params;
private final PsiSubstitutor mySubstitutor;
protected final int indexNesting; // 0 means bare expression 'x', 1 means x[?], 2 means x[?][?] etc
@NotNull protected final String syntheticField; // "" means no field, otherwise it's a name of fake field of container, e.g. "keys" for Map
public SliceUsage(@NotNull PsiElement element, @NotNull SliceUsage parent, @NotNull PsiSubstitutor substitutor) {
public SliceUsage(@NotNull PsiElement element,
@NotNull SliceUsage parent,
@NotNull PsiSubstitutor substitutor,
int indexNesting,
@NotNull String syntheticField) {
super(new UsageInfo(element));
myParent = parent;
mySubstitutor = substitutor;
this.syntheticField = syntheticField;
params = parent.params;
assert params != null;
this.indexNesting = indexNesting;
}
// root usage
private SliceUsage(@NotNull PsiElement element, @NotNull SliceAnalysisParams params) {
super(new UsageInfo(element));
myParent = null;
this.params = params;
mySubstitutor = PsiSubstitutor.EMPTY;
indexNesting = 0;
syntheticField = "";
}
@NotNull
public static SliceUsage createRootUsage(@NotNull PsiElement element, @NotNull SliceAnalysisParams params) {
return new SliceUsage(element, params);
}
@@ -75,11 +88,17 @@ public class SliceUsage extends UsageInfo2UsageAdapter {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
if (params.dataFlowToThis) {
SliceUtil.processUsagesFlownDownTo(element, uniqueProcessor, SliceUsage.this, mySubstitutor);
try {
if (params.dataFlowToThis) {
SliceUtil.processUsagesFlownDownTo(element, uniqueProcessor, SliceUsage.this, mySubstitutor, indexNesting,syntheticField);
}
else {
SliceForwardUtil.processUsagesFlownFromThe(element, uniqueProcessor, SliceUsage.this);
}
}
else {
SliceForwardUtil.processUsagesFlownFromThe(element, uniqueProcessor, SliceUsage.this);
catch (Exception e) {
int i = 0;
throw new RuntimeException(e);
}
}
});
@@ -94,13 +113,14 @@ public class SliceUsage extends UsageInfo2UsageAdapter {
return params.scope;
}
@NotNull
SliceUsage copy() {
PsiElement element = getUsageInfo().getElement();
return getParent() == null ? createRootUsage(element, params) : new SliceUsage(element, getParent(),mySubstitutor);
return getParent() == null ? createRootUsage(element, params) : new SliceUsage(element, getParent(),mySubstitutor,indexNesting,syntheticField);
}
@NotNull
public PsiSubstitutor getSubstitutor() {
return mySubstitutor;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
package com.intellij.slicer;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.util.Segment;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
@@ -25,11 +27,14 @@ import com.intellij.ui.SimpleTextAttributes;
import com.intellij.usageView.UsageTreeColors;
import com.intellij.usageView.UsageTreeColorsScheme;
import com.intellij.usages.TextChunk;
import com.intellij.util.Processor;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.*;
import java.util.List;
/**
* @author cdr
@@ -63,15 +68,35 @@ public class SliceUsageCellRenderer extends ColoredTreeCellRenderer {
public void customizeCellRendererFor(@NotNull SliceUsage sliceUsage) {
boolean isForcedLeaf = sliceUsage instanceof SliceDereferenceUsage;
TextChunk[] text = sliceUsage.getPresentation().getText();
TextChunk[] text = sliceUsage.getText();
final List<TextRange> usageRanges = new SmartList<TextRange>();
sliceUsage.processRangeMarkers(new Processor<Segment>() {
@Override
public boolean process(Segment segment) {
usageRanges.add(TextRange.create(segment));
return true;
}
});
boolean isInsideContainer = sliceUsage.indexNesting != 0;
for (TextChunk textChunk : text) {
SimpleTextAttributes attributes = textChunk.getSimpleAttributesIgnoreBackground();
if (isForcedLeaf) {
attributes = attributes.derive(attributes.getStyle(), Color.LIGHT_GRAY, attributes.getBgColor(), attributes.getWaveColor());
}
boolean inUsage = (attributes.getFontStyle() & Font.BOLD) != 0;
if (isInsideContainer && inUsage) {
//Color darker = Color.BLACK;//attributes.getBgColor() == null ? Color.BLACK : attributes.getBgColor().darker();
//attributes = attributes.derive(SimpleTextAttributes.STYLE_OPAQUE, attributes.getFgColor(), UIUtil.getTreeBackground().brighter(), attributes.getWaveColor());
//setMyBorder(IdeBorderFactory.createRoundedBorder(10, 3));
//setPaintFocusBorder(true);
}
append(textChunk.getText(), attributes);
}
for (int i=0; i<sliceUsage.indexNesting;i++) {
append(" (Tracking container contents"+(sliceUsage.syntheticField.isEmpty() ? "" : " '"+sliceUsage.syntheticField+"'")+")",SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
}
PsiElement element = sliceUsage.getElement();
PsiMethod method;
PsiClass aClass;
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,12 @@
*/
package com.intellij.slicer;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInspection.dataFlow.DfaUtil;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiSubstitutorImpl;
import com.intellij.psi.impl.source.DummyHolder;
@@ -34,6 +37,7 @@ import com.intellij.util.ArrayUtilRt;
import com.intellij.util.Processor;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import org.intellij.lang.annotations.Flow;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -49,14 +53,33 @@ public class SliceUtil {
public static boolean processUsagesFlownDownTo(@NotNull PsiElement expression,
@NotNull Processor<SliceUsage> processor,
@NotNull SliceUsage parent,
@NotNull PsiSubstitutor parentSubstitutor) {
@NotNull PsiSubstitutor parentSubstitutor,
int indexNesting,
@NotNull String syntheticField) {
assert indexNesting >= 0 : indexNesting;
expression = simplify(expression);
PsiElement original = expression;
if (expression instanceof PsiArrayAccessExpression) {
// now start tracking the array instead of element
expression = ((PsiArrayAccessExpression)expression).getArrayExpression();
indexNesting++;
}
if (expression instanceof PsiExpressionList && indexNesting != 0 && expression.getParent() instanceof PsiMethodCallExpression) {
// expression list ends up here if we track varargs
// unfold varargs list into individual expressions
PsiExpression[] expressions = ((PsiExpressionList)expression).getExpressions();
for (PsiExpression arg : expressions) {
if (!handToProcessor(arg, processor, parent, parentSubstitutor, indexNesting -1, syntheticField)) return false;
}
return true;
}
boolean needToReportDeclaration = false;
if (expression instanceof PsiReferenceExpression) {
PsiElement element = SliceForwardUtil.complexify(expression);
if (element instanceof PsiExpression && PsiUtil.isOnAssignmentLeftHand((PsiExpression)element)) {
PsiExpression rightSide = ((PsiAssignmentExpression)element.getParent()).getRExpression();
return rightSide == null || handToProcessor(rightSide, processor, parent, parentSubstitutor);
return rightSide == null || handToProcessor(rightSide, processor, parent, parentSubstitutor, indexNesting, syntheticField);
}
PsiReferenceExpression ref = (PsiReferenceExpression)expression;
JavaResolveResult result = ref.advancedResolve(false);
@@ -66,9 +89,13 @@ public class SliceUtil {
resolved = resolved.getNavigationElement();
}
if (resolved instanceof PsiMethod && expression.getParent() instanceof PsiMethodCallExpression) {
return processUsagesFlownDownTo(expression.getParent(), processor, parent, parentSubstitutor);
return processUsagesFlownDownTo(expression.getParent(), processor, parent, parentSubstitutor, indexNesting, syntheticField);
}
if (!(resolved instanceof PsiVariable)) return true;
// check for container item modifications, like "array[i] = expression;"
addContainerReferences((PsiVariable)resolved, processor, parent, parentSubstitutor, indexNesting, syntheticField);
needToReportDeclaration = true;
expression = resolved;
}
if (expression instanceof PsiVariable) {
@@ -81,25 +108,48 @@ public class SliceUtil {
final Set<PsiExpression> expressions = new THashSet<PsiExpression>(values);
PsiExpression initializer = variable.getInitializer();
if (initializer != null && expressions.isEmpty()) expressions.add(initializer);
boolean initializerReported = false;
for (PsiExpression exp : expressions) {
if (!handToProcessor(exp, processor, parent, parentSubstitutor)) return false;
if (!handToProcessor(exp, processor, parent, parentSubstitutor, indexNesting, syntheticField)) return false;
if (exp == initializer) initializerReported = true;
}
if (!initializerReported && needToReportDeclaration) { // otherwise we'll reach var declaration anyway because it is the initializer' parent
// parameter or variable declaration can be far away from its usage (except for variable initializer) so report it separately
return handToProcessor(variable, processor, parent, parentSubstitutor, indexNesting, syntheticField);
}
if (variable instanceof PsiField) {
return processFieldUsages((PsiField)variable, processor, parent, parentSubstitutor);
return processFieldUsages((PsiField)variable, parent, parentSubstitutor, processor);
}
else if (variable instanceof PsiParameter) {
return processParameterUsages((PsiParameter)variable, processor, parent, parentSubstitutor);
return processParameterUsages((PsiParameter)variable, parent, parentSubstitutor, indexNesting, syntheticField, processor);
}
}
if (expression instanceof PsiMethodCallExpression) {
if (expression instanceof PsiMethodCallExpression) { // ctr call can't return value or be container get, so don't use PsiCall here
Flow anno = isMethodFlowAnnotated(((PsiMethodCallExpression)expression).resolveMethod());
if (anno != null) {
String target = anno.target();
if (target.equals(Flow.DEFAULT_TARGET)) target = Flow.RETURN_METHOD_TARGET;
if (target.equals(Flow.RETURN_METHOD_TARGET)) {
PsiExpression qualifier = ((PsiMethodCallExpression)expression).getMethodExpression().getQualifierExpression();
if (qualifier != null) {
int nesting = calcNewIndexNesting(indexNesting, anno);
String source = anno.source();
if (source.equals(Flow.DEFAULT_SOURCE)) source = Flow.THIS_SOURCE;
String synthetic = StringUtil.trimStart(StringUtil.trimStart(source, Flow.THIS_SOURCE),".");
return processUsagesFlownDownTo(qualifier, processor, parent, parentSubstitutor, nesting, synthetic);
}
}
}
return processMethodReturnValue((PsiMethodCallExpression)expression, processor, parent, parentSubstitutor);
}
if (expression instanceof PsiConditionalExpression) {
PsiConditionalExpression conditional = (PsiConditionalExpression)expression;
PsiExpression thenE = conditional.getThenExpression();
PsiExpression elseE = conditional.getElseExpression();
if (thenE != null && !handToProcessor(thenE, processor, parent, parentSubstitutor)) return false;
if (elseE != null && !handToProcessor(elseE, processor, parent, parentSubstitutor)) return false;
if (thenE != null && !handToProcessor(thenE, processor, parent, parentSubstitutor, indexNesting, syntheticField)) return false;
if (elseE != null && !handToProcessor(elseE, processor, parent, parentSubstitutor, indexNesting, syntheticField)) return false;
}
if (expression instanceof PsiAssignmentExpression) {
PsiAssignmentExpression assignment = (PsiAssignmentExpression)expression;
@@ -107,12 +157,45 @@ public class SliceUtil {
PsiExpression rExpression = assignment.getRExpression();
if (tokenType == JavaTokenType.EQ && rExpression != null) {
return processUsagesFlownDownTo(rExpression, processor, parent, parentSubstitutor);
return processUsagesFlownDownTo(rExpression, processor, parent, parentSubstitutor, indexNesting, syntheticField);
}
}
if (indexNesting != 0) {
// consider container creation
PsiElement initializer = expression instanceof PsiNewExpression ? ((PsiNewExpression)expression).getArrayInitializer() : expression;
if (initializer instanceof PsiArrayInitializerExpression) {
for (PsiExpression init : ((PsiArrayInitializerExpression)initializer).getInitializers()) {
if (!handToProcessor(init, processor, parent, parentSubstitutor, indexNesting - 1, syntheticField)) return false;
}
}
// check for constructor put arguments
if (expression instanceof PsiNewExpression &&
!processContainerPutArguments((PsiNewExpression)expression, processor, parent, parentSubstitutor, indexNesting, syntheticField)) {
return false;
}
}
return true;
}
private static Flow isMethodFlowAnnotated(PsiMethod method) {
if (method == null) return null;
return AnnotationUtil.findAnnotationInHierarchy(method, Flow.class);
}
private static Flow isParamFlowAnnotated(PsiMethod method, int paramIndex) {
PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length <= paramIndex) {
if (parameters.length != 0 && parameters[parameters.length-1].isVarArgs()) {
paramIndex = parameters.length-1;
}
else {
return null;
}
}
return AnnotationUtil.findAnnotationInHierarchy(parameters[paramIndex], Flow.class);
}
private static PsiElement simplify(@NotNull PsiElement expression) {
if (expression instanceof PsiParenthesizedExpression) {
return simplify(((PsiParenthesizedExpression)expression).getExpression());
@@ -123,15 +206,16 @@ public class SliceUtil {
return expression;
}
private static boolean handToProcessor(@NotNull PsiExpression exp,
private static boolean handToProcessor(@NotNull PsiElement expression,
@NotNull Processor<SliceUsage> processor,
@NotNull SliceUsage parent,
@NotNull PsiSubstitutor substitutor) {
final PsiExpression realExpression =
exp.getParent() instanceof DummyHolder ? (PsiExpression)exp.getParent().getContext() : exp;
@NotNull PsiSubstitutor substitutor,
int indexNesting,
@NotNull String syntheticField) {
final PsiElement realExpression = expression.getParent() instanceof DummyHolder ? expression.getParent().getContext() : expression;
assert realExpression != null;
if (!(realExpression instanceof PsiCompiledElement)) {
SliceUsage usage = createSliceUsage(realExpression, parent, substitutor);
SliceUsage usage = createSliceUsage(realExpression, parent, substitutor, indexNesting, syntheticField);
if (!processor.process(usage)) return false;
}
return true;
@@ -184,7 +268,7 @@ public class SliceUtil {
if (returnValue == null) return;
PsiType right = superSubstitutor.substitute(superSubstitutor.substitute(returnValue.getType()));
if (right == null || !TypeConversionUtil.isAssignable(parentType, right)) return;
if (!handToProcessor(returnValue, processor, parent, substitutor)) {
if (!handToProcessor(returnValue, processor, parent, substitutor, parent.indexNesting, "")) {
stopWalking();
result[0] = false;
}
@@ -195,19 +279,21 @@ public class SliceUtil {
return result[0];
}
private static boolean processFieldUsages(@NotNull final PsiField field, @NotNull final Processor<SliceUsage> processor, @NotNull final SliceUsage parent,
@NotNull final PsiSubstitutor parentSubstitutor) {
private static boolean processFieldUsages(@NotNull final PsiField field,
@NotNull final SliceUsage parent,
@NotNull final PsiSubstitutor parentSubstitutor,
@NotNull final Processor<SliceUsage> processor) {
if (field.hasInitializer()) {
PsiExpression initializer = field.getInitializer();
if (initializer != null && !(field instanceof PsiCompiledElement)) {
if (!handToProcessor(initializer, processor, parent, parentSubstitutor)) return false;
if (!handToProcessor(initializer, processor, parent, parentSubstitutor, parent.indexNesting, "")) return false;
}
}
SearchScope searchScope = parent.getScope().toSearchScope();
return ReferencesSearch.search(field, searchScope).forEach(new Processor<PsiReference>() {
@Override
public boolean process(final PsiReference reference) {
SliceManager.getInstance(field.getProject()).checkCanceled();
ProgressManager.checkCanceled();
PsiElement element = reference.getElement();
if (!(element instanceof PsiReferenceExpression)) return true;
if (element instanceof PsiCompiledElement) {
@@ -221,16 +307,16 @@ public class SliceUtil {
PsiType rtype = rExpression.getType();
PsiType ftype = field.getType();
if (TypeConversionUtil.isAssignable(parentSubstitutor.substitute(ftype), parentSubstitutor.substitute(rtype))) {
return handToProcessor(rExpression, processor, parent, parentSubstitutor);
return handToProcessor(rExpression, processor, parent, parentSubstitutor, parent.indexNesting, "");
}
}
if (parentExpr instanceof PsiPrefixExpression && ((PsiPrefixExpression)parentExpr).getOperand() == referenceExpression && ( ((PsiPrefixExpression)parentExpr).getOperationTokenType() == JavaTokenType.PLUSPLUS || ((PsiPrefixExpression)parentExpr).getOperationTokenType() == JavaTokenType.MINUSMINUS)) {
PsiPrefixExpression prefixExpression = (PsiPrefixExpression)parentExpr;
return handToProcessor(prefixExpression, processor, parent, parentSubstitutor);
return handToProcessor(prefixExpression, processor, parent, parentSubstitutor, parent.indexNesting, "");
}
if (parentExpr instanceof PsiPostfixExpression && ((PsiPostfixExpression)parentExpr).getOperand() == referenceExpression && ( ((PsiPostfixExpression)parentExpr).getOperationTokenType() == JavaTokenType.PLUSPLUS || ((PsiPostfixExpression)parentExpr).getOperationTokenType() == JavaTokenType.MINUSMINUS)) {
PsiPostfixExpression postfixExpression = (PsiPostfixExpression)parentExpr;
return handToProcessor(postfixExpression, processor, parent, parentSubstitutor);
return handToProcessor(postfixExpression, processor, parent, parentSubstitutor, parent.indexNesting, "");
}
return true;
}
@@ -238,8 +324,12 @@ public class SliceUtil {
}
@NotNull
public static SliceUsage createSliceUsage(@NotNull PsiElement element, @NotNull SliceUsage parent, @NotNull PsiSubstitutor substitutor) {
return new SliceUsage(simplify(element), parent, substitutor);
public static SliceUsage createSliceUsage(@NotNull PsiElement element,
@NotNull SliceUsage parent,
@NotNull PsiSubstitutor substitutor,
int indexNesting,
@NotNull String syntheticField) {
return new SliceUsage(simplify(element), parent, substitutor,indexNesting, syntheticField);
}
@NotNull
@@ -247,12 +337,25 @@ public class SliceUtil {
return new SliceTooComplexDFAUsage(simplify(element), parent, substitutor);
}
static boolean processParameterUsages(@NotNull final PsiParameter parameter, @NotNull final Processor<SliceUsage> processor, @NotNull final SliceUsage parent,
@NotNull final PsiSubstitutor parentSubstitutor) {
private static boolean processParameterUsages(@NotNull final PsiParameter parameter,
@NotNull final SliceUsage parent,
@NotNull final PsiSubstitutor parentSubstitutor,
final int indexNesting,
@NotNull final String syntheticField,
@NotNull final Processor<SliceUsage> processor) {
PsiElement declarationScope = parameter.getDeclarationScope();
if (declarationScope instanceof PsiForeachStatement) {
PsiForeachStatement statement = (PsiForeachStatement)declarationScope;
PsiExpression iterated = statement.getIteratedValue();
if (statement.getIterationParameter() == parameter && iterated != null) {
if (!handToProcessor(iterated, processor, parent, parentSubstitutor, indexNesting + 1, syntheticField)) return false;
}
return true;
}
if (!(declarationScope instanceof PsiMethod)) return true;
final PsiMethod method = (PsiMethod)declarationScope;
final PsiType actualType = parameter.getType();
final PsiType actualParameterType = parameter.getType();
final PsiParameter[] actualParameters = method.getParameterList().getParameters();
final int paramSeqNo = ArrayUtilRt.find(actualParameters, parameter);
@@ -266,7 +369,7 @@ public class SliceUtil {
if (!MethodReferencesSearch.search(superMethod, parent.getScope().toSearchScope(), true).forEach(new Processor<PsiReference>() {
@Override
public boolean process(final PsiReference reference) {
SliceManager.getInstance(parameter.getProject()).checkCanceled();
ProgressManager.checkCanceled();
synchronized (processed) {
if (!processed.add(reference)) return true;
}
@@ -302,7 +405,17 @@ public class SliceUtil {
if (paramSeqNo >= expressions.length) {
return true;
}
PsiExpression passExpression = expressions[paramSeqNo];
PsiElement passExpression;
PsiType actualExpressionType;
if (actualParameterType instanceof PsiEllipsisType) {
passExpression = argumentList;
//passExpression = createArrayInitializerFromExpressions(argumentList, ((PsiEllipsisType)actualType).getComponentType(), expressions);
actualExpressionType = expressions[paramSeqNo].getType();
}
else {
passExpression = expressions[paramSeqNo];
actualExpressionType = ((PsiExpression)passExpression).getType();
}
Project project = argumentList.getProject();
PsiElement element = result.getElement();
@@ -324,14 +437,23 @@ public class SliceUtil {
PsiSubstitutor combined = unify(substitutor, parentSubstitutor, project);
if (combined == null) return true;
PsiType substituted = combined.substitute(passExpression.getType());
//PsiType substituted = combined.substitute(passExpression.getType());
PsiType substituted = combined.substitute(actualExpressionType);
if (substituted instanceof PsiPrimitiveType) {
final PsiClassType boxedType = ((PsiPrimitiveType)substituted).getBoxedType(argumentList);
substituted = boxedType != null ? boxedType : substituted;
}
if (substituted == null || !TypeConversionUtil.areTypesConvertible(substituted, actualType)) return true;
if (substituted == null) return true;
PsiType typeToCheck;
if (actualParameterType instanceof PsiEllipsisType) {
typeToCheck = ((PsiEllipsisType)actualParameterType).getComponentType();
}
else {
typeToCheck = actualParameterType;
}
if (!TypeConversionUtil.areTypesConvertible(substituted, typeToCheck)) return true;
return handToProcessor(passExpression, processor, parent, combined);
return handToProcessor(passExpression, processor, parent, combined, indexNesting, syntheticField);
}
})) {
return false;
@@ -341,6 +463,119 @@ public class SliceUtil {
return true;
}
private static void addContainerReferences(@NotNull PsiVariable variable,
@NotNull final Processor<SliceUsage> processor,
@NotNull final SliceUsage parent,
@NotNull final PsiSubstitutor parentSubstitutor,
final int indexNesting,
@NotNull final String syntheticField) {
if (indexNesting != 0) {
ReferencesSearch.search(variable).forEach(new Processor<PsiReference>() {
@Override
public boolean process(PsiReference reference) {
PsiElement element = reference.getElement();
if (element instanceof PsiExpression && !element.getManager().areElementsEquivalent(element, parent.getElement())) {
PsiExpression expression = (PsiExpression)element;
if (!addContainerItemModification(expression, processor, parent, parentSubstitutor, indexNesting, syntheticField)) return false;
}
return true;
}
});
}
}
private static boolean addContainerItemModification(@NotNull PsiExpression expression,
@NotNull Processor<SliceUsage> processor,
@NotNull SliceUsage parent,
@NotNull PsiSubstitutor parentSubstitutor,
int indexNesting,
@NotNull String syntheticField) {
PsiElement parentElement = expression.getParent();
if (parentElement instanceof PsiArrayAccessExpression &&
((PsiArrayAccessExpression)parentElement).getArrayExpression() == expression &&
PsiUtil.isAccessedForWriting((PsiExpression)parentElement)) {
if (PsiUtil.isOnAssignmentLeftHand((PsiExpression)parentElement)) {
PsiExpression rightSide = ((PsiAssignmentExpression)parentElement.getParent()).getRExpression();
return rightSide == null || handToProcessor(rightSide, processor, parent, parentSubstitutor, indexNesting -1, syntheticField);
}
}
PsiElement grand = parentElement == null ? null : parentElement.getParent();
if (grand instanceof PsiCallExpression) {
if (!processContainerPutArguments((PsiCallExpression)grand, processor, parent, parentSubstitutor, indexNesting, syntheticField)) return false;
}
return true;
}
private static boolean processContainerPutArguments(PsiCallExpression call,
Processor<SliceUsage> processor,
SliceUsage parent,
PsiSubstitutor parentSubstitutor,
int indexNesting,
@NotNull String syntheticField) {
assert indexNesting != 0;
JavaResolveResult result = call.resolveMethodGenerics();
PsiMethod method = (PsiMethod)result.getElement();
if (method != null) {
int parametersCount = method.getParameterList().getParametersCount();
Flow[] annotations = new Flow[parametersCount];
for (int i=0; i<parametersCount;i++) {
annotations[i] = isParamFlowAnnotated(method, i);
}
PsiExpression[] expressions = call.getArgumentList().getExpressions();
PsiParameter[] parameters = method.getParameterList().getParameters();
for (int i = 0; i < expressions.length; i++) {
PsiExpression argument = expressions[i];
Flow anno;
PsiParameter parameter;
if (i>=parameters.length) {
if (parameters.length != 0 && parameters[parameters.length-1].isVarArgs()) {
anno = annotations[parameters.length - 1];
parameter = parameters[parameters.length-1];
}
else {
break;
}
}
else {
anno = annotations[i];
parameter = parameters[i];
}
if (anno != null) {
String target = anno.target();
if (target.equals(Flow.DEFAULT_TARGET)) target = Flow.THIS_TARGET;
if (target.startsWith(Flow.THIS_TARGET)) {
String paramSynthetic = StringUtil.trimStart(StringUtil.trimStart(target, Flow.THIS_TARGET),".");
if (paramSynthetic.equals(syntheticField)) {
PsiSubstitutor substitutor = unify(result.getSubstitutor(), parentSubstitutor, argument.getProject());
int nesting = calcNewIndexNesting(indexNesting, anno);
if (!handToProcessor(argument, processor, parent, substitutor, nesting, paramSynthetic)) return false;
}
}
}
// check flow parameter to another param
for (int si=0; si<annotations.length; si++) {
if (si == i) continue;
Flow sourceAnno = annotations[si];
if (sourceAnno == null) continue;
if (sourceAnno.target().equals(parameter.getName())) {
int newNesting = calcNewIndexNesting(indexNesting, sourceAnno);
PsiExpression sourceArgument = expressions[si];
PsiSubstitutor substitutor = unify(result.getSubstitutor(), parentSubstitutor, argument.getProject());
if (!handToProcessor(sourceArgument, processor, parent, substitutor, newNesting, syntheticField)) return false;
}
}
}
}
return true;
}
private static int calcNewIndexNesting(int indexNesting, Flow anno) {
int nestingDelta = (anno.sourceIsContainer() ? 1 : 0) - (anno.targetIsContainer() ? 1 : 0);
return indexNesting + nestingDelta;
}
@NotNull
private static PsiSubstitutor removeRawMappingsLeftFromResolve(@NotNull PsiSubstitutor substitutor) {
Map<PsiTypeParameter, PsiType> map = null;
@@ -351,9 +586,9 @@ public class SliceUtil {
}
}
if (map == null) return substitutor;
Map<PsiTypeParameter, PsiType> newmap = new THashMap<PsiTypeParameter, PsiType>(substitutor.getSubstitutionMap());
newmap.keySet().removeAll(map.keySet());
return PsiSubstitutorImpl.createSubstitutor(newmap);
Map<PsiTypeParameter, PsiType> newMap = new THashMap<PsiTypeParameter, PsiType>(substitutor.getSubstitutionMap());
newMap.keySet().removeAll(map.keySet());
return PsiSubstitutorImpl.createSubstitutor(newMap);
}
@Nullable
@@ -0,0 +1,93 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiAnnotationMemberValue;
import com.intellij.psi.PsiNameValuePair;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.Annotation;
import java.lang.annotation.IncompleteAnnotationException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
class AnnotationInvocationHandler implements InvocationHandler {
@NotNull private final Class<? extends Annotation> type;
@NotNull private final PsiAnnotation myAnnotation;
AnnotationInvocationHandler(@NotNull Class<? extends Annotation> type, @NotNull PsiAnnotation annotation) {
this.type = type;
myAnnotation = annotation;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
String member = method.getName();
Class<?>[] paramTypes = method.getParameterTypes();
assert paramTypes.length == 0;
if (member.equals("toString")) {
return toStringImpl();
}
if (member.equals("annotationType")) {
return type;
}
// Handle annotation member accessors
PsiAnnotationMemberValue value = myAnnotation.findAttributeValue(member);
if (value == null) {
throw new IncompleteAnnotationException(type, member);
}
Object result = JavaPsiFacade.getInstance(myAnnotation.getProject()).getConstantEvaluationHelper().computeConstantExpression(value);
if (result == null) {
throw new IncompleteAnnotationException(type, member+". (unable to evaluate annotation value '"+value+"')");
}
// todo arrays
return result;
}
/**
* Implementation of dynamicProxy.toString()
*/
private String toStringImpl() {
StringBuilder result = new StringBuilder(128);
result.append('@');
result.append(type.getName());
result.append('(');
boolean firstMember = true;
PsiNameValuePair[] attributes = myAnnotation.getParameterList().getAttributes();
for (PsiNameValuePair e : attributes) {
if (firstMember) {
firstMember = false;
}
else {
result.append(", ");
}
result.append(e.getName());
result.append('=');
PsiAnnotationMemberValue value = e.getValue();
result.append(value == null ? "null" : value.getText());
}
result.append(')');
return result.toString();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,8 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Proxy;
import java.util.*;
/**
@@ -439,4 +441,18 @@ public class AnnotationUtil {
Object constValue = JavaPsiFacade.getInstance(anno.getProject()).getConstantEvaluationHelper().computeConstantExpression(attrValue);
return constValue instanceof String ? (String)constValue : null;
}
@Nullable
public static Boolean getBooleanAttributeValue(PsiAnnotation anno, @Nullable final String attributeName) {
PsiAnnotationMemberValue attrValue = anno.findAttributeValue(attributeName);
Object constValue = JavaPsiFacade.getInstance(anno.getProject()).getConstantEvaluationHelper().computeConstantExpression(attrValue);
return constValue instanceof Boolean ? (Boolean)constValue : null;
}
@Nullable
public static <T extends Annotation> T findAnnotationInHierarchy(@NotNull PsiModifierListOwner listOwner, @NotNull Class<T> annotationClass) {
PsiAnnotation annotation = findAnnotationInHierarchy(listOwner, Collections.singleton(annotationClass.getName()));
if (annotation == null) return null;
return (T)Proxy.newProxyInstance(
annotationClass.getClassLoader(), new Class<?>[]{annotationClass}, new AnnotationInvocationHandler(annotationClass, annotation));
}
}
@@ -0,0 +1,23 @@
class AnonArray {
void x() {
final int[] params = <flown11>{<flown111>-1};
calcIt(new Runnable() {
public void run() {
params[0] = <flown12>z(<flown12111>2);
}
});
y(<flown1>params[0]);
}
void y(int <caret>arg) {
}
void calcIt(Runnable r) {
r.run();
}
int z(int <flown1211>arg) {
return <flown121>arg;
}
}
@@ -1,16 +1,16 @@
class Test {
public static void main(String[] args) {
final String a = <flown11>args[0];
public static void main(String[] <flown1111>args) {
final String a = <flown111>args[0];
final String b = args[1];
new Runnable() {
public void run() {
System.out.println(f(<flown1>a, b));
System.out.println(f(<flown11>a, b));
}
}.run();
}
private static String f(String a, String b) {
private static String f(String <flown1>a, String b) {
return <caret>a + b;
}
@@ -0,0 +1,9 @@
class ArrayCopy {
void f(String s) {
String[] l = new String[]{"x"};
String[] y = new String[1];
System.arraycopy(l, 0, y, 0, l.length);
String <caret>c = y[0];
}
}
@@ -0,0 +1,16 @@
class Test {
private void x(int[] <flown1111>params) {
if (params == null) {
params = <flown1112>new int[]{<flown11121>-1};
}
params[3] = <flown1113>44;
for (int <flown11>param : <flown111>params) y(<flown1>param);
}
private void y(int <caret>arg) {
}
private void z() {
x(<flown11111>new int[]{<flown111111>1,<flown111112>8});
}
}
@@ -0,0 +1,37 @@
package x;
import java.util.*;
import org.intellij.lang.annotations.Flow;
public class ListTack {
void f(String <caret>s) {
}
void g(List<String> l) {
l.add(1, "uuu");
f(l.get(0));
}
void h() {
ArrayList<String> strings = new ArrayList<String>();
strings.add("x");
X<String> s2 = new X<String>(strings);
s2.add("y");
List<String> s3 = new ArrayList<String>();
s3.addAll(s2.toCollection());
Collection<String> s4 = new ArrayList<String>(s3.subList(0,1));
g(new ArrayList<String>(s4));
}
class X<T> {
X (@Flow(sourceIsContainer = true, targetIsContainer = true) Collection<T> input) {}
@Flow(sourceIsContainer = true)
T get() { return null;}
void add(@Flow(targetIsContainer = true) T item) {}
@Flow(sourceIsContainer=true, targetIsContainer = true)
Collection<T> toCollection() { return null; }
}
}
@@ -1,9 +1,9 @@
class Pair<A, B> {
public final A first;
public final A <flown111>first;
public final B second;
public Pair(A first, B second) {
this.first = <flown111>first;
public Pair(A <flown11111>first, B second) {
this.first = <flown1111>first;
this.second = second;
}
@@ -15,8 +15,8 @@ class Pair<A, B> {
return second;
}
public static <A, B> Pair<A, B> create(A first, B second) {
return new Pair<A,B>(<flown1111>first, second);
public static <A, B> Pair<A, B> create(A <flown1111111>first, B second) {
return new Pair<A,B>(<flown111111>first, second);
}
public final boolean equals(Object o){
@@ -1,13 +1,13 @@
class s {
{
x(<flown11211>"xxx");
x(<flown1121111>"xxx");
}
String x(String g) {
String d = <flown1>foo(<flown1121>g);
String x(String <flown112111>g) {
String d = <flown1>foo(<flown11211>g);
return <caret>d;
}
private String foo(String i) {
private String foo(String <flown1121>i) {
return <flown11>i==null ? <flown111>null : <flown112>i;
}
}
@@ -1,16 +1,16 @@
class Test {
public static void main(String[] args) {
final String a = <flown1121>args[0];
final String b = <flown11>Test.class == null ? <flown111>args[1] : <flown112>a;
public static void main(String[] <flown111211><flown11111>args) {
final String a = <flown11121>args[0];
final String b = <flown111>Test.class == null ? <flown1111>args[1] : <flown1112>a;
new Runnable() {
public void run() {
System.out.println(f(a, <flown1>b));
System.out.println(f(a, <flown11>b));
}
}.run();
}
private static String f(String a, String b) {
private static String f(String a, String <flown1>b) {
return a + <caret>b;
}
@@ -1,16 +1,17 @@
import java.util.Map;
class M<X, Y> {
interface Map<X,Y> {
Y get(X x);
}
Map<X, Y> m;
Y y;
Y <flown11111>y;
public M(Map<X, Y> m, Y y) {
public M(Map<X, Y> m, Y <flown1111111>y) {
this.m = m;
this.y = <flown11111>y;
this.y = <flown111111>y;
}
public static <MX, MY> M<MX, MY> makeM(MY y) {
return new M<MX, MY>(null, <flown111111>y);
public static <MX, MY> M<MX, MY> makeM(MY <flown111111111>y) {
return new M<MX, MY>(null, <flown11111111>y);
}
Y get(X x) {
@@ -19,7 +20,7 @@ class M<X, Y> {
}
public static void g() {
String <caret>a = <flown1>f(M.<String, String>makeM(<flown1111111>"a"), "k");
String <caret>a = <flown1>f(M.<String, String>makeM(<flown1111111111>"a"), "k");
}
public static <A> A f(M<A, A> a, A ka) {
@@ -0,0 +1,23 @@
import java.util.ArrayList;
import java.util.List;
public class ListTack {
void f(String s) {
String <caret>p = s;
}
void g(List<String> list) {
list.add(1, "uuu");
f(list.get(0));
f(list.remove(0));
}
void h() {
List<String> g = new ArrayList<String>();
g.add("zzz");
List<String> list = new ArrayList<String>();
list.add("xxx");
g(list);
}
}
@@ -1,24 +1,24 @@
public class WW {
void f(String ddd) {
void f(String <flown11111>ddd) {
if (hashCode() == 0)
ddd = <flown1111>"dd";
foo(<flown111>ddd);
ddd = <flown11112>"dd";
foo(<flown1111>ddd);
}
{
f(<flown1112>"xxx");
f(<flown111111>"xxx");
}
{
x(<flown1121>"zzz");
x(<flown111211>"zzz");
}
String x(String g) {
String d = <flown1>foo(<flown112>g);
String x(String <flown11121>g) {
String d = <flown1>foo(<flown1112>g);
return <caret>d;
}
private String foo(String i) {
private String foo(String <flown111>i) {
return <flown11>i;
}
}
@@ -0,0 +1,31 @@
package x;
import java.util.ArrayList;
import java.util.List;
public class ListTack {
void f(String <caret>s) {
}
void g(List<String> <flown111>l) {
l.add(1, <flown112>"uuu");
// f(l.get(0));
// f(l.remove(0));
Object[] array = <flown11>l.toArray();
f((String)<flown1>array[0]);
}
void h() {
List<String> g = <flown111131>new ArrayList<String>();
g.add(<flown111132>"zzz");
List<String> list = <flown11111>new ArrayList<String>();
list.add(<flown11112>"xxx");
list.addAll(<flown11113>g);
g(<flown1111>list);
}
}
@@ -1,5 +1,5 @@
class S {
String x(String g) {
String x(String <flown11>g) {
String d = (String)(<flown1>g);
return <caret>d;
}
@@ -0,0 +1,10 @@
import java.util.*;
class Map5 {
void f(String s, Map<String, String> m) {
m.put("x", s);
Set<String> keys = m.keySet();
String[] keyArray = keys.toArray(new String[0]);
String <caret>v = keyArray[0];
}
}
@@ -0,0 +1,8 @@
import java.util.Map;
class Map4 {
void f(String s, Map<String, String> m) {
m.put(s, "y");
String <caret>v = m.get(s);
}
}
@@ -1,14 +1,13 @@
class s {
{
x(<flown1211>"xxx");
x(<flown121111>"xxx");
}
String x(String g) {
String d = <flown1>foo(<flown121>g);
String x(String <flown12111>g) {
String d = <flown1>foo(<flown1211>g);
return <caret>d;
}
private String foo(String i) {
private String foo(String <flown121>i) {
if (i==null) {
new Callable<String>(){
public String call() throws Exception {
@@ -3,7 +3,7 @@ public class OverrideSlice {
int f(int i);
}
class O implements I {
public int f(int i) {
public int f(int <flown111>i) {
return <flown11>i;
}
}
@@ -14,9 +14,9 @@ public class OverrideSlice {
}
{
f(<flown1111>1, new O());
f(<flown111111>1, new O());
}
void f(int c, I i) {
int <caret>x = <flown1>i.f(<flown111>c);
void f(int <flown11111>c, I i) {
int <caret>x = <flown1>i.f(<flown1111>c);
}
}
@@ -1,10 +1,11 @@
class WW {
{ String ddd;
ddd = foo(<flown1>1);
{
String ddd;
ddd = foo(<flown11>1);
}
private String foo(int i) {
return <caret>i+"";
}
private String foo(int <flown1>i) {
return <caret>i+"";
}
}
@@ -1,14 +1,14 @@
class A {
private static class Tuple<X,Y> {
public final X x;
public final X <flown11>x;
public final Y y;
public Tuple(X x,Y y) {this.x=<flown11>x; this.y=y;}
public Tuple(X <flown1111>x,Y y) {this.x=<flown111>x; this.y=y;}
}
private static class Foo {
void f() {
Tuple<Integer,Integer> t1 = new Tuple<Integer,Integer>(1,2);
Tuple<String,String> t2 = new Tuple<String,String>(<flown111>"a","b");
Tuple<String,String> t2 = new Tuple<String,String>(<flown11111>"a","b");
Tuple<Foo,Foo> t2w = new Tuple<Foo,Foo>(null,null);
String <caret>x = t2.<flown1>x;
@@ -2,13 +2,13 @@ class A {
private static class Tuple<X,Y> {
public final X <caret>x;
public final Y y;
public Tuple(X x,Y y) {this.x=<flown1>x; this.y=y;}
public Tuple(X <flown11>x,Y y) {this.x=<flown1>x; this.y=y;}
}
private static class Foo {
void f() {
Tuple<Integer,Integer> t1 = new Tuple<Integer,Integer>(<flown11>1,2);
Tuple<String,String> t2 = new Tuple<String,String>(<flown12>"a","b");
Tuple<Integer,Integer> t1 = new Tuple<Integer,Integer>(<flown111>1,2);
Tuple<String,String> t2 = new Tuple<String,String>(<flown112>"a","b");
String x = t2.x;
}
}
@@ -1,8 +1,8 @@
class A {
private static class Tuple<X,Y> {
public final X x;
public final X <flown11>x;
public final Y y;
public Tuple(X x,Y y) {this.x=<flown11>x; this.y=y;}
public Tuple(X <flown1111>x,Y y) {this.x=<flown111>x; this.y=y;}
}
private static class Foo {
@@ -0,0 +1,10 @@
public class VarArgs {
private void g() {
f<flown111>(<flown1111>1);
f<flown112>(<flown1121>2, <flown1122>3, <flown1123>4);
}
private void f(int... <flown11>ints) {
int <caret>p = <flown1>ints[0];
}
}
@@ -1,16 +1,16 @@
class A {
private static class Tuple<X,Y> {
public final X x;
public final X <flown11>x;
public final Y y;
public Tuple(X x,Y y) {this.x=<flown11>x; this.y=y;}
public static <X1,Y1> Tuple<X1,Y1> create(X1 x, Y1 y) {
return new Tuple<X1,Y1>(<flown111>x, y);
public Tuple(X <flown1111>x,Y y) {this.x=<flown111>x; this.y=y;}
public static <X1,Y1> Tuple<X1,Y1> create(X1 <flown111111>x, Y1 y) {
return new Tuple<X1,Y1>(<flown11111>x, y);
}
}
private static class Foo {
void f() {
Tuple<String,String> t = Tuple.create(<flown1111>"","");
Tuple<String,String> t = Tuple.create(<flown1111111>"","");
Tuple<Object,Object> t2 = Tuple.create((Object) "",(Object) "");
String <caret>x = t.<flown1>x;
@@ -1,29 +1,29 @@
class Foo {
private static MyEnum staticMyEnum = MyEnum.<flown11>BAR;
private static MyEnum staticMyEnum = MyEnum.<flown111>BAR;
public static void main(String[] args) {
produce();
}
static void produce() {
produce(<flown1>staticMyEnum);
produce(<flown11>staticMyEnum);
}
static void produce(MyEnum myEnum) {
static void produce(MyEnum <flown1>myEnum) {
System.out.println("myEnum: " + myEnum<caret>);
}
static void f() {
staticMyEnum = MyEnum.<flown12>BAZ;
staticMyEnum = MyEnum.<flown112>BAZ;
}
static void g() {
staticMyEnum = MyEnum.<flown13>FOO;
staticMyEnum = MyEnum.<flown113>FOO;
}
enum MyEnum {
FOO,
BAR,
BAZ
<flown1131>FOO,
<flown1111>BAR,
<flown1121>BAZ
}
}
@@ -1,5 +1,17 @@
/*
* Copyright (c) 2000-2007 JetBrains s.r.o. All Rights Reserved.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.completion;
@@ -389,7 +401,7 @@ import java.lang.annotation.Target;
@Target({ElementType.LOCAL_VARIABLE})
@interface TLocalAnno {}'''
checkPreferredItems 0, 'TMetaAnno', 'Target', 'TreeSelectionMode', 'TLocalAnno'
checkPreferredItems 0, 'TMetaAnno', 'Target', 'TabLayoutPolicy', 'TabPlacement'
}
public void testPreferApplicableAnnotationsMethod() throws Throwable {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,8 @@ package com.intellij.codeInspection;
import com.intellij.codeInsight.completion.NormalCompletionDfaTest;
import com.intellij.codeInsight.completion.SmartTypeCompletionDfaTest;
import com.intellij.codeInsight.slice.SliceBackwardTest;
import com.intellij.codeInsight.slice.SliceTreeTest;
import com.intellij.slicer.SliceBackwardTest;
import com.intellij.slicer.SliceTreeTest;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,25 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.slice;
package com.intellij.slicer;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.slicer.SliceAnalysisParams;
import com.intellij.slicer.SliceHandler;
import com.intellij.slicer.SliceUsage;
import com.intellij.util.CommonProcessors;
import com.intellij.util.containers.IntArrayList;
import gnu.trove.THashMap;
@@ -42,10 +38,10 @@ import java.util.*;
/**
* @author cdr
*/
public class SliceBackwardTest extends DaemonAnalyzerTestCase {
public class SliceBackwardTest extends SliceTestCase {
private final TIntObjectHashMap<IntArrayList> myFlownOffsets = new TIntObjectHashMap<IntArrayList>();
private void dotest() throws Exception {
private void doTest() throws Exception {
configureByFile("/codeInsight/slice/backward/"+getTestName(false)+".java");
Map<String, RangeMarker> sliceUsageName2Offset = extractSliceOffsetsFromDocument(getEditor().getDocument());
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
@@ -59,10 +55,10 @@ public class SliceBackwardTest extends DaemonAnalyzerTestCase {
params.dataFlowToThis = true;
SliceUsage usage = SliceUsage.createRootUsage(element, params);
checkUsages(usage, true, myFlownOffsets);
checkUsages(usage, myFlownOffsets);
}
static void checkUsages(final SliceUsage usage, final boolean dataFlowToThis, final TIntObjectHashMap<IntArrayList> flownOffsets) {
static void checkUsages(final SliceUsage usage, final TIntObjectHashMap<IntArrayList> flownOffsets) {
final List<SliceUsage> children = new ArrayList<SliceUsage>();
boolean b = ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
@Override
@@ -76,7 +72,7 @@ public class SliceBackwardTest extends DaemonAnalyzerTestCase {
int[] offsets = list == null ? new int[0] : list.toArray();
Arrays.sort(offsets);
int size = list == null ? 0 : list.size();
int size = offsets.length;
assertEquals(message(startOffset, usage), size, children.size());
Collections.sort(children, new Comparator<SliceUsage>() {
@Override
@@ -90,7 +86,7 @@ public class SliceBackwardTest extends DaemonAnalyzerTestCase {
int offset = offsets[i];
assertEquals(message(offset, child), offset, child.getUsageInfo().getElement().getTextOffset());
checkUsages(child, dataFlowToThis, flownOffsets);
checkUsages(child, flownOffsets);
}
}
@@ -111,7 +107,10 @@ public class SliceBackwardTest extends DaemonAnalyzerTestCase {
Map<String, RangeMarker> sliceUsageName2Offset = new THashMap<String, RangeMarker>();
extract(document, sliceUsageName2Offset, "");
assertTrue(!document.getText().contains("<flown"));
int index = document.getText().indexOf("<flown");
if(index!=-1) {
fail(document.getText().substring(index, Math.min(document.getText().length(), index+50)));
}
assertTrue(!sliceUsageName2Offset.isEmpty());
return sliceUsageName2Offset;
}
@@ -153,28 +152,33 @@ public class SliceBackwardTest extends DaemonAnalyzerTestCase {
});
}
public void testSimple() throws Exception { dotest();}
public void testLocalVar() throws Exception { dotest();}
public void testInterMethod() throws Exception { dotest();}
public void testConditional() throws Exception { dotest();}
public void testConditional2() throws Exception { dotest();}
public void testMethodReturn() throws Exception { dotest();}
public void testVarUse() throws Exception { dotest();}
public void testWeirdCaretPosition() throws Exception { dotest();}
public void testAnonClass() throws Exception { dotest();}
public void testPostfix() throws Exception { dotest();}
public void testMethodCall() throws Exception { dotest();}
public void testEnumConst() throws Exception { dotest();}
public void testTypeAware() throws Exception { dotest();}
public void testTypeAware2() throws Exception { dotest();}
public void testViaParameterizedMethods() throws Exception { dotest();}
public void testTypeErased() throws Exception { dotest();}
public void testComplexTypeErasure() throws Exception { dotest();}
public void testGenericsSubst() throws Exception { dotest();}
public void testOverrides() throws Exception { dotest();}
public void testGenericBoxing() throws Exception { dotest();}
public void testAssignment() throws Exception { dotest();}
public void testGenericImplement() throws Exception { dotest();}
public void testGenericImplement2() throws Exception { dotest();}
public void testOverloadConstructor() throws Exception { dotest();}
public void testSimple() throws Exception { doTest();}
public void testLocalVar() throws Exception { doTest();}
public void testInterMethod() throws Exception { doTest();}
public void testConditional() throws Exception { doTest();}
public void testConditional2() throws Exception { doTest();}
public void testMethodReturn() throws Exception { doTest();}
public void testVarUse() throws Exception { doTest();}
public void testWeirdCaretPosition() throws Exception { doTest();}
public void testAnonClass() throws Exception { doTest();}
public void testPostfix() throws Exception { doTest();}
public void testMethodCall() throws Exception { doTest();}
public void testEnumConst() throws Exception { doTest();}
public void testTypeAware() throws Exception { doTest();}
public void testTypeAware2() throws Exception { doTest();}
public void testViaParameterizedMethods() throws Exception { doTest();}
public void testTypeErased() throws Exception { doTest();}
public void testComplexTypeErasure() throws Exception { doTest();}
public void testGenericsSubst() throws Exception { doTest();}
public void testOverrides() throws Exception { doTest();}
public void testGenericBoxing() throws Exception { doTest();}
public void testAssignment() throws Exception { doTest();}
public void testGenericImplement() throws Exception { doTest();}
public void testGenericImplement2() throws Exception { doTest();}
public void testOverloadConstructor() throws Exception { doTest();}
public void testArrayElements() throws Exception { doTest();}
public void testAnonArray() throws Exception { doTest();}
public void testVarArgs() throws Exception { doTest();}
public void testListTrackToArray() throws Exception { doTest();}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.slice;
package com.intellij.slicer;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
@@ -21,9 +21,6 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.slicer.SliceAnalysisParams;
import com.intellij.slicer.SliceForwardHandler;
import com.intellij.slicer.SliceUsage;
import com.intellij.util.containers.IntArrayList;
import gnu.trove.TIntObjectHashMap;
@@ -49,7 +46,7 @@ public class SliceForwardTest extends DaemonAnalyzerTestCase {
params.scope = new AnalysisScope(getProject());
params.dataFlowToThis = false;
SliceUsage usage = SliceUsage.createRootUsage(element, params);
SliceBackwardTest.checkUsages(usage, false, myFlownOffsets);
SliceBackwardTest.checkUsages(usage, myFlownOffsets);
}
public void testSimple() throws Exception { dotest();}
@@ -0,0 +1,42 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.slicer;
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkModificator;
import com.intellij.openapi.roots.AnnotationOrderRootType;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.PlatformTestUtil;
public abstract class SliceTestCase extends DaemonAnalyzerTestCase {
@Override
protected Sdk getTestProjectJdk() {
Sdk sdk = IdeaTestUtil.getMockJdk17();
// add JDK annotations
SdkModificator sdkModificator = sdk.getSdkModificator();
VirtualFile root = LocalFileSystem.getInstance().findFileByPath(
FileUtil.toSystemIndependentName(PlatformTestUtil.getCommunityPath()) + "/java/jdkAnnotations");
assertNotNull(root);
sdkModificator.addRoot(root, AnnotationOrderRootType.getInstance());
sdkModificator.commitChanges();
return sdk;
}
}
@@ -1,17 +1,31 @@
package com.intellij.codeInsight.slice;
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.slicer;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.impl.ToolWindowHeadlessManagerImpl;
import com.intellij.psi.*;
import com.intellij.slicer.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NonNls;
import java.util.*;
@@ -19,7 +33,7 @@ import java.util.*;
/**
* @author cdr
*/
public class SliceTreeTest extends LightDaemonAnalyzerTestCase {
public class SliceTreeTest extends SliceTestCase {
private SliceTreeStructure configureTree(@NonNls final String name) throws Exception {
configureByFile("/codeInsight/slice/backward/"+ name +".java");
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
@@ -78,26 +92,39 @@ public class SliceTreeTest extends LightDaemonAnalyzerTestCase {
List<SliceNode> nodes = new ArrayList<SliceNode>();
expandNodesTo(root, nodes);
for (int i = 0; i < nodes.size()-1; i++) {
SliceNode node = nodes.get(i);
assertNull(node.getDuplicate());
TIntArrayList hasDups = new TIntArrayList();
for (SliceNode node : nodes) {
if (node.getDuplicate() != null) {
PsiElement element = node.getValue().getElement();
hasDups.add(element.getTextRange().getStartOffset());
assertTrue(element instanceof PsiParameter && ((PsiParameter)element).getName().equals("i") || element instanceof PsiLiteralExpression);
}
}
SliceNode last = nodes.get(nodes.size() - 1);
assertNotNull(last.getDuplicate());
type(" xx");
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
backspace();
backspace();
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
backspace();
backspace();
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
backspace();
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
nodes.clear();
expandNodesTo(root, nodes);
for (int i = 0; i < nodes.size()-1; i++) {
SliceNode node = nodes.get(i);
assertNull(node.getDuplicate());
for (SliceNode node : nodes) {
if (node.getDuplicate() != null) {
PsiElement element = node.getValue().getElement();
int offset = element.getTextRange().getStartOffset();
int i = hasDups.indexOf(offset);
assertTrue(i != -1);
hasDups.remove(i);
assertTrue(element instanceof PsiParameter && ((PsiParameter)element).getName().equals("i") || element instanceof PsiLiteralExpression);
}
}
assertNotNull(last.getDuplicate());
assertTrue(hasDups.isEmpty());
}
public void testLeafExpressionsAreEmptyInCaseOfInfinitelyExpandingTreeWithDuplicateNodes() throws Exception {
@@ -162,17 +189,28 @@ public class SliceTreeTest extends LightDaemonAnalyzerTestCase {
child = (SliceNode)children.iterator().next();
assertTrue(child.getValue().getElement() instanceof PsiReferenceExpression);
children = child.getChildren();
assertEquals(1, children.size());
child = (SliceNode)children.iterator().next();
assertTrue(child.getValue().getElement() instanceof PsiParameter);
children = child.getChildren();
assertEquals(1, children.size());
child = (SliceNode)children.iterator().next();
assertTrue(child.getValue().getElement() instanceof PsiReferenceExpression);
children = child.getChildren();
assertEquals(1, children.size());
child = (SliceNode)children.iterator().next();
assertTrue(child.getValue().getElement() instanceof PsiParameter);
children = child.getChildren();
assertEquals(1, children.size());
child = (SliceNode)children.iterator().next();
assertTrue(child.getValue().getElement() instanceof PsiLiteralExpression);
assertEquals(child.getValue().getElement(), leaf);
}
public void testNullness() throws Exception {
SliceTreeStructure treeStructure = configureTree("Nulls");
final SliceRootNode root = (SliceRootNode)treeStructure.getRootElement();
@@ -185,67 +223,79 @@ public class SliceTreeTest extends LightDaemonAnalyzerTestCase {
" Value: o\n" +
" (6: 12) |String| |l|;\n" +
" (52: 13) |l| |=| |d|;\n" +
" (15: 13) |set|(|o|)|;\n" +
" (51: 21) |void| |set|(|String| |d|)| |{\n" +
" (15: 13) |set|(|o|)|;\n" +
" Value: nu()\n" +
" (6: 12) |String| |l|;\n" +
" (52: 13) |l| |=| |d|;\n" +
" (29: 13) |set|(|nu|(|)|)|;\n" +
" (51: 21) |void| |set|(|String| |d|)| |{\n" +
" (29: 13) |set|(|nu|(|)|)|;\n" +
" Value: t\n" +
" (6: 12) |String| |l|;\n" +
" (52: 13) |l| |=| |d|;\n" +
" (46: 15) |x|.|set|(|t|)|;\n" +
" (51: 21) |void| |set|(|String| |d|)| |{\n" +
" (46: 15) |x|.|set|(|t|)|;\n" +
"NotNull Values\n" +
" Value: \"\"\n" +
" (6: 12) |String| |l|;\n" +
" (52: 13) |l| |=| |d|;\n" +
" (19: 13) |set|(|CON|)|;\n" +
" (5: 39) |private| |final| |static| |String| |CON| |=| |\"\"|;\n" +
" (51: 21) |void| |set|(|String| |d|)| |{\n" +
" (19: 13) |set|(|CON|)|;\n" +
" (5: 39) |private| |final| |static| |String| |CON| |=| |\"\"|;\n" +
" Value: \"xxx\"\n" +
" (6: 12) |String| |l|;\n" +
" (52: 13) |l| |=| |d|;\n" +
" (10: 13) |set|(|\"xxx\"|)|;\n" +
" (51: 21) |void| |set|(|String| |d|)| |{\n" +
" (10: 13) |set|(|\"xxx\"|)|;\n" +
" Value: new String()\n" +
" (6: 12) |String| |l|;\n" +
" (52: 13) |l| |=| |d|;\n" +
" (17: 13) |set|(|new| |String|(|)|)|;\n" +
" (51: 21) |void| |set|(|String| |d|)| |{\n" +
" (17: 13) |set|(|new| |String|(|)|)|;\n" +
" Value: nn()\n" +
" (6: 12) |String| |l|;\n" +
" (52: 13) |l| |=| |d|;\n" +
" (18: 13) |set|(|nn|(|)|)|;\n" +
" (51: 21) |void| |set|(|String| |d|)| |{\n" +
" (18: 13) |set|(|nn|(|)|)|;\n" +
" Value: nn\n" +
" (6: 12) |String| |l|;\n" +
" (52: 13) |l| |=| |d|;\n" +
" (21: 13) |set|(|nn|)|;\n" +
" (51: 21) |void| |set|(|String| |d|)| |{\n" +
" (21: 13) |set|(|nn|)|;\n" +
" Value: g\n" +
" (6: 12) |String| |l|;\n" +
" (52: 13) |l| |=| |d|;\n" +
" (27: 13) |set|(|g|)|;\n" +
" (51: 21) |void| |set|(|String| |d|)| |{\n" +
" (27: 13) |set|(|g|)|;\n" +
" Value: \"null\"\n" +
" (6: 12) |String| |l|;\n" +
" (52: 13) |l| |=| |d|;\n" +
" (48: 15) |x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" +
" (48: 27) |x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" +
" (51: 21) |void| |set|(|String| |d|)| |{\n" +
" (48: 15) |x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" +
" (48: 27) |x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" +
" Value: t\n" +
" (6: 12) |String| |l|;\n" +
" (52: 13) |l| |=| |d|;\n" +
" (48: 15) |x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" +
" (48: 36) |x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" +
" (51: 21) |void| |set|(|String| |d|)| |{\n" +
" (48: 15) |x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" +
" (48: 36) |x|.|set|(|t| |==| |null| |?| |\"null\"| |:| |t|)|;\n" +
" Value: d\n" +
" (6: 12) |String| |l|;\n" +
" (55: 13) |l| |=| |d|;\n" +
"Other Values\n" +
" Value: g\n" +
" Value: private String d;\n" +
" (6: 12) |String| |l|;\n" +
" (52: 13) |l| |=| |d|;\n" +
" (11: 13) |set|(|g|)|;\n" +
" (24: 13) |set|(|other|)|;\n" +
" (23: 24) |String| |other| |=| |g| |==| |\"\"| |?| |CON| |:| |g|;\n" +
" (23: 40) |String| |other| |=| |g| |==| |\"\"| |?| |CON| |:| |g|;\n" +
" Value: d\n" +
" (51: 21) |void| |set|(|String| |d|)| |{\n" +
" (30: 13) |set|(|hz|(|)|)|;\n" +
" (42: 16) |return| |d|;\n" +
" (7: 20) |private| |String| |d|;\n" +
" Value: String g\n" +
" (6: 12) |String| |l|;\n" +
" (52: 13) |l| |=| |d|;\n" +
" (30: 13) |set|(|hz|(|)|)|;\n" +
" (42: 16) |return| |d|;\n" +
" (51: 21) |void| |set|(|String| |d|)| |{\n" +
" (11: 13) |set|(|g|)|;\n" +
" (9: 21) |public| |X|(|String| |g|)| |{\n" +
"");
}
@@ -260,7 +310,7 @@ public class SliceTreeTest extends LightDaemonAnalyzerTestCase {
}
});
String[] childrenExpected = dataExpected.length() == 0 ? ArrayUtil.EMPTY_STRING_ARRAY : dataExpected.split("\n");
String[] childrenExpected = dataExpected.isEmpty() ? ArrayUtil.EMPTY_STRING_ARRAY : dataExpected.split("\n");
String curChildren = "";
String curNode = null;
int iactual = 0;
@@ -283,7 +333,7 @@ public class SliceTreeTest extends LightDaemonAnalyzerTestCase {
curChildren += StringUtil.trimStart(e, " ") + "\n";
}
}
assertEquals(actualNodes.size(), iactual);
assertEquals(dataExpected, actualNodes.size(), iactual);
}
public void testDoubleNullness() throws Exception {
@@ -302,4 +352,51 @@ public class SliceTreeTest extends LightDaemonAnalyzerTestCase {
""
);
}
public void testGroupByLeavesWithLists() throws Exception {
SliceTreeStructure treeStructure = configureTree(getTestName(false));
final SliceRootNode root = (SliceRootNode)treeStructure.getRootElement();
Map<SliceNode, Collection<PsiElement>> map = SliceLeafAnalyzer.createMap();
Collection<PsiElement> leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, map);
assertEquals(2, leaves.size());
Set<String> names = ContainerUtil.map2Set(leaves, new Function<PsiElement, String>() {
@Override
public String fun(PsiElement element) {
return element.getText();
}
});
assertEquals(ContainerUtil.newHashSet("\"uuu\"", "\"xxx\""), names);
}
public void testCollectionTrack() throws Exception {
Set<String> names = groupByLeaves();
assertEquals(3, names.size());
assertEquals(ContainerUtil.newHashSet("\"uuu\"", "\"x\"", "\"y\""), names);
}
private Set<String> groupByLeaves() throws Exception {
SliceTreeStructure treeStructure = configureTree(getTestName(false));
final SliceRootNode root = (SliceRootNode)treeStructure.getRootElement();
Map<SliceNode, Collection<PsiElement>> map = SliceLeafAnalyzer.createMap();
Collection<PsiElement> leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, map);
return ContainerUtil.map2Set(leaves, new Function<PsiElement, String>() {
@Override
public String fun(PsiElement element) {
return element.getText();
}
});
}
public void testArrayCopyTrack() throws Exception {
Set<String> names = groupByLeaves();
assertOrderedEquals(Collections.singletonList("\"x\""), assertOneElement(names));
}
public void testMapValuesTrack() throws Exception {
Set<String> names = groupByLeaves();
assertOrderedEquals(Collections.singletonList("\"y\""), assertOneElement(names));
}
public void testMapKeysTrack() throws Exception {
Set<String> names = groupByLeaves();
assertOrderedEquals(Collections.singletonList("\"x\""), assertOneElement(names));
}
}
@@ -345,6 +345,17 @@
<annotation name="org.jetbrains.annotations.NonNls" />
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name='java.lang.System void arraycopy(java.lang.Object, int, java.lang.Object, int, int) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="target" val="&quot;dest&quot;"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name='java.lang.System void arraycopy(java.lang.Object, int, java.lang.Object, int, int) 2'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name="java.lang.Thread Thread(java.lang.Runnable, java.lang.String) 1">
<annotation name="org.jetbrains.annotations.NonNls" />
</item>
+132 -14
View File
@@ -5,6 +5,10 @@
</item>
<item name="java.util.ArrayList ArrayList(java.util.Collection&lt;? extends E&gt;) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name="java.util.Arrays T[] copyOf(T[], int)">
<annotation name="org.jetbrains.annotations.NotNull" />
@@ -577,26 +581,22 @@
<val name="intValues" val="{java.util.Calendar.SUNDAY, java.util.Calendar.MONDAY, java.util.Calendar.TUESDAY, java.util.Calendar.WEDNESDAY, java.util.Calendar.THURSDAY, java.util.Calendar.FRIDAY, java.util.Calendar.SATURDAY}" />
</annotation>
</item>
<item name="java.util.Collection Iterator&lt;E&gt; iterator()">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="java.util.Collection Object[] toArray()">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="java.util.Collection T[] toArray(T[] a)">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="java.util.Collection T[] toArray(T[])">
<annotation name="org.jetbrains.annotations.NotNull" />
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name="java.util.Collection T[] toArray(T[]) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="java.util.Collection boolean addAll(java.util.Collection&lt;? extends E&gt; c) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="java.util.Collection boolean addAll(java.util.Collection&lt;? extends E&gt;) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name="java.util.Collection boolean containsAll(java.util.Collection&lt;?&gt; c) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
@@ -618,6 +618,10 @@
</item>
<item name="java.util.Collection java.lang.Object[] toArray()">
<annotation name="org.jetbrains.annotations.NotNull" />
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name="java.util.Collection java.util.Iterator&lt;E&gt; iterator()">
<annotation name="org.jetbrains.annotations.NotNull" />
@@ -871,6 +875,47 @@
<item name="java.util.Formatter java.util.Formatter format(java.lang.String, java.lang.Object...) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name='java.util.HashSet HashSet(java.util.Collection&lt;? extends E&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name='java.util.LinkedHashSet LinkedHashSet(java.util.Collection&lt;? extends E&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name='java.util.LinkedList LinkedList(java.util.Collection&lt;? extends E&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name='java.util.List E get(int)'>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
</annotation>
</item>
<item name='java.util.List E remove(int)'>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
</annotation>
</item>
<item name='java.util.List E set(int, E)'>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
</annotation>
</item>
<item name='java.util.List E set(int, E) 1'>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name="java.util.List List&lt;E&gt; subList(int fromIndex, int toIndex)">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
@@ -883,14 +928,24 @@
<item name="java.util.List T[] toArray(T[]) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="java.util.List boolean addAll(int index, java.util.Collection&lt;? extends E&gt; c) 1">
<annotation name="org.jetbrains.annotations.NotNull" />
<item name='java.util.List boolean add(E) 0'>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name="java.util.List boolean addAll(int, java.util.Collection&lt;? extends E&gt;) 1">
<annotation name="org.jetbrains.annotations.NotNull" />
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name="java.util.List boolean addAll(java.util.Collection&lt;? extends E&gt;) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name="java.util.List boolean containsAll(java.util.Collection&lt;?&gt;) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
@@ -909,6 +964,10 @@
</item>
<item name="java.util.List java.util.List&lt;E&gt; subList(int, int)">
<annotation name="org.jetbrains.annotations.NotNull" />
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name="java.util.List java.util.ListIterator&lt;E&gt; listIterator()">
<annotation name="org.jetbrains.annotations.NotNull" />
@@ -916,11 +975,56 @@
<item name="java.util.List java.util.ListIterator&lt;E&gt; listIterator(int)">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name='java.util.List void add(int, E) 1'>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name='java.util.Map V get(java.lang.Object)'>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="source" val="&quot;this.values&quot;"/>
<val name="sourceIsContainer" val="true" />
</annotation>
</item>
<item name='java.util.Map V put(K, V)'>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="source" val="&quot;this.values&quot;"/>
<val name="sourceIsContainer" val="true" />
</annotation>
</item>
<item name='java.util.Map V put(K, V) 0'>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="target" val="&quot;this.keys&quot;"/>
<val name="targetIsContainer" val="true" />
</annotation>
</item>
<item name='java.util.Map V put(K, V) 1'>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="target" val="&quot;this.values&quot;"/>
<val name="targetIsContainer" val="true" />
</annotation>
</item>
<item name='java.util.Map V remove(java.lang.Object)'>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="source" val="&quot;this.values&quot;"/>
<val name="sourceIsContainer" val="true" />
</annotation>
</item>
<item name="java.util.Map java.util.Collection&lt;V&gt; values()">
<annotation name="org.jetbrains.annotations.NotNull" />
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="source" val="&quot;this.values&quot;"/>
<val name="sourceIsContainer" val="true" />
<val name="targetIsContainer" val="true" />
</annotation>
</item>
<item name="java.util.Map java.util.Set&lt;K&gt; keySet()">
<annotation name="org.jetbrains.annotations.NotNull" />
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="source" val="&quot;this.keys&quot;"/>
<val name="sourceIsContainer" val="true" />
<val name="targetIsContainer" val="true" />
</annotation>
</item>
<item name="java.util.Map java.util.Set&lt;java.util.Map.Entry&lt;K,V&gt;&gt; entrySet()">
<annotation name="org.jetbrains.annotations.NotNull" />
@@ -988,8 +1092,22 @@
<item name="java.util.SortedSet java.util.SortedSet&lt;E&gt; tailSet(E)">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name='java.util.TreeSet TreeSet(java.util.Collection&lt;? extends E&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name="java.util.UUID">
<annotation name="org.jetbrains.annotations.NonNls" />
</item>
<item name='java.util.Vector Vector(java.util.Collection&lt;? extends E&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
</root>
@@ -51,6 +51,34 @@
<item name="java.util.concurrent.ConcurrentMap boolean replace(K, V, V) 2">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name='java.util.concurrent.ConcurrentSkipListSet ConcurrentSkipListSet(java.util.Collection&lt;? extends E&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name='java.util.concurrent.ConcurrentSkipListSet ConcurrentSkipListSet(java.util.SortedSet&lt;E&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name='java.util.concurrent.CopyOnWriteArrayList CopyOnWriteArrayList(E[]) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name='java.util.concurrent.CopyOnWriteArrayList CopyOnWriteArrayList(java.util.Collection&lt;? extends E&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.intellij.lang.annotations.Flow'>
<val name="sourceIsContainer" val="true"/>
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name="java.util.concurrent.CountDownLatch boolean await(long, java.util.concurrent.TimeUnit) 1">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
Binary file not shown.
@@ -0,0 +1,125 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.intellij.lang.annotations;
import org.jetbrains.annotations.NonNls;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.PARAMETER, ElementType.METHOD})
/**
* This annotation assists the 'Data flow to this' feature by describing data flow
* from the method parameter to the corresponding container (e.g. <code>ArrayList.add(item)</code>)
* or from the container to the method return value (e.g. <code>Set.toArray()</code>)
* or between method parameters (e.g. <code>System.arraycopy(array1, 0, array2, length)</code>)
*/
public @interface Flow {
/**
* Denotes the source of the data flow.<br>
* Allowed values are:<br>
*
* <ul>
* <li><code>THIS_SOURCE</code> - Means that the data flows from this container.<br>
* E.g. annotation for java.util.List method get(index) means the method reads contents of list and returns it.<br>
* {@code @Flow(source = THIS_SOURCE) T get(int index);}<br>
* </li>
* <li>
* <code>this.</code>Field name - means that the data flows from this container some (synthetic) field.<br>
* E.g. annotation for java.util.Map.keySet() method here means that it returns data from the map from the field named "keys".<br>
* {@code @Flow(source = "this.keys") Set<K> keySet();}
* </li>
* </ul>
* By default, the source() value is:<br>
* <ul>
* <li>
* {@link #THIS_SOURCE} if the method was annotated, e.g.<br>
* {@code @Flow(sourceIsContainer=true, targetIsContainer=true) Object[] Collection.toArray()}<br>
* Here the annotation tells us that java.util.Collection.toArray() method<br>
* reads the contents of this collection (source=THIS_SOURCE by default) and passes it outside.
* </li>
* <li>
* Corresponding argument if the method parameter was annotated, e.g.<br>
* {@code void List.add(@Flow(targetIsContainer=true) E item)}<br>
* Here the annotation tells us that java.util.List.add(E item) method<br>
* takes the argument (source="item" by default) and passes it to this collection.
* </li>
* </ul>
*/
String source() default DEFAULT_SOURCE;
@NonNls String DEFAULT_SOURCE = "The method argument (if parameter was annotated) or this container (if instance method was annotated)";
@NonNls String THIS_SOURCE = "this";
/**
* true if the data source is container and we should track not the expression but its contents.<br>
* E.g. the java.util.ArrayList constructor takes the collection and stores its contents:<br>
* {@code ArrayList(@Flow(sourceIsContainer=true, targetIsContainer=true) Collection<? extends E> collection)}<br>
* By default it's false.
*/
boolean sourceIsContainer() default false;
/**
* Denotes the destination of the data flow.<br>
* Allowed values are:<br>
*
* <ul>
* <li><code>THIS_TARGET</code> - Means that the data flows inside this container (of the class the annotated method belongs to).<br>
* E.g. annotation for java.util.List method add(element) means the method takes the argument and passes it to this collection.<br>
* {@code boolean add(@Flow(target=THIS_TARGET, targetIsContainer=true) E element);}<br>
* </li>
* <li>
* Parameter name - means the data flows to this parameter.<br>
* E.g.<br>
* {@code void arraycopy(@Flow(sourceIsContainer=true, target="dest", targetIsContainer=true) Object src, int srcPos, Object dest, int destPos, int length)}<br>
* means that java.lang.System.arraycopy() method takes its first argument and passes it to the "dest" parameter.
* </li>
* <li>
* <code>this.</code>Field name - means that the data flows to this container in some (synthetic) field.<br>
* E.g. annotation for java.util.Map.put(key, value) method here means that it takes the argument 'key' and stores the data in some (hidden) field named "keys".<br>
* {@code V put(@Flow(target = "this.keys", targetIsContainer=true) K key, V value);}
* </li>
* </ul>
* By default, the target() value is:<br>
* <ul>
* <li>
* {@link #THIS_TARGET} if the parameter was annotated, e.g.<br>
* {@code void List.set(int index, @Flow(targetIsContainer=true) E element)}<br>
* Here the annotation tells us that java.util.List.set(index, element) method<br>
* reads its second argument 'element' and passes it to this collection (target=THIS_TARGET by default).
* </li>
* <li>
* {@link #RETURN_METHOD_TARGET} if the method was annotated, e.g.:<br>
* {@code @Flow(sourceIsContainer=true) E List.remove(int index)}<br>
* Here the annotation tells us that java.util.List.remove(int index) method<br>
* returns the data from its collection (target=RETURN_METHOD_TARGET by default).
* </li>
* </ul>
*/
String target() default DEFAULT_TARGET;
@NonNls String DEFAULT_TARGET = "This container (if the parameter was annotated) or the return value (if instance method was annotated)";
@NonNls String RETURN_METHOD_TARGET = "The return value of this method";
@NonNls String THIS_TARGET = "this";
/**
* true if the data target is container and we should track not the expression but its contents.<br>
* E.g. the java.lang.System.arraycopy() method parameter 'dest' is actually an array:<br>
* {@code void arraycopy(@Flow(sourceIsContainer=true, target="dest", targetIsContainer=true) Object src, int srcPos, Object dest, int destPos, int length)}<br>
* By default it's false.
*/
boolean targetIsContainer() default false;
}
@@ -1441,7 +1441,7 @@ import java.lang.annotation.Target;
configure('@T<caret> @interface Foo {}')
myFixture.completeBasic()
myFixture.assertPreferredCompletionItems 0, 'TMetaAnno', 'Target', 'TreeSelectionMode', 'TLocalAnno'
myFixture.assertPreferredCompletionItems 0, 'TMetaAnno', 'Target', 'TabLayoutPolicy', 'TabPlacement'
}
void testDiamondCompletion1() {