mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
+1
-1
@@ -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();
|
||||
|
||||
+23
-149
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -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
|
||||
}
|
||||
}
|
||||
+142
-107
@@ -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);
|
||||
}
|
||||
|
||||
+82
-7
@@ -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);
|
||||
|
||||
+7
@@ -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;
|
||||
|
||||
|
||||
+4
-6
@@ -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;
|
||||
|
||||
+38
-74
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -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;
|
||||
}
|
||||
|
||||
+5
-5
@@ -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
|
||||
|
||||
+4
-4
@@ -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);
|
||||
|
||||
+97
-5
@@ -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();
|
||||
|
||||
+20
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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__': $END$" description="if __name__ == '__main__'" toReformat="false" toShortenFQNames="true">
|
||||
|
||||
+11
-3
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user