diff --git a/java/execution/openapi/src/com/intellij/execution/filters/ExceptionExFilterFactory.java b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionExFilterFactory.java index 9b42c38d990c..722fb066ac1b 100644 --- a/java/execution/openapi/src/com/intellij/execution/filters/ExceptionExFilterFactory.java +++ b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionExFilterFactory.java @@ -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(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index eb4988779fb3..db74b243e329 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -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 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 _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 contracts = ContainerUtil.findAll(_contracts, new Condition() { - @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 gotoContractFalse = new SmartList(); - 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 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 applicable = ContainerUtil.filter(parseContract(text), new Condition() { + @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.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.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 - } -} - diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java index d3e2ed838bbd..07e5fcc5480d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java @@ -114,11 +114,14 @@ public class DataFlowRunner { myNestedClosures.clear(); Set 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]); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java new file mode 100644 index 000000000000..ed88d5cbd71e --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java @@ -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 + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java index 2fbced0c112b..81944764f45f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java @@ -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 myNotToReportReachability = new THashSet(); private final Set myUsefulInstanceofs = new THashSet(); @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - private final FactoryMap> myParametersNullability = new FactoryMap>() { - @Nullable - @Override - protected Map create(MethodCallInstruction key) { - return calcParameterNullability(key.getCallExpression()); - } - }; - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private final FactoryMap myReturnTypeNullability = new FactoryMap() { @Override protected Nullness create(MethodCallInstruction key) { @@ -66,51 +60,6 @@ public class StandardInstructionVisitor extends InstructionVisitor { } }; - private static Map 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 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 currentStates = ContainerUtil.newArrayList(memState); + Set 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() { + @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 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 addContractResults(DfaValue[] argValues, + MethodContract contract, + List states, + MethodCallInstruction instruction, + DfaValueFactory factory, + Set finalStates) { + DfaConstValue.Factory constFactory = factory.getConstFactory(); + List falseStates = ContainerUtil.newArrayList(); + for (int i = 0; i < argValues.length; i++) { + List 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 states = new ArrayList(); 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); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java index 76fd8d7b994a..442ee43c4794 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java @@ -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 myContracts; private final MethodType myMethodType; @Nullable private final DfaValue myPrecalculatedReturnValue; + private final boolean myVarArgCall; + private final Map 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 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 calcArgRequiredNullability(PsiSubstitutor substitutor, PsiParameter[] parameters) { + int checkedCount = Math.min(myArgs.length, parameters.length) - (myVarArgCall ? 1 : 0); + + Map 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 getContracts() { + return myContracts; + } + @Override public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) { return visitor.visitMethodCall(this, runner, stateBefore); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaConstValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaConstValue.java index f00f3ce0c677..5c8d7ca276d5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaConstValue.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaConstValue.java @@ -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 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; } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ContractSeveralClauses.java b/java/java-tests/testData/inspection/dataFlow/fixture/ContractSeveralClauses.java index a654b99ff12e..ac878ecdd054 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/ContractSeveralClauses.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ContractSeveralClauses.java @@ -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 s; } @Contract("null->null;!null->!null") - String escapeStr(String s) { - return s; + String escapeStr(@Nullable String s) { + return s; } } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ContractWithNotNull.java b/java/java-tests/testData/inspection/dataFlow/fixture/ContractWithNotNull.java new file mode 100644 index 000000000000..31a5f4680524 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ContractWithNotNull.java @@ -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(null, o1); + } + +} + diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index e9bc54b2d5ce..af0f2a7bc2cf 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -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(); } diff --git a/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java b/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java index 867c78c37b2a..fb782db28cd4 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java +++ b/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java @@ -33,7 +33,7 @@ public class CompositeFilter implements Filter, FilterMixin { private final List myFilters = new ArrayList(); 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 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); } diff --git a/platform/lang-api/src/com/intellij/execution/filters/Filter.java b/platform/lang-api/src/com/intellij/execution/filters/Filter.java index 1e375acaeb8c..66cde5b4ef2a 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/Filter.java +++ b/platform/lang-api/src/com/intellij/execution/filters/Filter.java @@ -34,20 +34,23 @@ public interface Filter { protected NextAction myNextAction = NextAction.EXIT; protected final List 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 resultItems) { super(-1, -1, null, null); myResultItems = resultItems; } - + public List getResultItems() { List 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 - * null, 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 null, if there was no match, otherwise, an instance of {@link Result} */ @Nullable Result applyFilter(String line, int entireLength); diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java index 5fc2fe2cccea..12eff1bd76e6 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java @@ -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; diff --git a/platform/lang-impl/src/com/intellij/refactoring/actions/BasePlatformRefactoringAction.java b/platform/lang-impl/src/com/intellij/refactoring/actions/BasePlatformRefactoringAction.java index 92eae30aa5a5..8867ea92d2cc 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/actions/BasePlatformRefactoringAction.java +++ b/platform/lang-impl/src/com/intellij/refactoring/actions/BasePlatformRefactoringAction.java @@ -113,12 +113,10 @@ public abstract class BasePlatformRefactoringAction extends BaseRefactoringActio protected RefactoringActionHandler getHandler(@NotNull Language language, PsiElement element) { List 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; diff --git a/platform/lang-impl/src/com/intellij/refactoring/actions/ChangeSignatureAction.java b/platform/lang-impl/src/com/intellij/refactoring/actions/ChangeSignatureAction.java index 328768ccbc83..f07419aa9be1 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/actions/ChangeSignatureAction.java +++ b/platform/lang-impl/src/com/intellij/refactoring/actions/ChangeSignatureAction.java @@ -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 diff --git a/platform/lang-impl/src/com/intellij/refactoring/actions/ExtractIncludeAction.java b/platform/lang-impl/src/com/intellij/refactoring/actions/ExtractIncludeAction.java index 6625316d32c6..5e8133e518c1 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/actions/ExtractIncludeAction.java +++ b/platform/lang-impl/src/com/intellij/refactoring/actions/ExtractIncludeAction.java @@ -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()); } diff --git a/platform/platform-impl/src/com/intellij/diagnostic/DefaultIdeaErrorLogger.java b/platform/platform-impl/src/com/intellij/diagnostic/DefaultIdeaErrorLogger.java index 164403aa549d..901da17f1b4b 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/DefaultIdeaErrorLogger.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/DefaultIdeaErrorLogger.java @@ -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()); diff --git a/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java b/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java index d99a38c199b4..26429fc0e896 100644 --- a/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java +++ b/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java @@ -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()); } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java index 8a27864d35ee..b0a0694a412d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java @@ -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(); + } + } + } } diff --git a/platform/platform-impl/src/com/intellij/util/io/UrlConnectionUtil.java b/platform/platform-impl/src/com/intellij/util/io/UrlConnectionUtil.java index 9d7836c77f31..198ead74eeec 100644 --- a/platform/platform-impl/src/com/intellij/util/io/UrlConnectionUtil.java +++ b/platform/platform-impl/src/com/intellij/util/io/UrlConnectionUtil.java @@ -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; } diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTRunnerConsoleProperties.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTRunnerConsoleProperties.java index 977a28fb14e7..6bc2f72ea45e 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTRunnerConsoleProperties.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTRunnerConsoleProperties.java @@ -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 diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/TestProxyPrinterProvider.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/TestProxyPrinterProvider.java index 769a5225e58d..7368321924f8 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/TestProxyPrinterProvider.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/TestProxyPrinterProvider.java @@ -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); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java index 648124ff2aa1..ddbb79419f5c 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java @@ -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(); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebuggerExpressionComboBox.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebuggerExpressionComboBox.java index 8ff17b346e0e..051683b875c1 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebuggerExpressionComboBox.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebuggerExpressionComboBox.java @@ -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(); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTree.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTree.java index 2cf8c71b9594..5a49b3f866ff 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTree.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTree.java @@ -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) { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgPusher.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgPusher.java index 2e9321253b7e..2b6108dddbbc 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgPusher.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgPusher.java @@ -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 diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgPullAction.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgPullAction.java index c6650a12dd34..d5a934e1d86d 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgPullAction.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgPullAction.java @@ -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) { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgPullDialog.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgPullDialog.java index e140af36108b..15d2f7122baa 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgPullDialog.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgPullDialog.java @@ -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(); diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgPushDialog.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgPushDialog.java index b346c1534f6c..33e4709286cf 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgPushDialog.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgPushDialog.java @@ -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. */ diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenImportingTestCase.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenImportingTestCase.java index 9f49621133cf..860f78cabfe6 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenImportingTestCase.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenImportingTestCase.java @@ -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()); } diff --git a/python/resources/liveTemplates/Python.xml b/python/resources/liveTemplates/Python.xml index 96689b16a275..47928f82d8aa 100644 --- a/python/resources/liveTemplates/Python.xml +++ b/python/resources/liveTemplates/Python.xml @@ -4,8 +4,21 @@ +