Merge remote-tracking branch 'origin/master'

This commit is contained in:
Ilya.Kazakevich
2014-05-28 01:30:38 +04:00
40 changed files with 708 additions and 435 deletions
@@ -86,7 +86,7 @@ public class ExceptionExFilterFactory implements ExceptionFilterFactory {
worker.execute(text, lineEndOffset);
Result result = worker.getResult();
if (result == null) continue;
HyperlinkInfo hyperlinkInfo = result.hyperlinkInfo;
HyperlinkInfo hyperlinkInfo = result.getHyperlinkInfo();
if (!(hyperlinkInfo instanceof FileHyperlinkInfo)) continue;
OpenFileDescriptor descriptor = ((FileHyperlinkInfo)hyperlinkInfo).getDescriptor();
@@ -16,8 +16,6 @@
package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.ConditionCheckManager;
import com.intellij.codeInsight.ConditionChecker;
import com.intellij.codeInspection.dataFlow.instructions.*;
import com.intellij.codeInspection.dataFlow.value.*;
import com.intellij.openapi.diagnostic.Logger;
@@ -29,7 +27,6 @@ import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.Stack;
import com.siyeh.ig.numeric.UnnecessaryExplicitNumericCastInspection;
@@ -41,7 +38,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.regex.Pattern;
import static com.intellij.codeInsight.ConditionChecker.Type.*;
import static com.intellij.codeInspection.dataFlow.MethodContract.ValueConstraint;
import static com.intellij.psi.CommonClassNames.*;
@@ -61,6 +57,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
}
catch (Exception e) {
LOG.error(e);
//noinspection unchecked
return Condition.FALSE;
}
}
@@ -1364,11 +1361,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
@Override public void visitMethodCallExpression(PsiMethodCallExpression expression) {
startElement(expression);
if (handleContracts(expression, getCallContracts(expression))) {
finishElement(expression);
return;
}
PsiReferenceExpression methodExpression = expression.getMethodExpression();
PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
@@ -1405,7 +1397,18 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
}
addConditionalRuntimeThrow();
addInstruction(new MethodCallInstruction(expression, createChainedVariableValue(expression)));
List<MethodContract> contracts = getCallContracts(expression);
addInstruction(new MethodCallInstruction(expression, createChainedVariableValue(expression), contracts));
if (!contracts.isEmpty()) {
// if a contract resulted in 'fail', handle it
addInstruction(new DupInstruction());
addInstruction(new PushInstruction(myFactory.getConstFactory().getContractFail(), null));
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, expression.getProject()));
ConditionalGotoInstruction ifNotFail = new ConditionalGotoInstruction(null, true, null);
addInstruction(ifNotFail);
returnCheckingFinally();
ifNotFail.setOffset(myCurrentFlow.getInstructionCount());
}
if (!myCatchStack.isEmpty()) {
addMethodThrows(expression.resolveMethod());
@@ -1429,129 +1432,8 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
finishElement(expression);
}
private boolean handleContracts(PsiMethodCallExpression expression, List<MethodContract> _contracts) {
if (_contracts.isEmpty()) {
return false;
}
PsiMethod method = expression.resolveMethod();
if (method == null) {
return false;
}
final int paramCount = method.getParameterList().getParametersCount();
boolean varArgs = method.isVarArgs();
final PsiExpression[] args = expression.getArgumentList().getExpressions();
if (varArgs && args.length < paramCount - 1 || !varArgs && args.length != paramCount) {
return false;
}
List<MethodContract> contracts = ContainerUtil.findAll(_contracts, new Condition<MethodContract>() {
@Override
public boolean value(MethodContract contract) {
return paramCount == contract.arguments.length;
}
});
if (contracts.isEmpty()) {
return false;
}
for (PsiExpression arg : args) {
arg.accept(this);
}
if (varArgs) {
for (int i = 0; i < args.length - paramCount + 1; i++) {
addInstruction(new PopInstruction());
}
pushUnknown();
}
if (contracts.size() > 1) {
addInstruction(new DupInstruction(args.length, contracts.size() - 1));
}
for (int i = 0; i < contracts.size(); i++) {
handleContract(expression, contracts.get(i), contracts.size() - 1 - i, paramCount);
}
pushUnknownReturnValue(expression); // goto here if all contracts are false
return true;
}
private void handleContract(PsiMethodCallExpression expression, MethodContract contract, int remainingContracts, int paramCount) {
final ControlFlow.ControlFlowOffset exitPoint = getEndOffset(expression);
List<GotoInstruction> gotoContractFalse = new SmartList<GotoInstruction>();
for (int i = paramCount - 1; i >= 0; i--) {
ValueConstraint arg = contract.arguments[i];
if (arg == ValueConstraint.NULL_VALUE || arg == ValueConstraint.NOT_NULL_VALUE) {
addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, expression.getProject()));
}
else if (arg != ValueConstraint.TRUE_VALUE && arg != ValueConstraint.FALSE_VALUE) {
addInstruction(new PopInstruction());
continue;
}
boolean expectingTrueOnStack = arg == ValueConstraint.NULL_VALUE || arg == ValueConstraint.TRUE_VALUE;
ConditionalGotoInstruction continueCheckingContract = addInstruction(new ConditionalGotoInstruction(null, !expectingTrueOnStack, null));
for (int j = 0; j < i; j++) {
addInstruction(new PopInstruction());
}
gotoContractFalse.add(addInstruction(new GotoInstruction(null)));
continueCheckingContract.setOffset(myCurrentFlow.getInstructionCount());
}
for (int j = 0; j < remainingContracts * paramCount; j++) {
addInstruction(new PopInstruction());
}
// if contract is true
switch (contract.returnValue) {
case ANY_VALUE:
pushUnknownReturnValue(expression);
addInstruction(new GotoInstruction(exitPoint));
break;
case NULL_VALUE:
addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
addInstruction(new GotoInstruction(exitPoint));
break;
case NOT_NULL_VALUE:
PsiType type = expression.getType();
addInstruction(new PushInstruction(myFactory.createTypeValue(type, Nullness.NOT_NULL), null));
addInstruction(new GotoInstruction(exitPoint));
break;
case TRUE_VALUE:
addInstruction(new PushInstruction(myFactory.getConstFactory().getTrue(), null));
addInstruction(new GotoInstruction(exitPoint));
break;
case FALSE_VALUE:
addInstruction(new PushInstruction(myFactory.getConstFactory().getFalse(), null));
addInstruction(new GotoInstruction(exitPoint));
break;
case THROW_EXCEPTION:
returnCheckingFinally();
break;
}
// if contract is false
for (GotoInstruction instruction : gotoContractFalse) {
instruction.setOffset(myCurrentFlow.getInstructionCount());
}
}
private void pushUnknownReturnValue(PsiMethodCallExpression expression) {
PsiMethod method = expression.resolveMethod();
if (method != null) {
PsiType type = expression.getType();
addInstruction(new PushInstruction(myFactory.createTypeValue(type, DfaPsiUtil.getElementNullability(type, method)), null));
}
else {
pushUnknown();
}
}
private static List<MethodContract> getCallContracts(PsiMethodCallExpression expression) {
PsiMethod resolved = expression.resolveMethod();
final PsiMethod resolved = expression.resolveMethod();
if (resolved != null) {
final PsiAnnotation contractAnno = findContractAnnotation(resolved);
if (contractAnno != null) {
@@ -1562,7 +1444,13 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
String text = AnnotationUtil.getStringAttributeValue(contractAnno, null);
if (text != null) {
try {
return Result.create(parseContract(text), contractAnno);
List<MethodContract> applicable = ContainerUtil.filter(parseContract(text), new Condition<MethodContract>() {
@Override
public boolean value(MethodContract contract) {
return contract.arguments.length == resolved.getParameterList().getParametersCount();
}
});
return Result.create(applicable, contractAnno);
}
catch (Exception ignored) {
}
@@ -1701,7 +1589,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
}
}
addConditionalRuntimeThrow();
addInstruction(new MethodCallInstruction(expression, null));
addInstruction(new MethodCallInstruction(expression, null, Collections.<MethodContract>emptyList()));
}
else {
final PsiExpressionList args = expression.getArgumentList();
@@ -1719,7 +1607,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
}
addConditionalRuntimeThrow();
addInstruction(new MethodCallInstruction(expression, null));
addInstruction(new MethodCallInstruction(expression, null, Collections.<MethodContract>emptyList()));
if (!myCatchStack.isEmpty()) {
addMethodThrows(ctr);
@@ -1951,17 +1839,3 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
}
class MethodContract {
public final ValueConstraint[] arguments;
public final ValueConstraint returnValue;
public MethodContract(ValueConstraint[] arguments, ValueConstraint returnValue) {
this.arguments = arguments;
this.returnValue = returnValue;
}
public enum ValueConstraint {
ANY_VALUE, NULL_VALUE, NOT_NULL_VALUE, TRUE_VALUE, FALSE_VALUE, THROW_EXCEPTION
}
}
@@ -114,11 +114,14 @@ public class DataFlowRunner {
myNestedClosures.clear();
Set<Instruction> joinInstructions = ContainerUtil.newHashSet();
for (Instruction instruction : myInstructions) {
for (int index = 0; index < myInstructions.length; index++) {
Instruction instruction = myInstructions[index];
if (instruction instanceof GotoInstruction) {
joinInstructions.add(myInstructions[((GotoInstruction)instruction).getOffset()]);
} else if (instruction instanceof ConditionalGotoInstruction) {
joinInstructions.add(myInstructions[((ConditionalGotoInstruction)instruction).getOffset()]);
} else if (instruction instanceof MethodCallInstruction) {
joinInstructions.add(myInstructions[index + 1]);
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2000-2014 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.codeInspection.dataFlow;
/**
* @author peter
*/
public class MethodContract {
public final ValueConstraint[] arguments;
public final ValueConstraint returnValue;
public MethodContract(ValueConstraint[] arguments, ValueConstraint returnValue) {
this.arguments = arguments;
this.returnValue = returnValue;
}
public enum ValueConstraint {
ANY_VALUE, NULL_VALUE, NOT_NULL_VALUE, TRUE_VALUE, FALSE_VALUE, THROW_EXCEPTION
}
}
@@ -22,6 +22,7 @@ import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.FactoryMap;
import com.intellij.util.containers.MultiMap;
@@ -31,6 +32,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.codeInspection.dataFlow.MethodContract.ValueConstraint.*;
import static com.intellij.psi.JavaTokenType.*;
import static com.intellij.psi.JavaTokenType.EQEQ;
import static com.intellij.psi.JavaTokenType.NE;
@@ -46,14 +48,6 @@ public class StandardInstructionVisitor extends InstructionVisitor {
private final Set<PsiElement> myNotToReportReachability = new THashSet<PsiElement>();
private final Set<InstanceofInstruction> myUsefulInstanceofs = new THashSet<InstanceofInstruction>();
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
private final FactoryMap<MethodCallInstruction, Map<PsiExpression, Nullness>> myParametersNullability = new FactoryMap<MethodCallInstruction, Map<PsiExpression, Nullness>>() {
@Nullable
@Override
protected Map<PsiExpression, Nullness> create(MethodCallInstruction key) {
return calcParameterNullability(key.getCallExpression());
}
};
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
private final FactoryMap<MethodCallInstruction, Nullness> myReturnTypeNullability = new FactoryMap<MethodCallInstruction, Nullness>() {
@Override
protected Nullness create(MethodCallInstruction key) {
@@ -66,51 +60,6 @@ public class StandardInstructionVisitor extends InstructionVisitor {
}
};
private static Map<PsiExpression, Nullness> calcParameterNullability(@Nullable PsiCallExpression callExpression) {
PsiExpressionList argumentList = callExpression == null ? null : callExpression.getArgumentList();
if (argumentList != null) {
JavaResolveResult result = callExpression.resolveMethodGenerics();
PsiMethod method = (PsiMethod)result.getElement();
if (method != null) {
PsiSubstitutor substitutor = result.getSubstitutor();
PsiExpression[] args = argumentList.getExpressions();
PsiParameter[] parameters = method.getParameterList().getParameters();
boolean varArg = isVarArgCall(method, substitutor, args, parameters);
int checkedCount = Math.min(args.length, parameters.length) - (varArg ? 1 : 0);
Map<PsiExpression, Nullness> map = ContainerUtil.newHashMap();
for (int i = 0; i < checkedCount; i++) {
map.put(args[i], DfaPsiUtil.getElementNullability(substitutor.substitute(parameters[i].getType()), parameters[i]));
}
return map;
}
}
return Collections.emptyMap();
}
private static boolean isVarArgCall(PsiMethod method, PsiSubstitutor substitutor, PsiExpression[] args, PsiParameter[] parameters) {
if (!method.isVarArgs()) {
return false;
}
int argCount = args.length;
int paramCount = parameters.length;
if (argCount > paramCount) {
return true;
}
else if (paramCount > 0) {
if (argCount == paramCount) {
PsiType lastArgType = args[argCount - 1].getType();
if (lastArgType != null &&
!substitutor.substitute(parameters[paramCount - 1].getType()).isAssignableFrom(lastArgType)) {
return true;
}
}
}
return false;
}
@Override
public DfaInstructionState[] visitAssign(AssignInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
DfaValue dfaSource = memState.pop();
@@ -153,10 +102,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
public DfaInstructionState[] visitFieldReference(FieldReferenceInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
final DfaValue qualifier = memState.pop();
if (!checkNotNullable(memState, qualifier, NullabilityProblem.fieldAccessNPE, instruction.getElementToAssert())) {
if (qualifier instanceof DfaVariableValue) {
memState.setVarValue((DfaVariableValue)qualifier, runner.getFactory()
.createTypeValue(((DfaVariableValue)qualifier).getVariableType(), Nullness.NOT_NULL));
}
forceNotNull(runner, memState, qualifier);
}
return nextInstruction(instruction, runner, memState);
@@ -210,49 +156,162 @@ public class StandardInstructionVisitor extends InstructionVisitor {
protected void onInstructionProducesCCE(TypeCastInstruction instruction) {}
@Override
public DfaInstructionState[] visitMethodCall(MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
public DfaInstructionState[] visitMethodCall(final MethodCallInstruction instruction, final DataFlowRunner runner, final DfaMemoryState memState) {
DfaValue[] argValues = popCallArguments(instruction, runner, memState);
final DfaValue qualifier = popQualifier(instruction, runner, memState);
List<DfaMemoryState> currentStates = ContainerUtil.newArrayList(memState);
Set<DfaMemoryState> finalStates = ContainerUtil.newLinkedHashSet();
if (argValues != null) {
for (MethodContract contract : instruction.getContracts()) {
currentStates = addContractResults(argValues, contract, currentStates, instruction, runner.getFactory(), finalStates);
}
}
for (DfaMemoryState state : currentStates) {
state.push(getMethodResultValue(instruction, qualifier, runner.getFactory()));
finalStates.add(state);
}
return ContainerUtil.map2Array(finalStates, DfaInstructionState.class, new Function<DfaMemoryState, DfaInstructionState>() {
@Override
public DfaInstructionState fun(DfaMemoryState state) {
if (instruction.shouldFlushFields()) {
state.flushFields();
}
return new DfaInstructionState(runner.getInstruction(instruction.getIndex() + 1), state);
}
});
}
@Nullable
private DfaValue[] popCallArguments(MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
final PsiExpression[] args = instruction.getArgs();
Map<PsiExpression, Nullness> map = myParametersNullability.get(instruction);
PsiMethod method = instruction.getTargetMethod();
boolean varargCall = instruction.isVarArgCall();
DfaValue[] argValues;
if (method == null || instruction.getContracts().isEmpty()) {
argValues = null;
} else {
int paramCount = method.getParameterList().getParametersCount();
if (paramCount == args.length || method.isVarArgs() && args.length >= paramCount - 1) {
argValues = new DfaValue[paramCount];
if (varargCall) {
argValues[paramCount - 1] = DfaUnknownValue.getInstance();
}
} else {
argValues = null;
}
}
for (int i = 0; i < args.length; i++) {
final DfaValue arg = memState.pop();
PsiExpression expr = args[(args.length - i - 1)];
if (map.get(expr) == Nullness.NOT_NULL) {
int paramIndex = args.length - i - 1;
if (argValues != null && (paramIndex < args.length - 1 || !varargCall)) {
argValues[paramIndex] = arg;
}
PsiExpression expr = args[paramIndex];
Nullness requiredNullability = instruction.getArgRequiredNullability(expr);
if (requiredNullability == Nullness.NOT_NULL) {
if (!checkNotNullable(memState, arg, NullabilityProblem.passingNullableToNotNullParameter, expr)) {
if (arg instanceof DfaVariableValue) {
memState.setVarValue((DfaVariableValue)arg, runner.getFactory()
.createTypeValue(((DfaVariableValue)arg).getVariableType(), Nullness.NOT_NULL));
}
forceNotNull(runner, memState, arg);
}
}
else if (map.get(expr) == Nullness.UNKNOWN) {
else if (requiredNullability == Nullness.UNKNOWN) {
checkNotNullable(memState, arg, NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter, expr);
}
}
return argValues;
}
private DfaValue popQualifier(MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
@NotNull final DfaValue qualifier = memState.pop();
try {
boolean unboxing = instruction.getMethodType() == MethodCallInstruction.MethodType.UNBOXING;
NullabilityProblem problem = unboxing ? NullabilityProblem.unboxingNullable : NullabilityProblem.callNPE;
PsiExpression anchor = unboxing ? instruction.getContext() : instruction.getCallExpression();
if (!checkNotNullable(memState, qualifier, problem, anchor)) {
if (qualifier instanceof DfaVariableValue) {
memState.setVarValue((DfaVariableValue)qualifier, runner.getFactory().createTypeValue(
((DfaVariableValue)qualifier).getVariableType(), Nullness.NOT_NULL));
boolean unboxing = instruction.getMethodType() == MethodCallInstruction.MethodType.UNBOXING;
NullabilityProblem problem = unboxing ? NullabilityProblem.unboxingNullable : NullabilityProblem.callNPE;
PsiExpression anchor = unboxing ? instruction.getContext() : instruction.getCallExpression();
if (!checkNotNullable(memState, qualifier, problem, anchor)) {
forceNotNull(runner, memState, qualifier);
}
return qualifier;
}
private List<DfaMemoryState> addContractResults(DfaValue[] argValues,
MethodContract contract,
List<DfaMemoryState> states,
MethodCallInstruction instruction,
DfaValueFactory factory,
Set<DfaMemoryState> finalStates) {
DfaConstValue.Factory constFactory = factory.getConstFactory();
List<DfaMemoryState> falseStates = ContainerUtil.newArrayList();
for (int i = 0; i < argValues.length; i++) {
List<DfaMemoryState> nextStates = ContainerUtil.newArrayList();
DfaValue argValue = argValues[i];
MethodContract.ValueConstraint constraint = contract.arguments[i];
DfaConstValue expectedValue = constraint == NULL_VALUE || constraint == NOT_NULL_VALUE ? constFactory.getNull() :
constraint == FALSE_VALUE ? constFactory.getFalse() :
constraint == TRUE_VALUE ? constFactory.getTrue() :
null;
if (expectedValue == null) continue;
boolean invertCondition = constraint == NOT_NULL_VALUE;
DfaValue condition = factory.getRelationFactory().createRelation(argValue, expectedValue, EQEQ, invertCondition);
if (condition == null) {
if (!(argValue instanceof DfaConstValue)) continue;
condition = constFactory.createFromValue(argValue == expectedValue, PsiType.BOOLEAN, null);
}
for (DfaMemoryState state : states) {
boolean unknownVsNull = expectedValue == constFactory.getNull() &&
argValue instanceof DfaVariableValue &&
((DfaMemoryStateImpl)state).getVariableState((DfaVariableValue)argValue).getNullability() == Nullness.UNKNOWN;
DfaMemoryState falseCopy = state.createCopy();
if (state.applyCondition(condition)) {
if (unknownVsNull && !invertCondition) {
state.markEphemeral();
}
nextStates.add(state);
}
if (falseCopy.applyCondition(condition.createNegated())) {
if (unknownVsNull && invertCondition) {
falseCopy.markEphemeral();
}
falseStates.add(falseCopy);
}
}
return nextInstruction(instruction, runner, memState);
states = nextStates;
}
finally {
memState.push(getMethodResultValue(instruction, qualifier, runner.getFactory()));
if (instruction.shouldFlushFields()) {
memState.flushFields();
}
for (DfaMemoryState state : states) {
state.push(getDfaContractReturnValue(contract, instruction, factory));
finalStates.add(state);
}
return falseStates;
}
private DfaValue getDfaContractReturnValue(MethodContract contract,
MethodCallInstruction instruction,
DfaValueFactory factory) {
switch (contract.returnValue) {
case NULL_VALUE: return factory.getConstFactory().getNull();
case NOT_NULL_VALUE: return factory.createTypeValue(instruction.getResultType(), Nullness.NOT_NULL);
case TRUE_VALUE: return factory.getConstFactory().getTrue();
case FALSE_VALUE: return factory.getConstFactory().getFalse();
case THROW_EXCEPTION: return factory.getConstFactory().getContractFail();
default: return getMethodResultValue(instruction, null, factory);
}
}
private static void forceNotNull(DataFlowRunner runner, DfaMemoryState memState, DfaValue arg) {
if (arg instanceof DfaVariableValue) {
DfaVariableValue var = (DfaVariableValue)arg;
memState.setVarValue(var, runner.getFactory().createTypeValue(var.getVariableType(), Nullness.NOT_NULL));
}
}
@NotNull
private DfaValue getMethodResultValue(MethodCallInstruction instruction, @NotNull DfaValue qualifierValue, DfaValueFactory factory) {
private DfaValue getMethodResultValue(MethodCallInstruction instruction, @Nullable DfaValue qualifierValue, DfaValueFactory factory) {
DfaValue precalculated = instruction.getPrecalculatedReturnValue();
if (precalculated != null) {
return precalculated;
@@ -271,6 +330,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
}
if (methodType == MethodCallInstruction.MethodType.CAST) {
assert qualifierValue != null;
if (qualifierValue instanceof DfaConstValue) {
Object casted = TypeConversionUtil.computeCastTo(((DfaConstValue)qualifierValue).getValue(), type);
return factory.getConstFactory().createFromValue(casted, type, ((DfaConstValue)qualifierValue).getConstant());
@@ -346,15 +406,10 @@ public class StandardInstructionVisitor extends InstructionVisitor {
myCanBeNullInInstanceof.add(instruction);
boolean specialContractTreatment = isUnknownComparisonWithNullInContract(instruction, dfaLeft, dfaRight, factory, memState);
ArrayList<DfaInstructionState> states = new ArrayList<DfaInstructionState>();
final DfaMemoryState trueCopy = memState.createCopy();
if (trueCopy.applyCondition(dfaRelation)) {
if (specialContractTreatment && !dfaRelation.isNegated()) {
trueCopy.markEphemeral();
}
trueCopy.push(factory.getConstFactory().getTrue());
instruction.setTrueReachable();
states.add(new DfaInstructionState(next, trueCopy));
@@ -363,9 +418,6 @@ public class StandardInstructionVisitor extends InstructionVisitor {
//noinspection UnnecessaryLocalVariable
DfaMemoryState falseCopy = memState;
if (falseCopy.applyCondition(dfaRelation.createNegated())) {
if (specialContractTreatment && dfaRelation.isNegated()) {
falseCopy.markEphemeral();
}
falseCopy.push(factory.getConstFactory().getFalse());
instruction.setFalseReachable();
states.add(new DfaInstructionState(next, falseCopy));
@@ -377,23 +429,6 @@ public class StandardInstructionVisitor extends InstructionVisitor {
return states.toArray(new DfaInstructionState[states.size()]);
}
private static boolean isUnknownComparisonWithNullInContract(BinopInstruction instruction,
DfaValue dfaLeft,
DfaValue dfaRight,
DfaValueFactory factory,
DfaMemoryState memoryState) {
if (instruction.getPsiAnchor() != null || dfaRight != factory.getConstFactory().getNull()) {
return false;
}
if (dfaLeft instanceof DfaVariableValue) {
return ((DfaMemoryStateImpl)memoryState).getVariableState((DfaVariableValue)dfaLeft).getNullability() == Nullness.UNKNOWN;
}
if (dfaLeft instanceof DfaTypeValue) {
return ((DfaTypeValue)dfaLeft).getNullness() == Nullness.UNKNOWN;
}
return false;
}
public void skipConstantConditionReporting(@Nullable PsiElement anchor) {
ContainerUtil.addIfNotNull(myNotToReportReachability, anchor);
}
@@ -29,9 +29,14 @@ import com.intellij.codeInspection.dataFlow.*;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.psi.*;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class MethodCallInstruction extends Instruction {
@Nullable private final PsiCallExpression myCall;
@@ -39,40 +44,92 @@ public class MethodCallInstruction extends Instruction {
@NotNull private final PsiExpression[] myArgs;
private final boolean myShouldFlushFields;
@NotNull private final PsiExpression myContext;
@Nullable private final PsiMethod myTargetMethod;
private final List<MethodContract> myContracts;
private final MethodType myMethodType;
@Nullable private final DfaValue myPrecalculatedReturnValue;
private final boolean myVarArgCall;
private final Map<PsiExpression, Nullness> myArgRequiredNullability;
public enum MethodType {
BOXING, UNBOXING, REGULAR_METHOD_CALL, CAST
}
public MethodCallInstruction(@NotNull PsiExpression context, MethodType methodType, @Nullable PsiType resultType) {
myContext = context;
myContracts = Collections.emptyList();
myMethodType = methodType;
myCall = null;
myArgs = PsiExpression.EMPTY_ARRAY;
myType = resultType;
myShouldFlushFields = false;
myPrecalculatedReturnValue = null;
myTargetMethod = null;
myVarArgCall = false;
myArgRequiredNullability = Collections.emptyMap();
}
public MethodCallInstruction(@NotNull PsiCallExpression call, @Nullable DfaValue precalculatedReturnValue) {
public MethodCallInstruction(@NotNull PsiCallExpression call, @Nullable DfaValue precalculatedReturnValue, List<MethodContract> contracts) {
myContext = call;
myContracts = contracts;
myMethodType = MethodType.REGULAR_METHOD_CALL;
myCall = call;
final PsiExpressionList argList = call.getArgumentList();
myArgs = argList != null ? argList.getExpressions() : PsiExpression.EMPTY_ARRAY;
myType = myCall.getType();
myShouldFlushFields = !(myCall instanceof PsiNewExpression && myType != null && myType.getArrayDimensions() > 0) && !isPureCall(call);
JavaResolveResult result = call.resolveMethodGenerics();
myTargetMethod = (PsiMethod)result.getElement();
PsiSubstitutor substitutor = result.getSubstitutor();
if (argList != null && myTargetMethod != null) {
PsiParameter[] parameters = myTargetMethod.getParameterList().getParameters();
myVarArgCall = isVarArgCall(myTargetMethod, substitutor, myArgs, parameters);
myArgRequiredNullability = calcArgRequiredNullability(substitutor, parameters);
} else {
myVarArgCall = false;
myArgRequiredNullability = Collections.emptyMap();
}
myShouldFlushFields = !(call instanceof PsiNewExpression && myType != null && myType.getArrayDimensions() > 0) && !isPureCall();
myPrecalculatedReturnValue = precalculatedReturnValue;
}
private static boolean isPureCall(PsiCallExpression call) {
PsiMethod method = call.resolveMethod();
if (method == null) return false;
PsiAnnotation anno = ControlFlowAnalyzer.findContractAnnotation(method);
private Map<PsiExpression, Nullness> calcArgRequiredNullability(PsiSubstitutor substitutor, PsiParameter[] parameters) {
int checkedCount = Math.min(myArgs.length, parameters.length) - (myVarArgCall ? 1 : 0);
Map<PsiExpression, Nullness> map = ContainerUtil.newHashMap();
for (int i = 0; i < checkedCount; i++) {
map.put(myArgs[i], DfaPsiUtil.getElementNullability(substitutor.substitute(parameters[i].getType()), parameters[i]));
}
return map;
}
private static boolean isVarArgCall(PsiMethod method, PsiSubstitutor substitutor, PsiExpression[] args, PsiParameter[] parameters) {
if (!method.isVarArgs()) {
return false;
}
int argCount = args.length;
int paramCount = parameters.length;
if (argCount > paramCount) {
return true;
}
if (paramCount > 0 && argCount == paramCount) {
PsiType lastArgType = args[argCount - 1].getType();
if (lastArgType != null && !substitutor.substitute(parameters[paramCount - 1].getType()).isAssignableFrom(lastArgType)) {
return true;
}
}
return false;
}
private boolean isPureCall() {
if (myTargetMethod == null) return false;
PsiAnnotation anno = ControlFlowAnalyzer.findContractAnnotation(myTargetMethod);
if (anno != null && Boolean.TRUE.equals(AnnotationUtil.getBooleanAttributeValue(anno, "pure"))) return true;
return PropertyUtil.isSimplePropertyGetter(method);
return PropertyUtil.isSimplePropertyGetter(myTargetMethod);
}
@Nullable
@@ -93,6 +150,24 @@ public class MethodCallInstruction extends Instruction {
return myShouldFlushFields;
}
@Nullable
public PsiMethod getTargetMethod() {
return myTargetMethod;
}
public boolean isVarArgCall() {
return myVarArgCall;
}
@Nullable
public Nullness getArgRequiredNullability(@NotNull PsiExpression arg) {
return myArgRequiredNullability.get(arg);
}
public List<MethodContract> getContracts() {
return myContracts;
}
@Override
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
return visitor.visitMethodCall(this, runner, stateBefore);
@@ -34,10 +34,12 @@ import org.jetbrains.annotations.Nullable;
import java.util.Map;
public class DfaConstValue extends DfaValue {
private static final Throwable ourThrowable = new Throwable();
public static class Factory {
private final DfaConstValue dfaNull;
private final DfaConstValue dfaFalse;
private final DfaConstValue dfaTrue;
private final DfaConstValue dfaFail;
private final DfaValueFactory myFactory;
private final Map<Object, DfaConstValue> myValues = ContainerUtil.newHashMap();
@@ -46,6 +48,7 @@ public class DfaConstValue extends DfaValue {
dfaNull = new DfaConstValue(null, factory, null);
dfaFalse = new DfaConstValue(Boolean.FALSE, factory, null);
dfaTrue = new DfaConstValue(Boolean.TRUE, factory, null);
dfaFail = new DfaConstValue(ourThrowable, factory, null);
}
@Nullable
@@ -103,6 +106,10 @@ public class DfaConstValue extends DfaValue {
return instance;
}
public DfaConstValue getContractFail() {
return dfaFail;
}
public DfaConstValue getFalse() {
return dfaFalse;
}
@@ -16,12 +16,12 @@ class Foo {
}
@Contract("null,_->null;!null,_->!null")
String escapeStr(String s, Object o) {
return s;
String escapeStr(@Nullable String s, Object o) {
return <warning descr="Expression 's' might evaluate to null but is returned by the method which is not declared as @Nullable">s</warning>;
}
@Contract("null->null;!null->!null")
String escapeStr(String s) {
return s;
String escapeStr(@Nullable String s) {
return <warning descr="Expression 's' might evaluate to null but is returned by the method which is not declared as @Nullable">s</warning>;
}
}
@@ -0,0 +1,16 @@
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
class Foo {
@Contract("_,null->null;_,!null->!null")
static Object f(@NotNull Object o1, Object o2) {
return o2;
}
static Object g(Object o1) {
return f(<warning descr="Passing 'null' argument to parameter annotated as @NotNull">null</warning>, o1);
}
}
@@ -209,6 +209,7 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase {
public void testContractAnnotation() { doTest(); }
public void testContractInLoopNotTooComplex() { doTest(); }
public void testContractWithNullable() { doTest(); }
public void testContractWithNotNull() { doTest(); }
public void testContractPreservesUnknownNullability() { doTest(); }
public void testContractSeveralClauses() { doTest(); }
public void testContractVarargs() { doTest(); }
@@ -33,7 +33,7 @@ public class CompositeFilter implements Filter, FilterMixin {
private final List<Filter> myFilters = new ArrayList<Filter>();
private boolean myIsAnyHeavy;
private boolean forceUseAllFilters = true;
private boolean forceUseAllFilters = false;
private final DumbService myDumbService;
public CompositeFilter(@NotNull Project project) {
@@ -85,8 +85,8 @@ public class CompositeFilter implements Filter, FilterMixin {
}
if (resultItems.size() == 1) {
ResultItem resultItem = resultItems.get(0);
return new Result(resultItem.highlightStartOffset, resultItem.highlightEndOffset, resultItem.hyperlinkInfo,
resultItem.highlightAttributes);
return new Result(resultItem.getHighlightStartOffset(), resultItem.getHighlightEndOffset(), resultItem.getHyperlinkInfo(),
resultItem.getHighlightAttributes());
}
return new Result(resultItems);
}
@@ -104,7 +104,7 @@ public class CompositeFilter implements Filter, FilterMixin {
List<ResultItem> newItems = newResult.getResultItems();
for (int i = 0; i < newItems.size(); i++) {
ResultItem item = newItems.get(i);
if (item.hyperlinkInfo == null || !intersects(resultItems, item)) {
if (item.getHyperlinkInfo() == null || !intersects(resultItems, item)) {
resultItems.add(item);
}
}
@@ -117,7 +117,7 @@ public class CompositeFilter implements Filter, FilterMixin {
for (int i = 0; i < items.size(); i++) {
ResultItem item = items.get(i);
if (item.hyperlinkInfo != null) {
if (item.getHyperlinkInfo() != null) {
if (newItemTextRange == null) {
newItemTextRange = new TextRange(newItem.highlightStartOffset, newItem.highlightEndOffset);
}
@@ -34,20 +34,23 @@ public interface Filter {
protected NextAction myNextAction = NextAction.EXIT;
protected final List<ResultItem> myResultItems;
public Result(final int highlightStartOffset, final int highlightEndOffset, final HyperlinkInfo hyperlinkInfo) {
public Result(final int highlightStartOffset, final int highlightEndOffset, @Nullable final HyperlinkInfo hyperlinkInfo) {
this(highlightStartOffset, highlightEndOffset, hyperlinkInfo, null);
}
public Result(final int highlightStartOffset, final int highlightEndOffset, final HyperlinkInfo hyperlinkInfo, final TextAttributes highlightAttributes) {
public Result(final int highlightStartOffset,
final int highlightEndOffset,
@Nullable final HyperlinkInfo hyperlinkInfo,
@Nullable final TextAttributes highlightAttributes) {
super(highlightStartOffset, highlightEndOffset, hyperlinkInfo, highlightAttributes);
myResultItems = null;
}
public Result(@NotNull List<ResultItem> resultItems) {
super(-1, -1, null, null);
myResultItems = resultItems;
}
public List<ResultItem> getResultItems() {
List<ResultItem> resultItems = myResultItems;
if (resultItems == null) {
@@ -56,6 +59,59 @@ public interface Filter {
return resultItems;
}
/**
* @deprecated This method will be removed. Result may be constructed using ResultItems, in that case this method will return incorrect value. Use {@link #getResultItems()} instead.
*/
@Deprecated
@Override
public int getHighlightStartOffset() {
return super.getHighlightStartOffset();
}
/**
* @deprecated This method will be removed. Result may be constructed using ResultItems, in that case this method will return incorrect value. Use {@link #getResultItems()} instead.
*/
@Deprecated
@Override
public int getHighlightEndOffset() {
return super.getHighlightEndOffset();
}
/**
* @deprecated This method will be removed. Result may be constructed using ResultItems, in that case this method will return incorrect value. Use {@link #getResultItems()} instead.
*/
@Deprecated
@Nullable
@Override
public TextAttributes getHighlightAttributes() {
return super.getHighlightAttributes();
}
/**
* @deprecated This method will be removed. Result may be constructed using ResultItems, in that case this method will return incorrect value. Use {@link #getResultItems()} or {@link #getFirstHyperlinkInfo()} instead.
*/
@Deprecated
@Nullable
@Override
public HyperlinkInfo getHyperlinkInfo() {
return super.getHyperlinkInfo();
}
@Nullable
public HyperlinkInfo getFirstHyperlinkInfo() {
HyperlinkInfo info = super.getHyperlinkInfo();
if (info == null && myResultItems != null) {
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < myResultItems.size(); i++) {
ResultItem resultItem = myResultItems.get(i);
if (resultItem.getHyperlinkInfo() != null) {
return resultItem.getHyperlinkInfo();
}
}
}
return info;
}
public NextAction getNextAction() {
return myNextAction;
}
@@ -70,36 +126,73 @@ public interface Filter {
}
class ResultItem {
/**
* @deprecated use getter, the visibility of this field will be decreased.
*/
@Deprecated
public final int highlightStartOffset;
/**
* @deprecated use getter, the visibility of this field will be decreased.
*/
@Deprecated
public final int highlightEndOffset;
/**
* @deprecated use getter, the visibility of this field will be decreased.
*/
@Deprecated @Nullable
public final TextAttributes highlightAttributes;
/**
* @deprecated use getter, the visibility of this field will be decreased.
*/
@Deprecated @Nullable
public final HyperlinkInfo hyperlinkInfo;
public ResultItem(final int highlightStartOffset, final int highlightEndOffset, final HyperlinkInfo hyperlinkInfo) {
@SuppressWarnings("deprecation")
public ResultItem(final int highlightStartOffset, final int highlightEndOffset, @Nullable final HyperlinkInfo hyperlinkInfo) {
this(highlightStartOffset, highlightEndOffset, hyperlinkInfo, null);
}
public ResultItem(final int highlightStartOffset, final int highlightEndOffset, final HyperlinkInfo hyperlinkInfo, final TextAttributes highlightAttributes) {
@SuppressWarnings("deprecation")
public ResultItem(final int highlightStartOffset,
final int highlightEndOffset,
@Nullable final HyperlinkInfo hyperlinkInfo,
@Nullable final TextAttributes highlightAttributes) {
this.highlightStartOffset = highlightStartOffset;
this.highlightEndOffset = highlightEndOffset;
this.hyperlinkInfo = hyperlinkInfo;
this.highlightAttributes = highlightAttributes;
}
public int getHighlightStartOffset() {
//noinspection deprecation
return highlightStartOffset;
}
public int getHighlightEndOffset() {
//noinspection deprecation
return highlightEndOffset;
}
@Nullable
public TextAttributes getHighlightAttributes() {
//noinspection deprecation
return highlightAttributes;
}
@Nullable
public HyperlinkInfo getHyperlinkInfo() {
//noinspection deprecation
return hyperlinkInfo;
}
}
/**
* Filters line by creating an instance of {@link Result}.
*
*
* @param line
* The line to be filtered. Note that the line must contain a line
* separator at the end.
*
* @param entireLength
* The length of the entire text including the line passed for filtration.
*
* @return
* <tt>null</tt>, if there was no match, otherwise, an instance of {@link Result}
* @param line The line to be filtered. Note that the line must contain a line
* separator at the end.
* @param entireLength The length of the entire text including the line passed for filtration.
* @return <tt>null</tt>, if there was no match, otherwise, an instance of {@link Result}
*/
@Nullable
Result applyFilter(String line, int entireLength);
@@ -297,6 +297,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
}
}
}
myFilters.setForceUseAllFilters(true);
myHeavyUpdateTicket = 0;
myHeavyAlarm = myFilters.isAnyHeavy() ? new Alarm(Alarm.ThreadToUse.SHARED_THREAD, this) : null;
@@ -113,12 +113,10 @@ public abstract class BasePlatformRefactoringAction extends BaseRefactoringActio
protected RefactoringActionHandler getHandler(@NotNull Language language, PsiElement element) {
List<RefactoringSupportProvider> providers = LanguageRefactoringSupport.INSTANCE.allForLanguage(language);
if (providers.isEmpty()) return null;
if (element == null && !providers.isEmpty()) return getRefactoringHandler(providers.get(0));
if (element != null) {
for (RefactoringSupportProvider provider : providers) {
if (provider.isAvailable(element)) {
return getRefactoringHandler(provider, element);
}
if (element == null) return getRefactoringHandler(providers.get(0));
for (RefactoringSupportProvider provider : providers) {
if (provider.isAvailable(element)) {
return getRefactoringHandler(provider, element);
}
}
return null;
@@ -17,21 +17,22 @@ package com.intellij.refactoring.actions;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageRefactoringSupport;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.lang.refactoring.RefactoringSupportProvider;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiNameIdentifierOwner;
import com.intellij.psi.PsiReference;
import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.refactoring.changeSignature.ChangeSignatureHandler;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ChangeSignatureAction extends BaseRefactoringAction {
public class ChangeSignatureAction extends BasePlatformRefactoringAction {
public ChangeSignatureAction() {
setInjectedContext(true);
@@ -49,7 +50,7 @@ public class ChangeSignatureAction extends BaseRefactoringAction {
@Override
protected boolean isAvailableOnElementInEditorAndFile(@NotNull final PsiElement element, @NotNull final Editor editor, @NotNull PsiFile file, @NotNull DataContext context) {
PsiElement targetMember = findTargetMember(file, editor);
PsiElement targetMember = findTargetMember(element);
if (targetMember == null) {
final ChangeSignatureHandler targetHandler = getChangeSignatureHandler(file.getLanguage());
if (targetHandler != null) {
@@ -62,17 +63,6 @@ public class ChangeSignatureAction extends BaseRefactoringAction {
return true;
}
@Nullable
private static PsiElement findTargetMember(PsiFile file, Editor editor) {
final PsiElement element = file.findElementAt(editor.getCaretModel().getOffset());
final PsiElement targetMember = findTargetMember(element);
if (targetMember != null) return targetMember;
final PsiReference reference = file.findReferenceAt(editor.getCaretModel().getOffset());
if (reference == null) return null;
return reference.resolve();
}
@Nullable
private static PsiElement findTargetMember(@Nullable PsiElement element) {
if (element == null) return null;
@@ -91,71 +81,45 @@ public class ChangeSignatureAction extends BaseRefactoringAction {
return null;
}
@Nullable
@Override
protected boolean hasAvailableHandler(@NotNull DataContext dataContext) {
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
if (project == null) return false;
final Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
final PsiElement targetMember;
if (editor != null) {
final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
if (file == null) return false;
targetMember = findTargetMember(file, editor);
} else {
final PsiElement[] elements = getPsiElementArray(dataContext);
if (elements.length != 1) return false;
targetMember = findTargetMember(elements[0]);
}
return targetMember != null && getChangeSignatureHandler(targetMember.getLanguage()) != null;
protected RefactoringActionHandler getRefactoringHandler(@NotNull RefactoringSupportProvider provider) {
return provider.getChangeSignatureHandler();
}
@Nullable
@Override
public RefactoringActionHandler getHandler(@NotNull DataContext dataContext) {
Language language = LangDataKeys.LANGUAGE.getData(dataContext);
if (language == null) {
PsiElement psiElement = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
if (psiElement != null) {
language = psiElement.getLanguage();
}
}
if (language != null) {
return new RefactoringActionHandler() {
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) {
editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
final PsiElement targetMember = findTargetMember(file, editor);
if (targetMember == null) {
final ChangeSignatureHandler handler = getChangeSignatureHandler(file.getLanguage());
if (handler != null) {
final String notFoundMessage = handler.getTargetNotFoundMessage();
if (notFoundMessage != null) {
CommonRefactoringUtil.showErrorHint(project, editor, notFoundMessage, ChangeSignatureHandler.REFACTORING_NAME, null);
}
protected RefactoringActionHandler getRefactoringHandler(@NotNull RefactoringSupportProvider provider, final PsiElement element) {
return new RefactoringActionHandler() {
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) {
editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
final PsiElement targetMember = findTargetMember(element);
if (targetMember == null) {
final ChangeSignatureHandler handler = getChangeSignatureHandler(file.getLanguage());
if (handler != null) {
final String notFoundMessage = handler.getTargetNotFoundMessage();
if (notFoundMessage != null) {
CommonRefactoringUtil.showErrorHint(project, editor, notFoundMessage, ChangeSignatureHandler.REFACTORING_NAME, null);
}
return;
}
final ChangeSignatureHandler handler = getChangeSignatureHandler(targetMember.getLanguage());
if (handler == null) return;
handler.invoke(project, new PsiElement[]{targetMember}, dataContext);
return;
}
final ChangeSignatureHandler handler = getChangeSignatureHandler(targetMember.getLanguage());
if (handler == null) return;
handler.invoke(project, new PsiElement[]{targetMember}, dataContext);
}
@Override
public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) {
if (elements.length != 1) return;
final PsiElement targetMember = findTargetMember(elements[0]);
if (targetMember == null) return;
final ChangeSignatureHandler handler = getChangeSignatureHandler(targetMember.getLanguage());
if (handler == null) return;
handler.invoke(project, new PsiElement[]{targetMember}, dataContext);
}
};
}
return null;
}
@Override
protected boolean isAvailableForLanguage(Language language) {
return getChangeSignatureHandler(language) != null;
@Override
public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) {
if (elements.length != 1) return;
final PsiElement targetMember = findTargetMember(elements[0]);
if (targetMember == null) return;
final ChangeSignatureHandler handler = getChangeSignatureHandler(targetMember.getLanguage());
if (handler == null) return;
handler.invoke(project, new PsiElement[]{targetMember}, dataContext);
}
};
}
@Nullable
@@ -18,20 +18,19 @@ package com.intellij.refactoring.actions;
import com.intellij.ide.TitledHandler;
import com.intellij.lang.Language;
import com.intellij.lang.refactoring.RefactoringSupportProvider;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.refactoring.lang.LanguageExtractInclude;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author ven
*/
public class ExtractIncludeAction extends BaseRefactoringAction {
public class ExtractIncludeAction extends BasePlatformRefactoringAction {
@Override
public boolean isAvailableInEditorOnly() {
return true;
@@ -65,9 +64,15 @@ public class ExtractIncludeAction extends BaseRefactoringAction {
return LanguageExtractInclude.INSTANCE.forLanguage(baseLanguage) != null;
}
@Nullable
@Override
public RefactoringActionHandler getHandler(@NotNull DataContext dataContext) {
PsiFile file = CommonDataKeys.PSI_FILE.getData(dataContext);
protected RefactoringActionHandler getRefactoringHandler(@NotNull RefactoringSupportProvider provider) {
return null;
}
@Nullable
protected RefactoringActionHandler getRefactoringHandler(@NotNull RefactoringSupportProvider provider, PsiElement element) {
PsiFile file = element.getContainingFile();
if (file == null) return null;
return LanguageExtractInclude.INSTANCE.forLanguage(file.getViewProvider().getBaseLanguage());
}
@@ -23,6 +23,7 @@ import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diagnostic.ErrorLogger;
import com.intellij.openapi.diagnostic.ErrorReportSubmitter;
import com.intellij.openapi.diagnostic.IdeaLoggingEvent;
import com.intellij.openapi.updateSettings.impl.UpdateChecker;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.io.MappingFailedException;
import org.jetbrains.annotations.NonNls;
@@ -47,6 +48,8 @@ public class DefaultIdeaErrorLogger implements ErrorLogger {
if (ourLoggerBroken) return false;
try {
UpdateChecker.checkForUpdate(event);
boolean notificationEnabled = !DISABLED_VALUE.equals(System.getProperty(FATAL_ERROR_NOTIFICATION_PROPERTY, ENABLED_VALUE));
ErrorReportSubmitter submitter = IdeErrorsDialog.getSubmitter(event.getThrowable());
@@ -265,11 +265,11 @@ public class EditorHyperlinkSupport {
Filter.Result result = customFilter.applyFilter(text, endOffset);
if (result != null) {
for (Filter.ResultItem resultItem : result.getResultItems()) {
if (resultItem.hyperlinkInfo != null) {
createHyperlink(resultItem.highlightStartOffset, resultItem.highlightEndOffset, resultItem.highlightAttributes, resultItem.hyperlinkInfo);
if (resultItem.getHyperlinkInfo() != null) {
createHyperlink(resultItem.getHighlightStartOffset(), resultItem.getHighlightEndOffset(), resultItem.getHighlightAttributes(), resultItem.getHyperlinkInfo());
}
else if (resultItem.highlightAttributes != null) {
addHighlighter(resultItem.highlightStartOffset, resultItem.highlightEndOffset, resultItem.highlightAttributes);
else if (resultItem.getHighlightAttributes() != null) {
addHighlighter(resultItem.getHighlightStartOffset(), resultItem.getHighlightEndOffset(), resultItem.getHighlightAttributes());
}
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.updateSettings.impl;
import com.intellij.diagnostic.IdeErrorsDialog;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.plugins.*;
import com.intellij.ide.reporter.ConnectionException;
@@ -25,6 +26,7 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.ex.ApplicationInfoEx;
import com.intellij.openapi.diagnostic.IdeaLoggingEvent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.progress.ProcessCanceledException;
@@ -840,4 +842,16 @@ public final class UpdateChecker {
LOG.error(e);
}
}
private static boolean ourHasFailedPlugins = false;
public static void checkForUpdate(IdeaLoggingEvent event) {
if (!ourHasFailedPlugins && UpdateSettings.getInstance().CHECK_NEEDED) {
final Throwable throwable = event.getThrowable();
final IdeaPluginDescriptor pluginDescriptor = PluginManager.getPlugin(IdeErrorsDialog.findPluginId(throwable));
if (pluginDescriptor != null && !pluginDescriptor.isBundled()) {
ourHasFailedPlugins = true;
updateAndShowResult();
}
}
}
}
@@ -51,12 +51,15 @@ public class UrlConnectionUtil {
}
public static InputStream getConnectionInputStreamWithException(@NotNull URLConnection connection, @NotNull ProgressIndicator pi)
public static InputStream getConnectionInputStreamWithException(@NotNull URLConnection connection, @Nullable ProgressIndicator pi)
throws IOException {
InputStreamGetter getter = new InputStreamGetter(connection);
final Future<?> getterFuture = ApplicationManager.getApplication().executeOnPooledThread(getter);
while (true) {
pi.checkCanceled();
if (pi != null) {
pi.checkCanceled();
}
try {
try {
getterFuture.get(50, TimeUnit.MILLISECONDS);
@@ -64,8 +67,11 @@ public class UrlConnectionUtil {
catch (TimeoutException ignored) {
}
pi.setIndeterminate(true);
pi.setText(pi.getText());
if (pi != null) {
pi.setIndeterminate(true);
pi.setText(pi.getText());
}
if (getterFuture.isDone()) {
break;
}
@@ -94,15 +94,15 @@ public class SMTRunnerConsoleProperties extends TestConsoleProperties implements
final int stacktraceLength = stacktrace.length();
final String[] lines = StringUtil.splitByLines(stacktrace);
for (String line : lines) {
final Filter.Result result;
Filter.Result result;
try {
result = myCustomFilter.applyFilter(line, stacktraceLength);
}
catch (Throwable t) {
throw new RuntimeException("Error while applying " + myCustomFilter + " to '"+line+"'", t);
throw new RuntimeException("Error while applying " + myCustomFilter + " to '" + line + "'", t);
}
if (result != null) {
final HyperlinkInfo info = result.hyperlinkInfo;
final HyperlinkInfo info = result != null ? result.getFirstHyperlinkInfo() : null;
if (info != null) {
// covers 99% use existing cases
if (info instanceof FileHyperlinkInfo) {
@@ -113,7 +113,7 @@ public class SMTRunnerConsoleProperties extends TestConsoleProperties implements
return new Navigatable() {
@Override
public void navigate(boolean requestFocus) {
result.hyperlinkInfo.navigate(project);
info.navigate(project);
}
@Override
@@ -103,10 +103,10 @@ public final class TestProxyPrinterProvider {
throw new RuntimeException("Error while applying " + myFilter + " to '"+line+"'", t);
}
if (result != null) {
defaultPrint(line.substring(0, result.highlightStartOffset), contentType);
String linkText = line.substring(result.highlightStartOffset, result.highlightEndOffset);
printHyperlink(linkText, result.hyperlinkInfo);
defaultPrint(line.substring(result.highlightEndOffset), contentType);
defaultPrint(line.substring(0, result.getHighlightStartOffset()), contentType);
String linkText = line.substring(result.getHighlightStartOffset(), result.getHighlightEndOffset());
printHyperlink(linkText, result.getHyperlinkInfo());
defaultPrint(line.substring(result.getHighlightEndOffset()), contentType);
}
else {
defaultPrint(line, contentType);
@@ -19,13 +19,16 @@ import com.intellij.ide.DataManager;
import com.intellij.ide.dnd.DnDEvent;
import com.intellij.ide.dnd.DnDManager;
import com.intellij.ide.dnd.DnDNativeTarget;
import com.intellij.openapi.CompositeDisposable;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.ui.AnActionButton;
import com.intellij.ui.AnActionButtonRunnable;
import com.intellij.ui.CaptionPanel;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.*;
import com.intellij.ui.border.CustomLineBorder;
import com.intellij.util.Alarm;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XDebuggerBundle;
@@ -46,9 +49,11 @@ import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.event.KeyEvent;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
@@ -63,6 +68,7 @@ public class XWatchesViewImpl implements DnDNativeTarget, XWatchesView, XDebugVi
@NotNull private final XDebugSession mySession;
private final XDebugSessionData mySessionData;
private final JPanel myDecoratedPanel;
private final CompositeDisposable myDisposables = new CompositeDisposable();
public XWatchesViewImpl(@NotNull final XDebugSession session, final @NotNull XDebugSessionData sessionData) {
mySession = session;
@@ -105,13 +111,99 @@ public class XWatchesViewImpl implements DnDNativeTarget, XWatchesView, XDebugVi
myTreePanel.getTree().getEmptyText().setText(XDebuggerBundle.message("debugger.no.watches"));
installEditListeners();
}
private void installEditListeners() {
final XDebuggerTree watchTree = myTreePanel.getTree();
final Alarm quitePeriod = new Alarm();
final Alarm editAlarm = new Alarm();
final ClickListener mouseListener = new ClickListener() {
@Override
public boolean onClick(@NotNull MouseEvent event, int clickCount) {
if (!SwingUtilities.isLeftMouseButton(event) ||
((event.getModifiers() & (InputEvent.SHIFT_MASK | InputEvent.ALT_MASK | InputEvent.CTRL_MASK | InputEvent.META_MASK)) !=0) ) {
return false;
}
boolean sameRow = isAboveSelectedItem(event, watchTree);
final AnAction editWatchAction = ActionManager.getInstance().getAction(XDebuggerActions.XEDIT_WATCH);
Presentation presentation = editWatchAction.getTemplatePresentation().clone();
DataContext context = DataManager.getInstance().getDataContext(watchTree);
final AnActionEvent actionEvent = new AnActionEvent(null, context, "WATCH_TREE", presentation, ActionManager.getInstance(), 0);
Runnable runnable = new Runnable() {
public void run() {
editWatchAction.actionPerformed(actionEvent);
}
};
if (sameRow && editAlarm.isEmpty() && quitePeriod.isEmpty()) {
editAlarm.addRequest(runnable, UIUtil.getMultiClickInterval());
} else {
editAlarm.cancelAllRequests();
}
return false;
}
};
final ClickListener mouseEmptySpaceListener = new DoubleClickListener() {
@Override
protected boolean onDoubleClick(MouseEvent event) {
if (!isAboveSelectedItem(event, watchTree)) {
myRootNode.addNewWatch();
return true;
}
return false;
}
};
ListenerUtil.addClickListener(watchTree, mouseListener);
ListenerUtil.addClickListener(watchTree, mouseEmptySpaceListener);
final FocusListener focusListener = new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
quitePeriod.addRequest(EmptyRunnable.getInstance(), UIUtil.getMultiClickInterval());
}
@Override
public void focusLost(FocusEvent e) {
editAlarm.cancelAllRequests();
}
};
ListenerUtil.addFocusListener(watchTree, focusListener);
final TreeSelectionListener selectionListener = new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
quitePeriod.addRequest(EmptyRunnable.getInstance(), UIUtil.getMultiClickInterval());
}
};
watchTree.addTreeSelectionListener(selectionListener);
myDisposables.add(new Disposable() {
@Override
public void dispose() {
ListenerUtil.removeClickListener(watchTree, mouseListener);
ListenerUtil.removeClickListener(watchTree, mouseEmptySpaceListener);
ListenerUtil.removeFocusListener(watchTree, focusListener);
watchTree.removeTreeSelectionListener(selectionListener);
}
});
}
@Override
public void dispose() {
Disposer.dispose(myDisposables);
DnDManager.getInstance().unregisterTarget(this, myTreePanel.getTree());
}
private static boolean isAboveSelectedItem(MouseEvent event, XDebuggerTree watchTree) {
Rectangle bounds = watchTree.getRowBounds(watchTree.getLeadSelectionRow());
if (bounds != null) {
bounds.width = watchTree.getWidth();
if (bounds.contains(event.getPoint())) {
return true;
}
}
return false;
}
private void executeAction(@NotNull String watch) {
AnAction action = ActionManager.getInstance().getAction(watch);
Presentation presentation = action.getTemplatePresentation().clone();
@@ -17,6 +17,9 @@ package com.intellij.xdebugger.impl.ui;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.ui.EditorComboBoxEditor;
@@ -106,6 +109,23 @@ public class XDebuggerExpressionComboBox extends XDebuggerEditorBase {
myComboBox.setMaximumRowCount(XDebuggerHistoryManager.MAX_RECENT_EXPRESSIONS);
}
@Override
protected Document createDocument(XExpression text) {
Document document = super.createDocument(text);
document.addDocumentListener(REPLACE_NEWLINES_LISTENER);
return document;
}
private static DocumentListener REPLACE_NEWLINES_LISTENER = new DocumentAdapter() {
@Override
public void documentChanged(DocumentEvent e) {
String text = e.getNewFragment().toString();
if (text.contains("\n")) {
e.getDocument().replaceString(e.getOffset(), e.getOffset() + e.getNewLength(), text.replace('\n', ' '));
}
}
};
@Override
protected void onHistoryChanged() {
fillComboBox();
@@ -153,8 +153,7 @@ public class XDebuggerTree extends DnDAwareTree implements DataProvider, Disposa
new DoubleClickListener() {
@Override
protected boolean onDoubleClick(MouseEvent e) {
expandIfEllipsis();
return true;
return expandIfEllipsis();
}
}.installOn(this);
@@ -188,7 +187,7 @@ public class XDebuggerTree extends DnDAwareTree implements DataProvider, Disposa
setTransferHandler(DEFAULT_TRANSFER_HANDLER);
}
private void expandIfEllipsis() {
private boolean expandIfEllipsis() {
MessageTreeNode[] treeNodes = getSelectedNodes(MessageTreeNode.class, null);
if (treeNodes.length == 1) {
MessageTreeNode node = treeNodes[0];
@@ -196,9 +195,11 @@ public class XDebuggerTree extends DnDAwareTree implements DataProvider, Disposa
TreeNode parent = node.getParent();
if (parent instanceof XValueContainerNode) {
((XValueContainerNode)parent).startComputingChildren();
return true;
}
}
}
return false;
}
public void addTreeListener(@NotNull XDebuggerTreeListener listener) {
@@ -64,7 +64,6 @@ public class HgPusher {
final HgPushDialog dialog = new HgPushDialog(myProject, repositories, selectedRepo);
dialog.show();
if (dialog.isOK()) {
dialog.rememberSettings();
pushCommand.set(preparePushCommand(myProject, dialog));
new Task.Backgroundable(myProject, "Pushing...", false) {
@Override
@@ -34,7 +34,6 @@ public class HgPullAction extends HgAbstractGlobalAction {
final HgPullDialog dialog = new HgPullDialog(project, repos, selectedRepo);
dialog.show();
if (dialog.isOK()) {
dialog.rememberSettings();
final String source = dialog.getSource();
final HgRepository hgRepository = dialog.getRepository();
new Task.Backgroundable(project, "Pulling changes from " + source, false) {
@@ -12,7 +12,6 @@
// limitations under the License.
package org.zmlx.hg4idea.ui;
import com.intellij.dvcs.DvcsRememberedInputs;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
@@ -21,11 +20,9 @@ import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.EditorComboBox;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.zmlx.hg4idea.HgRememberedInputs;
import org.zmlx.hg4idea.repo.HgRepository;
import org.zmlx.hg4idea.util.HgUtil;
@@ -60,8 +57,6 @@ public class HgPullDialog extends DialogWrapper {
public void createUIComponents() {
myRepositoryURL = new EditorComboBox("");
final DvcsRememberedInputs rememberedInputs = HgRememberedInputs.getInstance();
myRepositoryURL.setHistory(ArrayUtil.toObjectArray(rememberedInputs.getVisitedUrls(), String.class));
myRepositoryURL.addDocumentListener(new DocumentAdapter() {
@Override
public void documentChanged(DocumentEvent e) {
@@ -77,11 +72,6 @@ public class HgPullDialog extends DialogWrapper {
}
}
public void rememberSettings() {
final DvcsRememberedInputs rememberedInputs = HgRememberedInputs.getInstance();
rememberedInputs.addUrl(HgUtil.removePasswordIfNeeded(getSource()));
}
@NotNull
public HgRepository getRepository() {
return hgRepositorySelector.getRepository();
@@ -12,7 +12,6 @@
// limitations under the License.
package org.zmlx.hg4idea.ui;
import com.intellij.dvcs.DvcsRememberedInputs;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
@@ -20,10 +19,8 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.ComboboxSpeedSearch;
import com.intellij.ui.EditorComboBox;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.zmlx.hg4idea.HgRememberedInputs;
import org.zmlx.hg4idea.HgVcsMessages;
import org.zmlx.hg4idea.repo.HgRepository;
import org.zmlx.hg4idea.util.HgUtil;
@@ -88,8 +85,6 @@ public class HgPushDialog extends DialogWrapper {
public void createUIComponents() {
myRepositoryURL = new EditorComboBox("");
final DvcsRememberedInputs rememberedInputs = HgRememberedInputs.getInstance();
myRepositoryURL.setHistory(ArrayUtil.toObjectArray(rememberedInputs.getVisitedUrls(), String.class));
myRepositoryURL.addDocumentListener(new DocumentAdapter() {
@Override
public void documentChanged(com.intellij.openapi.editor.event.DocumentEvent e) {
@@ -192,11 +187,6 @@ public class HgPushDialog extends DialogWrapper {
return HgPushDialog.class.getName();
}
public void rememberSettings() {
final DvcsRememberedInputs rememberedInputs = HgRememberedInputs.getInstance();
rememberedInputs.addUrl(HgUtil.removePasswordIfNeeded(myRepositoryURL.getText()));
}
/**
* Updates the form on every change.
*/
@@ -197,7 +197,7 @@ public abstract class MavenImportingTestCase extends MavenTestCase {
assertTrue(getCompilerExtension(module).isCompilerOutputPathInherited());
}
private CompilerModuleExtension getCompilerExtension(String module) {
protected CompilerModuleExtension getCompilerExtension(String module) {
ModuleRootManager m = getRootManager(module);
return CompilerModuleExtension.getInstance(m.getModule());
}
+13
View File
@@ -4,8 +4,21 @@
<variable name="method" expression="pyFunctionName()" defaultValue="" alwaysStopAt="false" />
<variable name="end" expression="" defaultValue="" alwaysStopAt="true" />
<context>
<option name="HTML_TEXT" value="false" />
<option name="HTML" value="false" />
<option name="XSL_TEXT" value="false" />
<option name="XML" value="false" />
<option name="Python" value="true" />
<option name="Django" value="false" />
<option name="CSS_PROPERTY_VALUE" value="false" />
<option name="CSS_DECLARATION_BLOCK" value="false" />
<option name="CSS_RULESET_LIST" value="false" />
<option name="CSS" value="false" />
<option name="JAVA_SCRIPT" value="false" />
<option name="TypeScript" value="false" />
<option name="SQL" value="false" />
<option name="CoffeeScript" value="false" />
<option name="OTHER" value="false" />
</context>
</template>
<template name="main" value="if __name__ == '__main__':&#10; $END$" description="if __name__ == '__main__'" toReformat="false" toShortenFQNames="true">
@@ -17,11 +17,15 @@ package com.jetbrains.python.codeInsight.liveTemplates;
import com.intellij.codeInsight.template.FileTypeBasedContextType;
import com.intellij.patterns.PsiElementPattern;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.PythonFileType;
import com.jetbrains.python.psi.PyParameterList;
import com.jetbrains.python.psi.PyStringLiteralExpression;
import org.jetbrains.annotations.NotNull;
import static com.intellij.patterns.PlatformPatterns.psiElement;
@@ -39,16 +43,20 @@ public class PythonTemplateContextType extends FileTypeBasedContextType {
if (super.isInContext(file, offset)) {
final PsiElement element = file.findElementAt(offset);
if (element != null) {
return !isAfterDot(element);
return !(isAfterDot(element) || element instanceof PsiComment || element instanceof PyStringLiteralExpression ||
isInsideParameterList(element));
}
}
return false;
}
private static boolean isInsideParameterList(@NotNull PsiElement element) {
return PsiTreeUtil.getParentOfType(element, PyParameterList.class) != null;
}
private static boolean isAfterDot(@NotNull PsiElement element) {
ProcessingContext context = new ProcessingContext();
final PsiElementPattern.Capture<PsiElement> capture = psiElement().afterLeafSkipping(psiElement().whitespace(),
psiElement().withElementType(PyTokenTypes.DOT));
return capture.accepts(element, context);
return capture.accepts(element, new ProcessingContext());
}
}
@@ -195,7 +195,7 @@ public class PyTypeModelBuilder {
}
else if (type instanceof PyUnionType && allowUnions) {
if (type instanceof PyDynamicallyEvaluatedType || PyTypeChecker.isUnknown(type)) {
result = new UnknownType(build(((PyUnionType)type).excludeNull(), true));
result = new UnknownType(build(((PyUnionType)type).excludeNull(myContext), true));
}
else {
result = new OneOf(
@@ -1521,7 +1521,7 @@ public class PyUtil {
public static List<PyParameter> getParameters(@NotNull Callable callable, @NotNull TypeEvalContext context) {
PyType type = context.getType(callable);
if (type instanceof PyUnionType) {
type = ((PyUnionType)type).excludeNull();
type = ((PyUnionType)type).excludeNull(context);
}
if (type instanceof PyCallableType) {
final PyCallableType callableType = (PyCallableType)type;
@@ -239,7 +239,7 @@ public class PyNamedParameterImpl extends PyPresentableElementImpl<PyNamedParame
final PyType type = context.getType(defaultValue);
if (type != null && !(type instanceof PyNoneType)) {
if (type instanceof PyTupleType) {
return PyTypeParser.getTypeByName(this, "collections.Iterable");
return PyUnionType.createWeakType(type);
}
return type;
}
@@ -208,7 +208,7 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExp
final PyAssignmentStatement assignment = (PyAssignmentStatement)nextParent;
final PyExpression value = assignment.getAssignedValue();
if (value != null) {
final PyType assignedType = context.getType(value);
final PyType assignedType = PyTypeChecker.toNonWeakType(context.getType(value), context);
if (assignedType instanceof PyTupleType) {
final PyType t = getTypeFromTupleAssignment((PyTupleExpression)parent, (PyTupleType)assignedType);
if (t != null) {
@@ -386,6 +386,14 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExp
}
}
}
else if (iterableType instanceof PyTupleType) {
final PyTupleType tupleType = (PyTupleType)iterableType;
final List<PyType> memberTypes = new ArrayList<PyType>();
for (int i = 0; i < tupleType.getElementCount(); i++) {
memberTypes.add(tupleType.getElementType(i));
}
return PyUnionType.union(memberTypes);
}
else if (iterableType instanceof PyUnionType) {
final Collection<PyType> members = ((PyUnionType)iterableType).getMembers();
final List<PyType> iterationTypes = new ArrayList<PyType>();
@@ -49,7 +49,7 @@ public class PyDynamicallyEvaluatedType extends PyUnionType {
@Override
public String getName() {
PyType res = excludeNull();
PyType res = excludeNull(TypeEvalContext.codeInsightFallback());
return res != null ? res.getName() : PyNames.UNKNOWN_TYPE;
}
}
@@ -215,6 +215,17 @@ public class PyTypeChecker {
return false;
}
@Nullable
public static PyType toNonWeakType(@Nullable PyType type, @NotNull TypeEvalContext context) {
if (type instanceof PyUnionType) {
final PyUnionType unionType = (PyUnionType)type;
if (unionType.isWeak()) {
return unionType.excludeNull(context);
}
}
return type;
}
public static boolean hasGenerics(@Nullable PyType type, @NotNull TypeEvalContext context) {
final Set<PyGenericType> collected = new HashSet<PyGenericType>();
collectGenerics(type, context, collected, new HashSet<PyType>());
@@ -184,7 +184,7 @@ public class PyUnionType implements PyType {
* @return union with excluded types
*/
@Nullable
public PyType exclude(PyType type, TypeEvalContext context) {
public PyType exclude(@Nullable PyType type, @NotNull TypeEvalContext context) {
final List<PyType> members = new ArrayList<PyType>();
for (PyType m : getMembers()) {
if (type == null) {
@@ -202,8 +202,8 @@ public class PyUnionType implements PyType {
}
@Nullable
public PyType excludeNull() {
return exclude(null, null);
public PyType excludeNull(@NotNull TypeEvalContext context) {
return exclude(null, context);
}
private static PyType unit(@Nullable PyType type) {
@@ -805,6 +805,20 @@ public class PyTypeTest extends PyTestCase {
"expr = iter(xs).next()\n");
}
// PY-10967
public void testDefaultTupleParameterMember() {
doTest("int",
"def foo(xs=(1, 2)):\n" +
" expr, foo = xs\n");
}
public void testTupleIterationType() {
doTest("int | str",
"xs = (1, 'a')\n" +
"for expr in xs:\n" +
" pass\n");
}
private static TypeEvalContext getTypeEvalContext(@NotNull PyExpression element) {
return TypeEvalContext.userInitiated(element.getContainingFile()).withTracing();
}