diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryExtension.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryExtension.java index 6868023872b8..23c95d517bd6 100644 --- a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryExtension.java +++ b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryExtension.java @@ -184,6 +184,7 @@ public class TestDiscoveryExtension extends RunConfigurationExtension { } } + @SuppressWarnings("WeakerAccess") // called via reflection from com.intellij.InternalTestDiscoveryListener.flushCurrentTraces() public static void processAvailableTraces(final String[] fullTestNames, final String tracesDirectory, final String moduleName, diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/ModuleInsight.java b/java/idea-ui/src/com/intellij/ide/util/importProject/ModuleInsight.java index a52164de461d..992491b52790 100644 --- a/java/idea-ui/src/com/intellij/ide/util/importProject/ModuleInsight.java +++ b/java/idea-ui/src/com/intellij/ide/util/importProject/ModuleInsight.java @@ -459,6 +459,9 @@ public abstract class ModuleInsight { catch (IOException e) { LOG.info(e); } + catch (IllegalArgumentException e) { // may be thrown from java.util.zip.ZipCoder.toString for corrupted archive + LOG.info(e); + } catch (InternalError e) { // indicates that file is somehow damaged and cannot be processed LOG.info(e); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index d9d81f92a0c5..b643e14a85ad 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -325,77 +325,65 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh myHolder.add(checkFeature(expression, Feature.LAMBDA_EXPRESSIONS)); final PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent()); if (parent instanceof PsiExpressionStatement) return; + if (!myHolder.hasErrorResults() && !LambdaUtil.isValidLambdaContext(parent)) { + myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression) + .descriptionAndTooltip("Lambda expression not expected here").create()); + } + + PsiType functionalInterfaceType = null; if (!myHolder.hasErrorResults()) { - if (LambdaUtil.isValidLambdaContext(parent)) { - final PsiType functionalInterfaceType = expression.getFunctionalInterfaceType(); - if (functionalInterfaceType != null) { - final String notFunctionalMessage = LambdaHighlightingUtil.checkInterfaceFunctional(functionalInterfaceType); - if (notFunctionalMessage != null) { - HighlightInfo result = - HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(notFunctionalMessage) - .create(); - myHolder.add(result); - } - else { - final PsiCallExpression callExpression = parent instanceof PsiExpressionList && parent.getParent() instanceof PsiCallExpression ? - (PsiCallExpression)parent.getParent() : null; - final JavaResolveResult containingCallResolveResult = callExpression != null ? callExpression.resolveMethodGenerics() : null; - final String errorMessage; - if (containingCallResolveResult instanceof MethodCandidateInfo) { - errorMessage = ((MethodCandidateInfo)containingCallResolveResult).getParentInferenceErrorMessage((PsiExpressionList)parent); - } - else { - errorMessage = null; - } - if (errorMessage != null) { - HighlightInfo result = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) - .range(expression).descriptionAndTooltip(errorMessage).create(); - myHolder.add(result); - } - else { - final Map returnErrors = LambdaUtil - .checkReturnTypeCompatible(expression, LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType)); - if (returnErrors != null) { - for (Map.Entry entry : returnErrors.entrySet()) { - myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) - .range(entry.getKey()) - .descriptionAndTooltip(entry.getValue()).create()); - } - } - else { - final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); - final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult); - if (interfaceMethod != null) { - final PsiParameter[] parameters = interfaceMethod.getParameterList().getParameters(); - HighlightInfo result = LambdaHighlightingUtil - .checkParametersCompatible(expression, parameters, LambdaUtil.getSubstitutor(interfaceMethod, resolveResult)); - if (result != null) { - myHolder.add(result); - } - else { - checkFunctionalInterfaceTypeAccessible(expression, functionalInterfaceType); - } - } - } - } - } + functionalInterfaceType = expression.getFunctionalInterfaceType(); + if (functionalInterfaceType != null) { + final String notFunctionalMessage = LambdaHighlightingUtil.checkInterfaceFunctional(functionalInterfaceType); + if (notFunctionalMessage != null) { + myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression) + .descriptionAndTooltip(notFunctionalMessage).create()); } - else if (LambdaUtil.getFunctionalInterfaceType(expression, true) != null) { - myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip("Cannot infer functional interface type").create()); + else { + checkFunctionalInterfaceTypeAccessible(expression, functionalInterfaceType); } } - else { - HighlightInfo result = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression) - .descriptionAndTooltip("Lambda expression not expected here").create(); - myHolder.add(result); + else if (LambdaUtil.getFunctionalInterfaceType(expression, true) != null) { + myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip("Cannot infer functional interface type").create()); } - if (!myHolder.hasErrorResults()) { - final PsiElement body = expression.getBody(); - if (body instanceof PsiCodeBlock) { - myHolder.add(HighlightControlFlowUtil.checkUnreachableStatement((PsiCodeBlock)body)); + } + + if (!myHolder.hasErrorResults() && functionalInterfaceType != null) { + String parentInferenceErrorMessage = null; + final PsiCallExpression callExpression = parent instanceof PsiExpressionList && parent.getParent() instanceof PsiCallExpression ? + (PsiCallExpression)parent.getParent() : null; + final JavaResolveResult containingCallResolveResult = callExpression != null ? callExpression.resolveMethodGenerics() : null; + if (containingCallResolveResult instanceof MethodCandidateInfo) { + parentInferenceErrorMessage = ((MethodCandidateInfo)containingCallResolveResult).getParentInferenceErrorMessage((PsiExpressionList)parent); + } + final Map returnErrors = LambdaUtil.checkReturnTypeCompatible(expression, LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType)); + if (parentInferenceErrorMessage != null && (returnErrors == null || !returnErrors.containsValue(parentInferenceErrorMessage))) { + myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(parentInferenceErrorMessage).create()); + } + else if (returnErrors != null) { + for (Map.Entry entry : returnErrors.entrySet()) { + myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .range(entry.getKey()) + .descriptionAndTooltip(entry.getValue()).create()); } } } + + if (!myHolder.hasErrorResults() && functionalInterfaceType != null) { + final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); + final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult); + if (interfaceMethod != null) { + final PsiParameter[] parameters = interfaceMethod.getParameterList().getParameters(); + myHolder.add(LambdaHighlightingUtil.checkParametersCompatible(expression, parameters, LambdaUtil.getSubstitutor(interfaceMethod, resolveResult))); + } + } + + if (!myHolder.hasErrorResults()) { + final PsiElement body = expression.getBody(); + if (body instanceof PsiCodeBlock) { + myHolder.add(HighlightControlFlowUtil.checkUnreachableStatement((PsiCodeBlock)body)); + } + } } @Override diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/OptionalIsPresentInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/OptionalIsPresentInspection.java index 73d43330d3b8..93f360530609 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/OptionalIsPresentInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/OptionalIsPresentInspection.java @@ -134,7 +134,7 @@ public class OptionalIsPresentInspection extends BaseJavaBatchLocalInspectionToo if(!"get".equals(call.getMethodExpression().getReferenceName())) return false; PsiExpression qualifier = call.getMethodExpression().getQualifierExpression(); if(!(qualifier instanceof PsiReferenceExpression)) return false; - return ((PsiReferenceExpression)qualifier).resolve() == variable; + return ((PsiReferenceExpression)qualifier).isReferenceTo(variable); } @Contract("null, _ -> false") diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java index dcb6b192a3af..e29355b85b9c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java @@ -80,7 +80,10 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns return new JavaElementVisitor() { @Override public void visitMethodCallExpression(PsiMethodCallExpression methodCall) { - final PsiMethod method = methodCall.resolveMethod(); + PsiMethod method = methodCall.resolveMethod(); + if(method == null) return; + PsiClass psiClass = method.getContainingClass(); + if(psiClass == null) return; if (isCallOf(method, CommonClassNames.JAVA_UTIL_COLLECTION, STREAM_METHOD, 0)) { handleCollectionStream(methodCall); } @@ -90,27 +93,28 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns else if (isCallOf(method, CommonClassNames.JAVA_UTIL_OPTIONAL, IS_PRESENT_METHOD, 0)) { handleOptionalIsPresent(methodCall); } - else if (isCallOf(method, CommonClassNames.JAVA_UTIL_STREAM_STREAM, ANY_MATCH_METHOD, 1)) { + else if (isStreamCall(method, ANY_MATCH_METHOD)) { if(isParentNegated(methodCall)) { boolean argNegated = isArgumentLambdaNegated(methodCall); registerMatchFix(methodCall, - new SimplifyMatchNegationFix(argNegated ? "!Stream.anyMatch(x -> !(...))" : "!Stream.anyMatch(...)", + new SimplifyMatchNegationFix( + "!" + psiClass.getName() + (argNegated ? ".anyMatch(x -> !(...))" : ".anyMatch(...)"), argNegated ? ALL_MATCH_METHOD : NONE_MATCH_METHOD)); } } - else if (isCallOf(method, CommonClassNames.JAVA_UTIL_STREAM_STREAM, NONE_MATCH_METHOD, 1)) { + else if (isStreamCall(method, NONE_MATCH_METHOD)) { if(isParentNegated(methodCall)) { - registerMatchFix(methodCall, new SimplifyMatchNegationFix("!Stream.noneMatch(...)", ANY_MATCH_METHOD)); + registerMatchFix(methodCall, new SimplifyMatchNegationFix("!"+psiClass.getName()+".noneMatch(...)", ANY_MATCH_METHOD)); } if(isArgumentLambdaNegated(methodCall)) { - registerMatchFix(methodCall, new SimplifyMatchNegationFix("Stream.noneMatch(x -> !(...))", ALL_MATCH_METHOD)); + registerMatchFix(methodCall, new SimplifyMatchNegationFix(psiClass.getName()+".noneMatch(x -> !(...))", ALL_MATCH_METHOD)); } } - else if (isCallOf(method, CommonClassNames.JAVA_UTIL_STREAM_STREAM, ALL_MATCH_METHOD, 1)) { + else if (isStreamCall(method, ALL_MATCH_METHOD)) { if(isArgumentLambdaNegated(methodCall)) { boolean parentNegated = isParentNegated(methodCall); registerMatchFix(methodCall, - new SimplifyMatchNegationFix(parentNegated ? "!Stream.allMatch(x -> !(...))" : "Stream.allMatch(x -> !(...))", + new SimplifyMatchNegationFix((parentNegated ? "!" : "") + psiClass.getName() + ".allMatch(x -> !(...))", parentNegated ? ANY_MATCH_METHOD : NONE_MATCH_METHOD)); } } @@ -311,6 +315,14 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns return false; } + static boolean isStreamCall(@Nullable PsiMethod method, @NotNull String methodName) { + if (method == null || !methodName.equals(method.getName()) || method.getParameterList().getParametersCount() != 1) { + return false; + } + final PsiClass containingClass = method.getContainingClass(); + return containingClass != null && InheritanceUtil.isInheritor(containingClass, CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM); + } + interface CallChainFix { String getName(); void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor); @@ -627,11 +639,11 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns @Override public String getName() { - return "Replace "+myFrom+" with Stream."+myTo+"(...)"; + return "Replace "+myFrom+" with "+myTo+"(...)"; } public String getMessage() { - return myFrom+" can be replaced with Stream."+myTo+"(...)"; + return myFrom+" can be replaced with "+myTo+"(...)"; } @Override 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 2a356bd66862..79cb175e2668 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 @@ -28,8 +28,7 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.*; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.FactoryMap; -import com.intellij.util.containers.Stack; +import com.intellij.util.containers.FList; import com.siyeh.ig.numeric.UnnecessaryExplicitNumericCastInspection; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -50,18 +49,11 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { private final DfaValueFactory myFactory; private ControlFlow myCurrentFlow; - private Stack myCatchStack; - private final DfaValue myRuntimeException; - private final DfaValue myError; - private final DfaValue myString; + private FList myTrapStack = FList.emptyList(); + private final ExceptionTransfer myRuntimeException; + private final ExceptionTransfer myError; private final PsiType myNpe; private final PsiType myAssertionError; - private final Stack myElementStack = new Stack<>(); - - /** - * Variables for try-related control transfers. Contain exceptions or an (Throwable-inconvertible) string to indicate return inside finally - */ - private FactoryMap myExceptionHolders; ControlFlowAnalyzer(final DfaValueFactory valueFactory, @NotNull PsiElement codeFragment, boolean ignoreAssertions) { myFactory = valueFactory; @@ -69,26 +61,14 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { myProject = codeFragment.getProject(); myIgnoreAssertions = ignoreAssertions; GlobalSearchScope scope = codeFragment.getResolveScope(); - myRuntimeException = myFactory.createTypeValue(createClassType(scope, JAVA_LANG_RUNTIME_EXCEPTION), Nullness.NOT_NULL); - myError = myFactory.createTypeValue(createClassType(scope, JAVA_LANG_ERROR), Nullness.NOT_NULL); + myRuntimeException = new ExceptionTransfer(myFactory.createTypeValue(createClassType(scope, JAVA_LANG_RUNTIME_EXCEPTION), Nullness.NOT_NULL)); + myError = new ExceptionTransfer(myFactory.createTypeValue(createClassType(scope, JAVA_LANG_ERROR), Nullness.NOT_NULL)); myNpe = createClassType(scope, JAVA_LANG_NULL_POINTER_EXCEPTION); myAssertionError = createClassType(scope, JAVA_LANG_ASSERTION_ERROR); - myString = myFactory.createTypeValue(createClassType(scope, JAVA_LANG_STRING), Nullness.NOT_NULL); - - myExceptionHolders = new FactoryMap() { - @Nullable - @Override - protected DfaVariableValue create(PsiTryStatement key) { - String text = "java.lang.Object $exception" + myExceptionHolders.size() + "$"; - PsiParameter mockVar = JavaPsiFacade.getElementFactory(myProject).createParameterFromText(text, null); - return myFactory.getVarFactory().createVariableValue(mockVar, false); - } - }; } @Nullable public ControlFlow buildControlFlow() { - myCatchStack = new Stack<>(); myCurrentFlow = new ControlFlow(myFactory); try { myCodeFragment.accept(this); @@ -104,7 +84,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { addInstruction(new CheckReturnValueInstruction(myCodeFragment)); } - addInstruction(new ReturnInstruction(false, null)); + addInstruction(new ReturnInstruction(myFactory.controlTransfer(ReturnTransfer.INSTANCE, FList.emptyList()), null)); if (Registry.is("idea.dfa.live.variables.analysis")) { new LiveVariablesAnalyzer(myCurrentFlow, myFactory).flushDeadVariablesOnStatementFinish(); @@ -135,15 +115,10 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { private void startElement(PsiElement element) { myCurrentFlow.startElement(element); - myElementStack.push(element); } private void finishElement(PsiElement element) { myCurrentFlow.finishElement(element); - PsiElement popped = myElementStack.pop(); - if (element != popped) { - throw new AssertionError("Expected " + element + ", popped " + popped); - } if (element instanceof PsiStatement && !(element instanceof PsiReturnStatement)) { addInstruction(new FinishElementInstruction(element)); } @@ -250,9 +225,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { description.accept(this); } - CatchDescriptor cd = findNextCatch(false); - initException(myAssertionError, cd); - addThrowCode(cd, statement); + throwException(myAssertionError, statement); } finishElement(statement); } @@ -369,20 +342,36 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { PsiStatement exitedStatement = statement.findExitedStatement(); if (exitedStatement != null) { - flushVariablesOnControlTransfer(exitedStatement); - addInstruction(new GotoInstruction(getEndOffset(exitedStatement))); + controlTransfer(new InstructionTransfer(getEndOffset(exitedStatement), getVariablesInside(exitedStatement)), + getTrapsInsideStatement(exitedStatement)); } finishElement(statement); } + private void controlTransfer(InstructionTransfer target, FList traps) { + addInstruction(new ControlTransferInstruction(myFactory.controlTransfer(target, traps))); + } + + @NotNull + private FList getTrapsInsideStatement(PsiStatement statement) { + return FList.createFromReversed(ContainerUtil.reverse( + ContainerUtil.findAll(myTrapStack, cd -> PsiTreeUtil.isAncestor(statement, cd.getAnchor(), true)))); + } + + @NotNull + private List getVariablesInside(PsiElement exitedStatement) { + return ContainerUtil.map(PsiTreeUtil.findChildrenOfType(exitedStatement, PsiVariable.class), + var -> myFactory.getVarFactory().createVariableValue(var, false)); + } + @Override public void visitContinueStatement(PsiContinueStatement statement) { startElement(statement); PsiStatement continuedStatement = statement.findContinuedStatement(); if (continuedStatement instanceof PsiLoopStatement) { PsiStatement body = ((PsiLoopStatement)continuedStatement).getBody(); - flushVariablesOnControlTransfer(body); - addInstruction(new GotoInstruction(getEndOffset(body))); + controlTransfer(new InstructionTransfer(getEndOffset(body), getVariablesInside(body)), getTrapsInsideStatement(body)); + } else { addInstruction(new EmptyInstruction(null)); } @@ -590,24 +579,10 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { addInstruction(new CheckReturnValueInstruction(returnValue)); } - returnCheckingFinally(false, statement); + addInstruction(new ReturnInstruction(myFactory.controlTransfer(ReturnTransfer.INSTANCE, myTrapStack), statement)); finishElement(statement); } - private void returnCheckingFinally(boolean viaException, @NotNull PsiElement anchor) { - CatchDescriptor finallyDescriptor = findFinally(); - if (finallyDescriptor != null) { - addInstruction(new PushInstruction(getExceptionHolder(finallyDescriptor), null)); - addInstruction(new PushInstruction(myString, null)); - addInstruction(new AssignInstruction(null, null)); - addInstruction(new PopInstruction()); - - addInstruction(new GotoInstruction(finallyDescriptor.getJumpOffset(this))); - } else { - addInstruction(new ReturnInstruction(viaException, anchor)); - } - } - @Override public void visitSwitchLabelStatement(PsiSwitchLabelStatement statement) { startElement(statement); finishElement(statement); @@ -737,13 +712,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { if (exception != null) { exception.accept(this); - CatchDescriptor cd = findNextCatch(false); - if (cd == null) { - addInstruction(new FieldReferenceInstruction(exception, "thrown exception")); - addInstruction(new ReturnInstruction(true, statement)); - finishElement(statement); - return; - } addConditionalRuntimeThrow(); addInstruction(new DupInstruction()); @@ -753,105 +721,32 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { addInstruction(gotoInstruction); addInstruction(new FieldReferenceInstruction(exception, "thrown exception")); - initException(myNpe, cd); - addThrowCode(cd, statement); + throwException(myNpe, statement); gotoInstruction.setOffset(myCurrentFlow.getInstructionCount()); - addInstruction(new PushInstruction(getExceptionHolder(cd), null)); - addInstruction(new SwapInstruction()); - addInstruction(new AssignInstruction(null, null)); - addInstruction(new PopInstruction()); - addThrowCode(cd, statement); + throwException(exception.getType(), statement); } finishElement(statement); } private void addConditionalRuntimeThrow() { - CatchDescriptor cd = findNextCatch(false); - if (cd == null) { + if (myTrapStack.isEmpty()) { return; } pushUnknown(); final ConditionalGotoInstruction ifNoException = addInstruction(new ConditionalGotoInstruction(null, false, null)); - addInstruction(new EmptyStackInstruction()); - - addInstruction(new PushInstruction(getExceptionHolder(cd), null)); pushUnknown(); final ConditionalGotoInstruction ifError = addInstruction(new ConditionalGotoInstruction(null, false, null)); - addInstruction(new PushInstruction(myRuntimeException, null)); - GotoInstruction ifRuntime = addInstruction(new GotoInstruction(null)); + throwException(myRuntimeException, null); ifError.setOffset(myCurrentFlow.getInstructionCount()); - addInstruction(new PushInstruction(myError, null)); - ifRuntime.setOffset(myCurrentFlow.getInstructionCount()); - - addInstruction(new AssignInstruction(null, null)); - addInstruction(new PopInstruction()); - - addThrowCode(cd, null); + throwException(myError, null); ifNoException.setOffset(myCurrentFlow.getInstructionCount()); } - private void flushVariablesOnControlTransfer(PsiElement stopWhenAncestorOf) { - for (int i = myElementStack.size() - 1; i >= 0; i--) { - PsiElement scope = myElementStack.get(i); - if (PsiTreeUtil.isAncestor(scope, stopWhenAncestorOf, true)) { - break; - } - if (scope instanceof PsiCodeBlock) { - flushCodeBlockVariables((PsiCodeBlock)scope); - } - } - } - - // the exception object should be in $exception$ variable - private void addThrowCode(@Nullable CatchDescriptor cd, @Nullable PsiElement explicitThrower) { - if (cd == null) { - addInstruction(new ReturnInstruction(true, explicitThrower)); - return; - } - - flushVariablesOnControlTransfer(cd.getBlock()); - addInstruction(new GotoInstruction(cd.getJumpOffset(this))); - } - - @Nullable - private CatchDescriptor findNextCatch(boolean catchRethrow) { - if (myCatchStack.isEmpty()) { - return null; - } - - PsiElement currentElement = myElementStack.peek(); - - CatchDescriptor cd = myCatchStack.get(myCatchStack.size() - 1); - if (!cd.isFinally() && PsiTreeUtil.isAncestor(cd.getBlock().getParent(), currentElement, false)) { - int i = myCatchStack.size() - 2; - while (!catchRethrow && i >= 0 && !myCatchStack.get(i).isFinally() && myCatchStack.get(i).getTryStatement() == cd.getTryStatement()) { - i--; - } - if (i < 0) { - return null; - } - cd = myCatchStack.get(i); - } - - return cd; - } - - - @Nullable - private CatchDescriptor findFinally() { - for (int i = myCatchStack.size() - 1; i >= 0; i--) { - CatchDescriptor cd = myCatchStack.get(i); - if (cd.isFinally()) return cd; - } - - return null; - } - private static class ApplyNotNullInstruction extends Instruction { private final PsiMethodCallExpression myCall; @@ -874,50 +769,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { } } - private static class CatchDescriptor { - private final PsiType myType; - private final PsiParameter myParameter; - private final PsiCodeBlock myBlock; - private final boolean myIsFinally; - - public CatchDescriptor(PsiCodeBlock finallyBlock) { - myType = null; - myParameter = null; - myBlock = finallyBlock; - myIsFinally = true; - } - - public CatchDescriptor(PsiParameter parameter, PsiCodeBlock catchBlock) { - myType = parameter.getType(); - myParameter = parameter; - myBlock = catchBlock; - myIsFinally = false; - } - - public PsiCodeBlock getBlock() { - return myBlock; - } - public PsiTryStatement getTryStatement() { - return (PsiTryStatement) (isFinally() ? myBlock.getParent() : myBlock.getParent().getParent()); - } - - public PsiType getType() { - return myType; - } - - public boolean isFinally() { - return myIsFinally; - } - - public ControlFlow.ControlFlowOffset getJumpOffset(ControlFlowAnalyzer analyzer) { - return analyzer.getStartOffset(isFinally() ? myBlock : myBlock.getParent()); - } - - public PsiParameter getParameter() { - return myParameter; - } - } - @Override public void visitTryStatement(PsiTryStatement statement) { startElement(statement); @@ -926,27 +777,23 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { PsiCodeBlock tryBlock = statement.getTryBlock(); PsiCodeBlock finallyBlock = statement.getFinallyBlock(); - if (finallyBlock != null) { - myCatchStack.push(new CatchDescriptor(finallyBlock)); + Trap.TryFinally finallyDescriptor = finallyBlock != null ? new Trap.TryFinally(finallyBlock, getStartOffset(finallyBlock)) : null; + if (finallyDescriptor != null) { + myTrapStack = myTrapStack.prepend(finallyDescriptor); } PsiCatchSection[] sections = statement.getCatchSections(); - for (int i = sections.length - 1; i >= 0; i--) { - PsiCatchSection section = sections[i]; - PsiCodeBlock catchBlock = section.getCatchBlock(); - PsiParameter parameter = section.getParameter(); - if (parameter != null && catchBlock != null) { - PsiType type = parameter.getType(); - if (type instanceof PsiClassType || type instanceof PsiDisjunctionType) { - myCatchStack.push(new CatchDescriptor(parameter, catchBlock)); - continue; + if (sections.length > 0) { + LinkedHashMap clauses = new LinkedHashMap<>(); + for (PsiCatchSection section : sections) { + PsiCodeBlock catchBlock = section.getCatchBlock(); + if (catchBlock != null) { + clauses.put(section, getStartOffset(catchBlock)); } } - throw new CannotAnalyzeException(); + myTrapStack = myTrapStack.prepend(new Trap.TryCatch(statement, clauses)); } - ControlFlow.ControlFlowOffset endOffset = finallyBlock == null ? getEndOffset(statement) : getStartOffset(finallyBlock); - if (resourceList != null) { resourceList.accept(this); } @@ -955,79 +802,37 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { tryBlock.accept(this); } - addInstruction(new GotoInstruction(endOffset)); + InstructionTransfer gotoEnd = new InstructionTransfer(getEndOffset(statement), getVariablesInside(tryBlock)); + FList singleFinally = FList.createFromReversed(ContainerUtil.createMaybeSingletonList(finallyDescriptor)); + controlTransfer(gotoEnd, singleFinally); + + if (sections.length > 0) { + assert myTrapStack.getHead() instanceof Trap.TryCatch; + myTrapStack = myTrapStack.getTail(); + } for (PsiCatchSection section : sections) { - section.accept(this); - addInstruction(new GotoInstruction(endOffset)); - myCatchStack.pop(); + PsiCodeBlock catchBlock = section.getCatchBlock(); + if (catchBlock != null) { + visitCodeBlock(catchBlock); + } + controlTransfer(gotoEnd, singleFinally); } if (finallyBlock != null) { - CatchDescriptor finallyDescriptor = myCatchStack.pop(); + assert myTrapStack.getHead() instanceof Trap.TryFinally; + myTrapStack = myTrapStack.getTail().prepend(new Trap.InsideFinally(finallyBlock)); + finallyBlock.accept(this); - - //if $exception$==null => continue normal execution - addInstruction(new PushInstruction(getExceptionHolder(finallyDescriptor), null)); - addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null)); - addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, myProject)); - addInstruction(new ConditionalGotoInstruction(getEndOffset(statement), false, null)); - - // else throw $exception$ - rethrowException(finallyDescriptor, false); + addInstruction(new ControlTransferInstruction(null)); // DfaControlTransferValue is on stack + + assert myTrapStack.getHead() instanceof Trap.InsideFinally; + myTrapStack = myTrapStack.getTail(); } finishElement(statement); } - @Override - public void visitCatchSection(PsiCatchSection section) { - startElement(section); - PsiCodeBlock catchBlock = section.getCatchBlock(); - if (catchBlock != null) { - CatchDescriptor currentDescriptor = new CatchDescriptor(section.getParameter(), catchBlock); - DfaVariableValue exceptionHolder = getExceptionHolder(currentDescriptor); - - // exception is in exceptionHolder mock variable - // check if it's assignable to catch parameter type - PsiType declaredType = section.getCatchType(); - List flattened = declaredType instanceof PsiDisjunctionType ? - ((PsiDisjunctionType)declaredType).getDisjunctions() : - ContainerUtil.createMaybeSingletonList(declaredType); - for (PsiType catchType : flattened) { - addInstruction(new PushInstruction(exceptionHolder, null)); - addInstruction(new PushInstruction(myFactory.createTypeValue(catchType, Nullness.UNKNOWN), null)); - addInstruction(new BinopInstruction(JavaTokenType.INSTANCEOF_KEYWORD, null, myProject)); - addInstruction(new ConditionalGotoInstruction(ControlFlow.deltaOffset(getStartOffset(catchBlock), -5), false, null)); - } - - // not assignable => rethrow - rethrowException(currentDescriptor, true); - - // e = $exception$ - addInstruction(new PushInstruction(myFactory.getVarFactory().createVariableValue(section.getParameter(), false), null)); - addInstruction(new PushInstruction(exceptionHolder, null)); - addInstruction(new AssignInstruction(null, null)); - addInstruction(new PopInstruction()); - - addInstruction(new FlushVariableInstruction(exceptionHolder)); - - catchBlock.accept(this); - } - finishElement(section); - } - - private void rethrowException(CatchDescriptor currentDescriptor, boolean catchRethrow) { - CatchDescriptor nextCatch = findNextCatch(catchRethrow); - if (nextCatch != null) { - addInstruction(new PushInstruction(getExceptionHolder(nextCatch), null, false)); - addInstruction(new PushInstruction(getExceptionHolder(currentDescriptor), null, true)); - addInstruction(new AssignInstruction(null, null)); - addInstruction(new PopInstruction()); - } - addThrowCode(nextCatch, null); - } - @Override public void visitResourceList(PsiResourceList resourceList) { for (PsiResourceListElement resource : resourceList) { @@ -1044,7 +849,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { final List closerExceptions = ExceptionUtil.getCloserExceptions(resource); if (!closerExceptions.isEmpty()) { - addThrows(null, findNextCatch(false), closerExceptions.toArray(new PsiClassType[closerExceptions.size()])); + addThrows(null, closerExceptions.toArray(new PsiClassType[closerExceptions.size()])); } } } @@ -1409,35 +1214,28 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { } private void addMethodThrows(PsiMethod method, @Nullable PsiElement explicitCall) { - CatchDescriptor cd = findNextCatch(false); if (method != null) { - PsiClassType[] refs = method.getThrowsList().getReferencedTypes(); - addThrows(explicitCall, cd, refs); + addThrows(explicitCall, method.getThrowsList().getReferencedTypes()); } } - private void addThrows(@Nullable PsiElement explicitCall, CatchDescriptor cd, PsiClassType[] refs) { + private void addThrows(@Nullable PsiElement explicitCall, PsiClassType[] refs) { for (PsiClassType ref : refs) { pushUnknown(); ConditionalGotoInstruction cond = new ConditionalGotoInstruction(null, false, null); addInstruction(cond); - addInstruction(new EmptyStackInstruction()); - initException(ref, cd); - addThrowCode(cd, explicitCall); + throwException(ref, explicitCall); cond.setOffset(myCurrentFlow.getInstructionCount()); } } - private void initException(PsiType ref, @Nullable CatchDescriptor cd) { - if (cd == null) return; - addInstruction(new PushInstruction(getExceptionHolder(cd), null)); - addInstruction(new PushInstruction(myFactory.createTypeValue(ref, Nullness.NOT_NULL), null)); - addInstruction(new AssignInstruction(null, null)); - addInstruction(new PopInstruction()); + private void throwException(PsiType ref, @Nullable PsiElement anchor) { + throwException(new ExceptionTransfer(myFactory.createTypeValue(ref, Nullness.NOT_NULL)), anchor); } - private DfaVariableValue getExceptionHolder(CatchDescriptor cd) { - return myExceptionHolders.get(cd.getTryStatement()); + private void throwException(ExceptionTransfer kind, @Nullable PsiElement anchor) { + addInstruction(new EmptyStackInstruction()); + addInstruction(new ReturnInstruction(myFactory.controlTransfer(kind, myTrapStack), anchor)); } @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) { @@ -1488,11 +1286,12 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, myProject)); ConditionalGotoInstruction ifNotFail = new ConditionalGotoInstruction(null, true, null); addInstruction(ifNotFail); - returnCheckingFinally(true, expression); + addInstruction(new ReturnInstruction(myFactory.controlTransfer(new ExceptionTransfer(DfaUnknownValue.getInstance()), myTrapStack), expression)); + ifNotFail.setOffset(myCurrentFlow.getInstructionCount()); } - if (!myCatchStack.isEmpty()) { + if (!myTrapStack.isEmpty()) { addMethodThrows(expression.resolveMethod(), expression); } @@ -1588,7 +1387,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { addConditionalRuntimeThrow(); addInstruction(new MethodCallInstruction(expression, null, constructor == null ? Collections.emptyList() : getMethodContracts(constructor))); - if (!myCatchStack.isEmpty()) { + if (!myTrapStack.isEmpty()) { addMethodThrows(constructor, expression); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index 3f8fb818a3fb..6dd56df706fd 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -237,7 +237,9 @@ public class DfaMemoryStateImpl implements DfaMemoryState { @Override public void emptyStack() { myCachedHash = null; - myStack.clear(); + while (!myStack.isEmpty() && !(myStack.peek() instanceof DfaControlTransferValue)) { + myStack.pop(); + } } @Override @@ -677,7 +679,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState { setVariableState(dfaVar, newState); return true; } - return applyRelation(dfaVar, myFactory.getConstFactory().getNull(), false); + return !getVariableState(dfaVar).isNotNull() && applyRelation(dfaVar, myFactory.getConstFactory().getNull(), false); } if (applyRelation(dfaVar, myFactory.getConstFactory().getNull(), true)) { DfaVariableState newState = getVariableState(dfaVar).withInstanceofValue((DfaTypeValue)dfaRight); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/controlTransfer.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/controlTransfer.kt new file mode 100644 index 000000000000..c19d8bcb16df --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/controlTransfer.kt @@ -0,0 +1,125 @@ +/* + * Copyright 2000-2016 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 + +import com.intellij.codeInspection.dataFlow.instructions.Instruction +import com.intellij.codeInspection.dataFlow.value.DfaTypeValue +import com.intellij.codeInspection.dataFlow.value.DfaValue +import com.intellij.codeInspection.dataFlow.value.DfaValueFactory +import com.intellij.codeInspection.dataFlow.value.DfaVariableValue +import com.intellij.psi.* +import com.intellij.util.containers.FList +import java.util.* + +/** + * @author peter + */ +class DfaControlTransferValue(factory: DfaValueFactory, + val target: TransferTarget, + val traps: FList) : DfaValue(factory) { + override fun toString() = target.toString() + " " + traps.toString() +} + +interface TransferTarget +data class ExceptionTransfer(val throwable: DfaValue) : TransferTarget +data class InstructionTransfer(val offset: ControlFlow.ControlFlowOffset, val toFlush: List) : TransferTarget +object ReturnTransfer : TransferTarget + +open class ControlTransferInstruction(val transfer: DfaControlTransferValue?) : Instruction() { + override fun accept(runner: DataFlowRunner, state: DfaMemoryState, visitor: InstructionVisitor): Array { + val transferValue = transfer ?: state.pop() as DfaControlTransferValue + return ControlTransferHandler(state, runner, transferValue.target).iteration(transferValue.traps).toTypedArray() + } + + override fun toString() = transfer.toString() +} + +sealed class Trap(val anchor: PsiElement) { + class TryCatch(tryStatement : PsiTryStatement, val clauses: LinkedHashMap): Trap(tryStatement) + class TryFinally(val finallyBlock: PsiCodeBlock, val jumpOffset: ControlFlow.ControlFlowOffset): Trap(finallyBlock) + class InsideFinally(val finallyBlock: PsiCodeBlock): Trap(finallyBlock) +} + +private class ControlTransferHandler(val state: DfaMemoryState, val runner: DataFlowRunner, val target: TransferTarget) { + var throwableState: DfaVariableState? = null + + fun iteration(traps: FList): List { + val (head, tail) = traps.head to traps.tail + return when (head) { + null -> transferToTarget() + is Trap.TryCatch -> if (target is ExceptionTransfer) processCatches(head, target.throwable, tail) else iteration(tail) + is Trap.TryFinally -> goToFinally(head.jumpOffset.instructionOffset, tail) + is Trap.InsideFinally -> leaveFinally(tail) + } + } + + private fun transferToTarget(): List { + return when (target) { + is InstructionTransfer -> { + target.toFlush.forEach { state.flushVariable(it) } + listOf(DfaInstructionState(runner.getInstruction(target.offset.instructionOffset), state)) + } + else -> emptyList() + } + } + + private fun goToFinally(offset: Int, traps: FList): List { + state.push(runner.factory.controlTransfer(target, traps)) + return listOf(DfaInstructionState(runner.getInstruction(offset), state)) + } + + private fun leaveFinally(traps: FList): List { + state.pop() as DfaControlTransferValue + return iteration(traps) + } + + private fun processCatches(tryCatch: Trap.TryCatch, thrownValue: DfaValue, traps: FList): List { + val result = arrayListOf() + for ((catchSection, jumpOffset) in tryCatch.clauses) { + val param = catchSection.parameter ?: continue + if (throwableState == null) throwableState = initVariableState(param, thrownValue) + + for (caughtType in allCaughtTypes(param)) { + throwableState?.withInstanceofValue(caughtType)?.let { varState -> + result.add(DfaInstructionState(runner.getInstruction(jumpOffset.instructionOffset), stateForCatchClause(param, varState))) + } + + throwableState = throwableState?.withNotInstanceofValue(caughtType) ?: return result + } + } + return result + iteration(traps) + } + + private fun allCaughtTypes(param: PsiParameter): List { + val psiTypes = param.type.let { if (it is PsiDisjunctionType) it.disjunctions else listOfNotNull(it) } + return psiTypes.map { runner.factory.createTypeValue(it, Nullness.NOT_NULL) }.filterIsInstance() + } + + private fun stateForCatchClause(param: PsiParameter, varState: DfaVariableState): DfaMemoryState { + val catchingCopy = state.createCopy() as DfaMemoryStateImpl + catchingCopy.setVariableState(catchingCopy.factory.varFactory.createVariableValue(param, false), varState) + return catchingCopy + } + + private fun initVariableState(param: PsiParameter, throwable: DfaValue): DfaVariableState { + val sampleVar = (state as DfaMemoryStateImpl).factory.varFactory.createVariableValue(param, false) + val varState = state.createVariableState(sampleVar).withNullability(Nullness.NOT_NULL) + return if (throwable is DfaTypeValue) varState.withInstanceofValue(throwable)!! else varState + } + +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ReturnInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ReturnInstruction.java index 9c67fbe9f5e1..4c84e46ea1fb 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ReturnInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ReturnInstruction.java @@ -26,14 +26,14 @@ package com.intellij.codeInspection.dataFlow.instructions; import com.intellij.codeInspection.dataFlow.*; import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class ReturnInstruction extends Instruction { - private final boolean isViaException; +public class ReturnInstruction extends ControlTransferInstruction { private final PsiElement myAnchor; - public ReturnInstruction(boolean isViaException, @Nullable PsiElement anchor) { - this.isViaException = isViaException; + public ReturnInstruction(@NotNull DfaControlTransferValue transfer, @Nullable PsiElement anchor) { + super(transfer); myAnchor = anchor; } @@ -43,15 +43,8 @@ public class ReturnInstruction extends Instruction { } public boolean isViaException() { - return isViaException; + DfaControlTransferValue transfer = getTransfer(); + return transfer != null && transfer.getTarget() instanceof ExceptionTransfer; } - @Override - public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) { - return DfaInstructionState.EMPTY_ARRAY; - } - - public String toString() { - return "RETURN"; - } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java index 9a20ae691695..c8e4d60f3d24 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java @@ -24,12 +24,17 @@ */ package com.intellij.codeInspection.dataFlow.value; +import com.intellij.codeInspection.dataFlow.DfaControlTransferValue; import com.intellij.codeInspection.dataFlow.Nullness; +import com.intellij.codeInspection.dataFlow.TransferTarget; +import com.intellij.codeInspection.dataFlow.Trap; import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.FList; +import com.intellij.util.containers.FactoryMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -101,18 +106,6 @@ public class DfaValueFactory { return getConstFactory().create(literal); } - @Nullable - public static PsiVariable resolveUnqualifiedVariable(PsiReferenceExpression refExpression) { - if (isEffectivelyUnqualified(refExpression)) { - PsiElement resolved = refExpression.resolve(); - if (resolved instanceof PsiVariable) { - return (PsiVariable)resolved; - } - } - - return null; - } - public static boolean isEffectivelyUnqualified(PsiReferenceExpression refExpression) { PsiExpression qualifier = refExpression.getQualifierExpression(); if (qualifier == null) { @@ -129,6 +122,13 @@ public class DfaValueFactory { return false; } + public DfaControlTransferValue controlTransfer(TransferTarget kind, FList traps) { + return myControlTransfers.get(Pair.create(kind, traps)); + } + + private final Map>, DfaControlTransferValue> myControlTransfers = + FactoryMap.createMap(p -> new DfaControlTransferValue(this, p.first, p.second)); + private final DfaVariableValue.Factory myVarFactory; private final DfaConstValue.Factory myConstFactory; private final DfaBoxedValue.Factory myBoxedFactory; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8CollectionRemoveIfInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8CollectionRemoveIfInspection.java index 682ed7749a31..2060327bd3dd 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8CollectionRemoveIfInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8CollectionRemoveIfInspection.java @@ -239,7 +239,7 @@ public class Java8CollectionRemoveIfInspection extends BaseJavaBatchLocalInspect if(!method.equals(expression.getReferenceName())) return false; PsiExpression qualifier = expression.getQualifierExpression(); if(!(qualifier instanceof PsiReferenceExpression)) return false; - return ((PsiReferenceExpression)qualifier).resolve() == myIterator; + return ((PsiReferenceExpression)qualifier).isReferenceTo(myIterator); } public PsiVariable getNextElementVariable(PsiStatement statement) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8ReplaceMapGetInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8ReplaceMapGetInspection.java index 030ce25d8654..ef8f4986aa16 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8ReplaceMapGetInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8ReplaceMapGetInspection.java @@ -159,7 +159,7 @@ public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTo PsiElement[] elements = declaration.getDeclaredElements(); if(elements.length > 0) { PsiElement lastDeclaration = elements[elements.length - 1]; - if(lastDeclaration instanceof PsiLocalVariable && lastDeclaration == target.resolve()) { + if(lastDeclaration instanceof PsiLocalVariable && target.isReferenceTo(lastDeclaration)) { PsiLocalVariable var = (PsiLocalVariable)lastDeclaration; PsiExpression initializer = PsiUtil.skipParenthesizedExprDown(var.getInitializer()); if (initializer instanceof PsiMethodCallExpression && diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/ParameterNameHintsManager.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/ParameterNameHintsManager.java index 6cfe9cd1593d..90092aab30dc 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/ParameterNameHintsManager.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/ParameterNameHintsManager.java @@ -42,7 +42,7 @@ public class ParameterNameHintsManager { "(from*, to*)", "(min*, max*)", "(key, value)", - "(format, arg)" + "(format, arg*)" ).map((s) -> MatcherConstructor.INSTANCE.createMatcher(s)) .collect(Collectors.toList()); @@ -122,9 +122,9 @@ public class ParameterNameHintsManager { private static boolean isSetter(PsiMethod method) { String methodName = method.getName(); - if (hasSingleParameter(method) - && methodName.startsWith("set") - && methodName.length() > 3 && Character.isUpperCase(methodName.charAt(3))) { + if (hasSingleParameter(method) && methodName.startsWith("set") + && (methodName.length() == 3 + || methodName.length() > 3 && Character.isUpperCase(methodName.charAt(3)))) { return true; } return false; diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java index 12d6646f9a2f..f32fc4a2a012 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java @@ -34,7 +34,6 @@ import com.intellij.openapi.application.impl.LaterInvocator; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; @@ -46,7 +45,6 @@ import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.psi.util.FileTypeUtils; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiUtil; -import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; import gnu.trove.THashSet; @@ -326,9 +324,7 @@ public abstract class ImportClassFixBase implements Inte final QuestionAction action = createQuestionAction(candidates, element.getProject(), editor); PsiFile psiFile = element.getContainingFile(); if (candidates.size() == 1 && - (FileTypeUtils.isInServerPageFile(psiFile) ? - CodeInsightSettings.getInstance().JSP_ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY : - CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY) && + ImportClassFixBase.isAddUnambiguousImportsOnTheFlyEnabled(psiFile) && (ApplicationManager.getApplication().isUnitTestMode() || DaemonListeners.canChangeFileSilently(psiFile)) && !LaterInvocator.isInModalContext()) { CommandProcessor.getInstance().runUndoTransparentAction(() -> action.execute()); diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaCopyPasteReferenceProcessor.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaCopyPasteReferenceProcessor.java index d7dea455820a..98689fab9dd9 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaCopyPasteReferenceProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaCopyPasteReferenceProcessor.java @@ -17,6 +17,7 @@ package com.intellij.codeInsight.editorActions; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFix; +import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFixBase; import com.intellij.codeInsight.daemon.impl.quickfix.StaticImportConstantFix; import com.intellij.codeInsight.daemon.impl.quickfix.StaticImportMethodFix; import com.intellij.openapi.diagnostic.Logger; @@ -106,8 +107,7 @@ public class JavaCopyPasteReferenceProcessor extends CopyPasteReferenceProcessor } } - if (CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY || - CodeInsightSettings.getInstance().ADD_MEMBER_IMPORTS_ON_THE_FLY) { + if (ImportClassFixBase.isAddUnambiguousImportsOnTheFlyEnabled(file)) { for (int i = 0; i < refs.length; i++) { if (isUnambiguous(refs[i])) { refs[i] = null; @@ -131,8 +131,7 @@ public class JavaCopyPasteReferenceProcessor extends CopyPasteReferenceProcessor int classCount = new ImportClassFix(ref).getClassesToImport().size(); if (constCount + classCount > 1) return false; if (constCount + classCount == 0) return true; - return constCount == 1 ? CodeInsightSettings.getInstance().ADD_MEMBER_IMPORTS_ON_THE_FLY - : CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY; + return constCount == 0 || CodeInsightSettings.getInstance().ADD_MEMBER_IMPORTS_ON_THE_FLY; } @Override diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java index 5a1a4c2d0583..2bdbe16a8973 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java @@ -24,6 +24,7 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.controlFlow.*; import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.ig.psiutils.ExpressionUtils; import one.util.streamex.StreamEx; @@ -152,4 +153,16 @@ abstract class MigrateToStreamFix implements LocalQuickFix { statement.delete(); } } + + static boolean isReachable(PsiReturnStatement target) { + ControlFlow flow; + try { + flow = ControlFlowFactory.getInstance(target.getProject()) + .getControlFlow(target.getParent(), LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance()); + } + catch (AnalysisCanceledException e) { + return true; + } + return ControlFlowUtil.isInstructionReachable(flow, flow.getStartOffset(target), 0); + } } diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java index ff9b77733dfe..cca1d5ce74ee 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.PsiEquivalenceUtil; import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.InitializerUsageStatus; import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.util.RefactoringUtil; import com.siyeh.ig.psiutils.ExpressionUtils; import org.jetbrains.annotations.NotNull; @@ -52,10 +53,12 @@ class ReplaceWithFindFirstFix extends MigrateToStreamFix { if (!ExpressionUtils.isSimpleExpression(orElseExpression)) return null; stream = generateOptionalUnwrap(stream, tb, value, orElseExpression, null); restoreComments(loopStatement, body); - if (nextReturnStatement.getParent() == loopStatement.getParent()) { + boolean sibling = nextReturnStatement.getParent() == loopStatement.getParent(); + PsiElement replacement = loopStatement.replace(elementFactory.createStatementFromText("return " + stream + ";", loopStatement)); + if(sibling || !isReachable(nextReturnStatement)) { nextReturnStatement.delete(); } - return loopStatement.replace(elementFactory.createStatementFromText("return " + stream + ";", loopStatement)); + return replacement; } else { PsiStatement[] statements = tb.getStatements(); @@ -84,6 +87,17 @@ class ReplaceWithFindFirstFix extends MigrateToStreamFix { return replaceInitializer(loopStatement, var, initializer, replacementText, status); } } + PsiAssignmentExpression previousAssignment = + ExpressionUtils.getAssignment(PsiTreeUtil.skipSiblingsBackward(loopStatement, PsiWhiteSpace.class, PsiComment.class)); + if(previousAssignment != null) { + PsiExpression prevRValue = previousAssignment.getRExpression(); + PsiExpression prevLValue = previousAssignment.getLExpression(); + if(prevRValue != null && prevLValue instanceof PsiReferenceExpression && ((PsiReferenceExpression)prevLValue).isReferenceTo(var)) { + previousAssignment.delete(); + return loopStatement.replace(elementFactory.createStatementFromText( + var.getName() + " = " + generateOptionalUnwrap(stream, tb, value, prevRValue, var.getType()) + ";", loopStatement)); + } + } return loopStatement.replace(elementFactory.createStatementFromText( var.getName() + " = " + generateOptionalUnwrap(stream, tb, value, lValue, var.getType()) + ";", loopStatement)); } diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithMatchFix.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithMatchFix.java index 1053a306f89c..1d0b0a14bfff 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithMatchFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithMatchFix.java @@ -69,7 +69,11 @@ class ReplaceWithMatchFix extends MigrateToStreamFix { removeLoop(loopStatement); return returnValue.replace(elementFactory.createExpressionFromText(streamText, nextReturnStatement)); } - return loopStatement.replace(elementFactory.createStatementFromText("return " + streamText + ";", loopStatement)); + PsiElement result = loopStatement.replace(elementFactory.createStatementFromText("return " + streamText + ";", loopStatement)); + if(!isReachable(nextReturnStatement)) { + nextReturnStatement.delete(); + } + return result; } } } @@ -84,27 +88,30 @@ class ReplaceWithMatchFix extends MigrateToStreamFix { if(assignment != null) { PsiExpression lValue = assignment.getLExpression(); PsiExpression rValue = assignment.getRExpression(); - if (!(lValue instanceof PsiReferenceExpression) || rValue == null) return null; - PsiElement maybeVar = ((PsiReferenceExpression)lValue).resolve(); - if(maybeVar instanceof PsiVariable) { - // Simplify single assignments like this: - // boolean flag = false; - // for(....) if(...) {flag = true; break;} - PsiVariable var = (PsiVariable)maybeVar; - PsiExpression initializer = var.getInitializer(); - InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(var, loopStatement); - if(initializer != null && status != InitializerUsageStatus.UNKNOWN) { - String replacement; - if(ExpressionUtils.isLiteral(initializer, Boolean.FALSE) && - ExpressionUtils.isLiteral(rValue, Boolean.TRUE)) { - replacement = streamText; - } else if(ExpressionUtils.isLiteral(initializer, Boolean.TRUE) && - ExpressionUtils.isLiteral(rValue, Boolean.FALSE)) { - replacement = "!"+streamText; - } else { - replacement = streamText + "?" + rValue.getText() + ":" + initializer.getText(); + if ((lValue instanceof PsiReferenceExpression) && rValue != null) { + PsiElement maybeVar = ((PsiReferenceExpression)lValue).resolve(); + if (maybeVar instanceof PsiVariable) { + // Simplify single assignments like this: + // boolean flag = false; + // for(....) if(...) {flag = true; break;} + PsiVariable var = (PsiVariable)maybeVar; + PsiExpression initializer = var.getInitializer(); + InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(var, loopStatement); + if (initializer != null && status != InitializerUsageStatus.UNKNOWN) { + String replacement; + if (ExpressionUtils.isLiteral(initializer, Boolean.FALSE) && + ExpressionUtils.isLiteral(rValue, Boolean.TRUE)) { + replacement = streamText; + } + else if (ExpressionUtils.isLiteral(initializer, Boolean.TRUE) && + ExpressionUtils.isLiteral(rValue, Boolean.FALSE)) { + replacement = "!" + streamText; + } + else { + replacement = streamText + "?" + rValue.getText() + ":" + initializer.getText(); + } + return replaceInitializer(loopStatement, var, initializer, replacement, status); } - return replaceInitializer(loopStatement, var, initializer, replacement, status); } } } diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java index d84181e06202..13281bd34db4 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java @@ -380,7 +380,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo @Contract("_, null -> false") static boolean isIdentityMapping(PsiVariable variable, PsiExpression mapperCall) { - return mapperCall instanceof PsiReferenceExpression && ((PsiReferenceExpression)mapperCall).resolve() == variable; + return mapperCall instanceof PsiReferenceExpression && ((PsiReferenceExpression)mapperCall).isReferenceTo(variable); } @Nullable @@ -724,7 +724,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo if(args.length != 1) return null; comparatorExpression = args[0]; } - if(!(listExpression instanceof PsiReferenceExpression) || ((PsiReferenceExpression)listExpression).resolve() != list) return null; + if(!(listExpression instanceof PsiReferenceExpression) || !((PsiReferenceExpression)listExpression).isReferenceTo(list)) return null; if(comparatorExpression == null || ExpressionUtils.isNullLiteral(comparatorExpression)) return ""; return comparatorExpression.getText(); } @@ -762,7 +762,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); if (!(qualifierExpression instanceof PsiReferenceExpression)) return null; PsiLocalVariable collectionVariable = extractCollectionVariable(expression.getMethodExpression().getQualifierExpression()); - if (collectionVariable == null || ((PsiReferenceExpression)qualifierExpression).resolve() != collectionVariable) return null; + if (collectionVariable == null || !((PsiReferenceExpression)qualifierExpression).isReferenceTo(collectionVariable)) return null; PsiExpression initializer = collectionVariable.getInitializer(); if (initializer == null) return null; PsiType type = initializer.getType(); @@ -1080,7 +1080,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo // check that increment is like for(...;...;i++) if(!(forStatement.getUpdate() instanceof PsiExpressionStatement)) return null; PsiExpression lValue = extractIncrementedLValue(((PsiExpressionStatement)forStatement.getUpdate()).getExpression()); - if(!(lValue instanceof PsiReferenceExpression) || ((PsiReferenceExpression)lValue).resolve() != counter) return null; + if(!(lValue instanceof PsiReferenceExpression) || !((PsiReferenceExpression)lValue).isReferenceTo(counter)) return null; // check that condition is like for(...;i nullables = new HashSet<>(); + nullables.addAll(manager.getNotNulls()); + nullables.addAll(manager.getNullables()); + return Arrays.stream(annotations) + .filter(annotation -> !nullables.contains(annotation.getQualifiedName())) + .toArray(PsiAnnotation[]::new); + } + }); + return type; + } + private static boolean isFinalVariableOnLHS(PsiExpression expr) { if (expr instanceof PsiReferenceExpression && RefactoringUtil.isAssignmentLHS(expr)) { final PsiElement resolve = ((PsiReferenceExpression)expr).resolve(); diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index 8535e5756a0c..55c581f13105 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -906,6 +906,16 @@ public class LambdaUtil { } } + public static T performWithLambdaTargetType(PsiLambdaExpression lambdaExpression, PsiType targetType, Producer producer) { + try { + getFunctionalTypeMap().put(lambdaExpression, targetType); + return producer.produce(); + } + finally { + getFunctionalTypeMap().remove(lambdaExpression); + } + } + /** * Generate lambda text for single argument expression lambda * diff --git a/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java b/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java index a2d3e817c218..50d378c78ba3 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java @@ -126,7 +126,7 @@ public class PsiPrimitiveType extends PsiType.Stub { public static PsiPrimitiveType getUnboxedType(PsiType type) { if (!(type instanceof PsiClassType)) return null; - assert type.isValid() : type; + PsiUtil.ensureValidType(type); LanguageLevel languageLevel = ((PsiClassType)type).getLanguageLevel(); if (!languageLevel.isAtLeast(LanguageLevel.JDK_1_5)) return null; diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java b/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java index 13ca286944bf..c791ba4574b8 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java @@ -52,8 +52,6 @@ import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentMap; -import static com.intellij.openapi.util.Pair.pair; - public abstract class BaseExternalAnnotationsManager extends ExternalAnnotationsManager { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.BaseExternalAnnotationsManager"); private static final Key EXTERNAL_ANNO_MARKER = Key.create("EXTERNAL_ANNO_MARKER"); @@ -61,9 +59,9 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations protected final PsiManager myPsiManager; - private final ConcurrentMap> myExternalAnnotations = ContainerUtil.createConcurrentWeakKeySoftValueMap(); + private final ConcurrentMap> myExternalAnnotationsCache = ContainerUtil.createConcurrentWeakKeySoftValueMap(); private final Map myAnnotationDataCache = new WeakKeyWeakValueHashMap(); - private final ConcurrentMap, Long>> myAnnotationFileToDataAndModStamp = ContainerUtil.createConcurrentSoftMap(); + private final ConcurrentMap, Long>> myAnnotationFileToDataAndModStampCache = ContainerUtil.createConcurrentSoftMap(); public BaseExternalAnnotationsManager(@NotNull PsiManager psiManager) { myPsiManager = psiManager; @@ -166,7 +164,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations @NotNull private MostlySingularMultiMap getDataFromFile(@NotNull PsiFile file) { - Pair, Long> cached = myAnnotationFileToDataAndModStamp.get(file); + Pair, Long> cached = myAnnotationFileToDataAndModStampCache.get(file); long fileModificationStamp = file.getModificationStamp(); if (cached != null && cached.getSecond() == fileModificationStamp) { return cached.getFirst(); @@ -188,7 +186,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations } MostlySingularMultiMap result = handler.getResult(); - myAnnotationFileToDataAndModStamp.put(file, pair(result, fileModificationStamp)); + myAnnotationFileToDataAndModStampCache.put(file, Pair.create(result, fileModificationStamp)); return result; } @@ -234,7 +232,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations final VirtualFile virtualFile = containingFile.getVirtualFile(); if (virtualFile == null) return null; - final List files = myExternalAnnotations.get(virtualFile); + final List files = myExternalAnnotationsCache.get(virtualFile); if (files == NULL_LIST) return null; if (files != null) { @@ -263,7 +261,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations } if (possibleAnnotations.isEmpty()) { - myExternalAnnotations.put(virtualFile, NULL_LIST); + myExternalAnnotationsCache.put(virtualFile, NULL_LIST); return null; } @@ -277,7 +275,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations return w1 == w2 ? 0 : w1 ? -1 : 1; } }); - myExternalAnnotations.put(virtualFile, result); + myExternalAnnotationsCache.put(virtualFile, result); return result; } @@ -285,8 +283,8 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations protected abstract List getExternalAnnotationsRoots(@NotNull VirtualFile libraryFile); protected void dropCache() { - myExternalAnnotations.clear(); - myAnnotationFileToDataAndModStamp.clear(); + myExternalAnnotationsCache.clear(); + myAnnotationFileToDataAndModStampCache.clear(); cache.clear(); } @@ -348,7 +346,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations @NotNull List annotationFiles) { VirtualFile virtualFile = fromFile.getVirtualFile(); if (virtualFile != null) { - myExternalAnnotations.put(virtualFile, annotationFiles); + myExternalAnnotationsCache.put(virtualFile, annotationFiles); } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSessionContainer.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSessionContainer.java index fcf4496e1e17..4301e330b2ac 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSessionContainer.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSessionContainer.java @@ -17,11 +17,13 @@ package com.intellij.psi.impl.source.resolve.graphInference; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Ref; import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy; import com.intellij.psi.impl.source.resolve.graphInference.constraints.ExpressionCompatibilityConstraint; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.util.*; +import com.intellij.util.Producer; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -118,17 +120,8 @@ public class InferenceSessionContainer { } if (session != null) { - final CompoundInitialState compoundInitialState = createState(session); - final InitialInferenceState initialInferenceState = compoundInitialState.getInitialState(PsiTreeUtil.getParentOfType(argumentList, PsiCall.class)); - if (initialInferenceState != null) { - InferenceSession childSession = new InferenceSession(initialInferenceState); - final List errorMessages = session.getIncompatibleErrorMessages(); - if (errorMessages != null) { - return childSession.prepareSubstitution(); - } - return childSession - .collectAdditionalAndInfer(parameters, arguments, properties, compoundInitialState.getInitialSubstitutor()); - } + final PsiSubstitutor childSubstitutor = inferNested(typeParameters, parameters, arguments, partialSubstitutor, (PsiCall)parent, policy, properties, session); + if (childSubstitutor != null) return childSubstitutor; } else if (topLevelCall instanceof PsiMethodCallExpression) { return new InferenceSession(typeParameters, partialSubstitutor, parent.getManager(), parent, policy).prepareSubstitution(); @@ -141,7 +134,80 @@ public class InferenceSessionContainer { inferenceSession.initExpressionConstraints(parameters, arguments, parent); return inferenceSession.infer(parameters, arguments, parent); } - + + private static PsiSubstitutor inferNested(final PsiTypeParameter[] typeParameters, + @NotNull final PsiParameter[] parameters, + @NotNull final PsiExpression[] arguments, + final PsiSubstitutor partialSubstitutor, + @NotNull final PsiCall parent, + @NotNull final ParameterTypeInferencePolicy policy, + final MethodCandidateInfo.CurrentCandidateProperties properties, + final InferenceSession parentSession) { + final CompoundInitialState compoundInitialState = createState(parentSession); + InitialInferenceState initialInferenceState = compoundInitialState.getInitialState(parent); + if (initialInferenceState != null) { + final InferenceSession childSession = new InferenceSession(initialInferenceState); + final List errorMessages = parentSession.getIncompatibleErrorMessages(); + if (errorMessages != null) { + return childSession.prepareSubstitution(); + } + return childSession.collectAdditionalAndInfer(parameters, arguments, properties, compoundInitialState.getInitialSubstitutor()); + } + + //we do not investigate lambda return expressions when lambda's return type is already inferred (proper) + //this way all calls from lambda's return expressions won't appear in nested sessions + else { + PsiElement gParent = PsiUtil.skipParenthesizedExprUp(parent.getParent()); + //find the nearest parent which appears in the map and start inference with a provided target type for a nested lambda + while (true) { + if (gParent instanceof PsiReturnStatement) { //process code block lambda + final PsiElement returnContainer = gParent.getParent(); + if (returnContainer instanceof PsiCodeBlock) { + gParent = returnContainer.getParent(); + } + } + if (gParent instanceof PsiLambdaExpression) { + final PsiCall call = PsiTreeUtil.getParentOfType(gParent, PsiCall.class); + if (call != null) { + initialInferenceState = compoundInitialState.getInitialState(call); + if (initialInferenceState != null) { + final int idx = LambdaUtil.getLambdaIdx(call.getArgumentList(), gParent); + final PsiMethod method = call.resolveMethod(); + if (method != null && idx > -1) { + final PsiType parameterType = PsiTypesUtil.getParameterType(method.getParameterList().getParameters(), idx, true); + final PsiType parameterTypeInTermsOfSession = initialInferenceState.getInferenceSubstitutor().substitute(parameterType); + final PsiType lambdaTargetType = compoundInitialState.getInitialSubstitutor().substitute(parameterTypeInTermsOfSession); + return LambdaUtil.performWithLambdaTargetType((PsiLambdaExpression)gParent, lambdaTargetType, new Producer() { + @Nullable + @Override + public PsiSubstitutor produce() { + if (call.equals(PsiTreeUtil.getParentOfType(parent, PsiCall.class, true))) { + //parent was mentioned in the top inference session + //just proceed with the target type + final InferenceSession inferenceSession = new InferenceSession(typeParameters, partialSubstitutor, parent.getManager(), parent, policy); + inferenceSession.initExpressionConstraints(parameters, arguments, parent); + return inferenceSession.infer(parameters, arguments, parent); + } + //one of the grand parents were found in the top inference session + //start from it as it is the top level call + final InferenceSession sessionInsideLambda = startTopLevelInference(call, policy); + return inferNested(typeParameters, parameters, arguments, partialSubstitutor, parent, policy, properties, sessionInsideLambda); + } + }); + } + } + else { + gParent = PsiUtil.skipParenthesizedExprUp(call.getParent()); + continue; + } + } + } + break; + } + } + return null; + } + private static CompoundInitialState createState(InferenceSession topLevelSession) { final PsiSubstitutor topInferenceSubstitutor = replaceVariables(topLevelSession.getInferenceVariables()); final Map nestedStates = new LinkedHashMap(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/additionalConstraints/DeepLambdaReturnExpressionsWithProperTargetType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/additionalConstraints/DeepLambdaReturnExpressionsWithProperTargetType.java new file mode 100644 index 000000000000..f6ccbee6a259 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/additionalConstraints/DeepLambdaReturnExpressionsWithProperTargetType.java @@ -0,0 +1,24 @@ + +import java.util.Optional; +import java.util.function.UnaryOperator; + +class Test { + + private void example() { + update(x -> x.flatMap(y -> Optional.empty())); + update(x -> x.flatMap(y -> x.flatMap(z -> Optional.empty()))); + update(x -> x.flatMap(y -> x.flatMap(z -> x.flatMap(w -> Optional.empty())))); + + update(x -> { + return x.flatMap(y -> { + return x.flatMap(z -> { + return x.flatMap(w -> { + return Optional.empty(); + }); + }); + }); + }); + } + + void update(UnaryOperator> u) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistency.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistency.java index 5d81e850e679..76cddf5e33e4 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistency.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistency.java @@ -33,7 +33,7 @@ class TypeArgsConsistency1 { I i1 = (i, j) -> i + j; foo((i, j) -> i + j); I i2 =bar((i, j) -> i) ; - I i3 = bar((i, j) -> "" + i + j); + I i3 = bar((i, j) -> "" + i + j); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistencyMisc1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistencyMisc1.java index 14facbd197a5..a27b804feafe 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistencyMisc1.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistencyMisc1.java @@ -66,9 +66,9 @@ class Test2 { { bar(x -> x); bar1(x -> x); - bar2(1, x -> x); - bar2("", x -> x); - bar3(x -> x, ""); + bar2(1, x -> x); + bar2("", x -> x); + bar3(x -> x, ""); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/HighlightFaultyLambdaReturnExpression.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/HighlightFaultyLambdaReturnExpression.java index fdd1faa3c54a..06c5c61c90d7 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/HighlightFaultyLambdaReturnExpression.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/HighlightFaultyLambdaReturnExpression.java @@ -1,4 +1,5 @@ - +import java.util.Collections; +import java.util.List; import java.util.function.Supplier; class Test { @@ -12,4 +13,14 @@ class Test { } public static void foo(Supplier consumer) {} + + private void foo(List descriptions) { + Collections.sort(descriptions, (o1, o2) -> { + final int elementsDiff = o1.length() - o2.length(); + if (elementsDiff == 0) { + return o1.equals(o2); + } + return -elementsDiff; + }); + } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/LiftedIntersectionType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/LiftedIntersectionType.java index e53f227b1950..f7d4fdd37967 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/LiftedIntersectionType.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/LiftedIntersectionType.java @@ -13,7 +13,7 @@ class Test { } void bar(C c) { - foo(c, x -> x.f()); + foo(c, x -> x.f()); foo(c, x -> x); } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashN.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashN.java index 0de2f83afa6a..065d55f3b63f 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashN.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashN.java @@ -1,5 +1,5 @@ // "Break string on '\n'" "false" class A { - String s = "Hello!\n"; + String s = "Hello!\n"; } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashNSlashR.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashNSlashR.java index c18a560abff9..220a365a43a6 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashNSlashR.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashNSlashR.java @@ -1,5 +1,5 @@ // "Break string on '\n'" "false" class A { - String s = "Hello!\n\r"; + String s = "Hello!\n\r"; } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambda2methodReference/beforeVarargsCallMoreArgs.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambda2methodReference/beforeVarargsCallMoreArgs.java index 1d1261adfdb2..2e9edf0cdcee 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambda2methodReference/beforeVarargsCallMoreArgs.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambda2methodReference/beforeVarargsCallMoreArgs.java @@ -7,6 +7,6 @@ class Example { } { - Function r = (s) -> m(s, s); + Function r = (s) -> m(s, s); } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before7.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before7.java index da98e4655f5b..a9d8079d459d 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before7.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before7.java @@ -12,7 +12,7 @@ public class Test { } @SuppressWarnings("unchecked") - void foo() { + void foo() { foo(new ArrayList()).addAll(foo1(new ArrayList); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchArrayAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchArrayAssignment.java new file mode 100644 index 000000000000..6364503ce775 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchArrayAssignment.java @@ -0,0 +1,12 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + public void testAssignment(List data) { + String[] found = {"no"}; + if (data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty())) { + found[0] = "yes"; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchUnreachableReturn.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchUnreachableReturn.java new file mode 100644 index 000000000000..93a1eb0c5706 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchUnreachableReturn.java @@ -0,0 +1,13 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + boolean find(List data) { + if(data != null) { + return data.stream().map(String::trim).anyMatch(trimmed -> trimmed.startsWith("xyz")); + } else { + throw new IllegalArgumentException(); + } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstReAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstReAssignment.java new file mode 100644 index 000000000000..4851693aa34b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstReAssignment.java @@ -0,0 +1,20 @@ +// "Replace with findFirst()" "true" + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class Main { + private int getInitialSize() {return 0;} + + public void testMap(Map> map) throws Exception { + int firstSize = 10; + + System.out.println(firstSize); + + // loop + // comment + firstSize = map.values().stream().filter(Objects::nonNull).findFirst().map(List::size).orElse(getInitialSize()); + System.out.println(firstSize); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstReturnUnreachable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstReturnUnreachable.java new file mode 100644 index 000000000000..45ebed51b091 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstReturnUnreachable.java @@ -0,0 +1,15 @@ +// "Replace with findFirst()" "true" + +import java.util.Collection; +import java.util.List; + +public class Main { + public static String find(List> list) { + if(list == null) { + System.out.println("oops"); + return ""; + } else { + return list.stream().flatMap(Collection::stream).filter(string -> string.startsWith("ABC")).findFirst().orElse(null); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchArrayAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchArrayAssignment.java new file mode 100644 index 000000000000..1fe81627f086 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchArrayAssignment.java @@ -0,0 +1,16 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + public void testAssignment(List data) { + String[] found = {"no"}; + for(String str : data) { + String trimmed = str.trim(); + if(!trimmed.isEmpty()) { + found[0] = "yes"; + break; + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchUnreachableReturn.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchUnreachableReturn.java new file mode 100644 index 000000000000..2fa660e42389 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchUnreachableReturn.java @@ -0,0 +1,19 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + boolean find(List data) { + if(data != null) { + for (String e : data) { + String trimmed = e.trim(); + if (trimmed.startsWith("xyz")) { + return true; + } + } + } else { + throw new IllegalArgumentException(); + } + return false; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstReAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstReAssignment.java new file mode 100644 index 000000000000..d1cd5f35eff2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstReAssignment.java @@ -0,0 +1,25 @@ +// "Replace with findFirst()" "true" + +import java.util.List; +import java.util.Map; + +public class Main { + private int getInitialSize() {return 0;} + + public void testMap(Map> map) throws Exception { + int firstSize = 10; + + System.out.println(firstSize); + + firstSize = getInitialSize(); + // loop + for(List list : map.values()) { + if(list != null) { + firstSize = list.size(); + // comment + break; + } + } + System.out.println(firstSize); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstReturnUnreachable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstReturnUnreachable.java new file mode 100644 index 000000000000..a47317fbd733 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstReturnUnreachable.java @@ -0,0 +1,21 @@ +// "Replace with findFirst()" "true" + +import java.util.List; + +public class Main { + public static String find(List> list) { + if(list == null) { + System.out.println("oops"); + return ""; + } else { + for (List innerList : list) { + for (String string : innerList) { + if (string.startsWith("ABC")) { + return string; + } + } + } + } + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressLocalInspection/before6.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressLocalInspection/before6.java index 36fd61a74e52..8f54bf7ff7a1 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressLocalInspection/before6.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressLocalInspection/before6.java @@ -1,5 +1,13 @@ // "Suppress for field" "false" /** @noinspection ALL*/ class a { - static private String mm = "00"; + static private String mm = "00"; + // The "Convert to local" inspection should be reported here if not suppressed + + static void test() { + mm = "1"; + if(mm == "1") { + mm = "2"; + } + } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before10.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before10.java index c263cb5fcae7..f8b32ba0108d 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before10.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before10.java @@ -4,6 +4,6 @@ public class Test { { int i = 0; //noinspection SillyAssignment my very long comment - i = i; + i = i; } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before8.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before8.java index 0897593fad70..4532b8121308 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before8.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before8.java @@ -1,6 +1,6 @@ // "Suppress for method" "false" /** @noinspection UNUSED_SYMBOL*/ class a { -private void run() { +private void run() { } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapObjectWithOptional/beforeInvalidTypeParameter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapObjectWithOptional/beforeInvalidTypeParameter.java index 15519b887d03..4e5ee1757301 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapObjectWithOptional/beforeInvalidTypeParameter.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapObjectWithOptional/beforeInvalidTypeParameter.java @@ -4,7 +4,7 @@ import java.util.Optional; public class Test { void m(String ss) { - f(ss); + f(ss); } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/BreakContinueViaFinally.java b/java/java-tests/testData/inspection/dataFlow/fixture/BreakContinueViaFinally.java new file mode 100644 index 000000000000..4fa7915e56b1 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/BreakContinueViaFinally.java @@ -0,0 +1,31 @@ +class Test { + public void testContinue() { + Object o = null; + for (int i = 0; i < 4; i++) { + try { + if (o == null) { + System.out.println("hello"); + continue; + } + System.out.println("fred"); + } finally { + o = ""; + } + } + } + + public void testBreak() { + Object o = null; + while (true) { + try { + System.out.println("hello"); + break; + } finally { + o = ""; + } + } + if (o != null) { + System.out.println("fred"); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambda.java b/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambda.java index 62a5fb1f6d4f..3f34513e4cab 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambda.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambda.java @@ -1,4 +1,4 @@ -// "Replace Stream.allMatch(x -> !(...)) with Stream.noneMatch(...)" "true" +// "Replace Stream.allMatch(x -> !(...)) with noneMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambdaTwice.java b/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambdaTwice.java index 1b682b22e4a0..960555bfce88 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambdaTwice.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambdaTwice.java @@ -1,4 +1,4 @@ -// "Replace !Stream.allMatch(x -> !(...)) with Stream.anyMatch(...)" "true" +// "Replace !Stream.allMatch(x -> !(...)) with anyMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedPrimitive.java b/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedPrimitive.java new file mode 100644 index 000000000000..9e50a197bf65 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedPrimitive.java @@ -0,0 +1,10 @@ +// "Replace DoubleStream.allMatch(x -> !(...)) with noneMatch(...)" "true" + +import java.util.stream.*; + +class Test { + public boolean testAllMatch(double[] data) { + if(DoubleStream.of(data).noneMatch(d -> Double.isNaN(d))) + return true; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegated.java b/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegated.java index 5d8217290911..fc0d0a971116 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegated.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegated.java @@ -1,4 +1,4 @@ -// "Replace !Stream.anyMatch(...) with Stream.noneMatch(...)" "true" +// "Replace !Stream.anyMatch(...) with noneMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedLambdaTwice.java b/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedLambdaTwice.java index d51ced9ee88f..fe678b9ee0ff 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedLambdaTwice.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedLambdaTwice.java @@ -1,4 +1,4 @@ -// "Replace !Stream.anyMatch(x -> !(...)) with Stream.allMatch(...)" "true" +// "Replace !Stream.anyMatch(x -> !(...)) with allMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedPrimitive.java b/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedPrimitive.java new file mode 100644 index 000000000000..c488190c82b3 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedPrimitive.java @@ -0,0 +1,10 @@ +// "Replace !LongStream.anyMatch(...) with noneMatch(...)" "true" + +import java.util.stream.*; + +class Test { + public boolean testAnyMatch(long[] data) { + if(LongStream.of(data).noneMatch(i -> i > 0)) + return true; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegated.java b/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegated.java index 51b4c0b74601..f2c92cf99890 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegated.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegated.java @@ -1,4 +1,4 @@ -// "Replace !Stream.noneMatch(...) with Stream.anyMatch(...)" "true" +// "Replace !Stream.noneMatch(...) with anyMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedLambda.java b/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedLambda.java index e11a37fed210..b0e0724163e4 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedLambda.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedLambda.java @@ -1,4 +1,4 @@ -// "Replace Stream.noneMatch(x -> !(...)) with Stream.allMatch(...)" "true" +// "Replace Stream.noneMatch(x -> !(...)) with allMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedPrimitive.java b/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedPrimitive.java new file mode 100644 index 000000000000..b1b22f847632 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedPrimitive.java @@ -0,0 +1,10 @@ +// "Replace !IntStream.noneMatch(...) with anyMatch(...)" "true" + +import java.util.stream.*; + +class Test { + public boolean testNoneMatch(int[] data) { + if(IntStream.of(data).anyMatch(i -> i > 0)) + return true; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegated.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegated.java index df19ee9bf6fa..a347338761d7 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegated.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegated.java @@ -1,4 +1,4 @@ -// "Replace Stream.allMatch(x -> !(...)) with Stream.noneMatch(...)" "false" +// "Replace Stream.allMatch(x -> !(...)) with noneMatch(...)" "false" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambda.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambda.java index d6c2fe534a35..0b037b427bc5 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambda.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambda.java @@ -1,4 +1,4 @@ -// "Replace Stream.allMatch(x -> !(...)) with Stream.noneMatch(...)" "true" +// "Replace Stream.allMatch(x -> !(...)) with noneMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambdaTwice.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambdaTwice.java index 7a6514bd79c7..82c4327b9723 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambdaTwice.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambdaTwice.java @@ -1,4 +1,4 @@ -// "Replace !Stream.allMatch(x -> !(...)) with Stream.anyMatch(...)" "true" +// "Replace !Stream.allMatch(x -> !(...)) with anyMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedPrimitive.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedPrimitive.java new file mode 100644 index 000000000000..df7b88d3ec00 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedPrimitive.java @@ -0,0 +1,10 @@ +// "Replace DoubleStream.allMatch(x -> !(...)) with noneMatch(...)" "true" + +import java.util.stream.*; + +class Test { + public boolean testAllMatch(double[] data) { + if(DoubleStream.of(data).allMatch(d -> !Double.isNaN(d))) + return true; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegated.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegated.java index 204e69b28f30..25862a73e816 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegated.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegated.java @@ -1,4 +1,4 @@ -// "Replace !Stream.anyMatch(...) with Stream.noneMatch(...)" "true" +// "Replace !Stream.anyMatch(...) with noneMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambda.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambda.java index 32ce1a0d7618..578e2993cdf4 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambda.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambda.java @@ -1,4 +1,4 @@ -// "Replace !Stream.anyMatch(x -> !(...)) with Stream.allMatch(...)" "false" +// "Replace !Stream.anyMatch(x -> !(...)) with allMatch(...)" "false" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambdaTwice.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambdaTwice.java index b2dfcd99b7ec..19c55ab6cf3a 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambdaTwice.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambdaTwice.java @@ -1,4 +1,4 @@ -// "Replace !Stream.anyMatch(x -> !(...)) with Stream.allMatch(...)" "true" +// "Replace !Stream.anyMatch(x -> !(...)) with allMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedPrimitive.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedPrimitive.java new file mode 100644 index 000000000000..69a1848bd26d --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedPrimitive.java @@ -0,0 +1,10 @@ +// "Replace !LongStream.anyMatch(...) with noneMatch(...)" "true" + +import java.util.stream.*; + +class Test { + public boolean testAnyMatch(long[] data) { + if(!LongStream.of(data).anyMatch(i -> i > 0)) + return true; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegated.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegated.java index 2b01f91af7ac..f4133a02a7eb 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegated.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegated.java @@ -1,4 +1,4 @@ -// "Replace !Stream.noneMatch(...) with Stream.anyMatch(...)" "true" +// "Replace !Stream.noneMatch(...) with anyMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedLambda.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedLambda.java index a37b825badfe..b6de9b267794 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedLambda.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedLambda.java @@ -1,4 +1,4 @@ -// "Replace Stream.noneMatch(x -> !(...)) with Stream.allMatch(...)" "true" +// "Replace Stream.noneMatch(x -> !(...)) with allMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedPrimitive.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedPrimitive.java new file mode 100644 index 000000000000..6791ed98ec2a --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedPrimitive.java @@ -0,0 +1,10 @@ +// "Replace !IntStream.noneMatch(...) with anyMatch(...)" "true" + +import java.util.stream.*; + +class Test { + public boolean testNoneMatch(int[] data) { + if(!IntStream.of(data).noneMatch(i -> i > 0)) + return true; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructorNoDuplicates.java b/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructorNoDuplicates.java index 69d4839095d8..c435409bbb78 100644 --- a/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructorNoDuplicates.java +++ b/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructorNoDuplicates.java @@ -5,7 +5,7 @@ class A { void m() { - new StringTokenizer("asd", "\\\t\nqwerty!#2@$") + new StringTokenizer("asd", "\\\t\nqwerty!#2@$") } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt index e49fb2e6f845..bba79b121692 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt @@ -633,6 +633,22 @@ class Test { onLineStartingWith("draw").assertInlays("x->10", "y->20") onLineStartingWith("drawRect").assertInlays("w->10", "h->12") } + + fun `test do not show for setters`() { + setup(""" +class Test { + + void main() { + set(10); + } + + void set(int newValue) {} + +} +""") + + onLineStartingWith("set(").assertNoInlays() + } fun `test show for method with boolean param and return value`() { setup(""" diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewInferenceCollectingAdditionalConstraintsTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewInferenceCollectingAdditionalConstraintsTest.java index 3cfeaa32fc5e..22c993a3dee6 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewInferenceCollectingAdditionalConstraintsTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewInferenceCollectingAdditionalConstraintsTest.java @@ -31,12 +31,12 @@ public class NewInferenceCollectingAdditionalConstraintsTest extends LightDaemon doTest(); } - private void doTest() { - doTest(true); + public void testDeepLambdaReturnExpressionsWithProperTargetType() throws Exception { + doTest(); } - private void doTest(boolean warnings) { + private void doTest() { IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable()); - doTest(BASE_PATH + "/" + getTestName(false) + ".java", warnings, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", true, false); } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/SuppressLocalInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/SuppressLocalInspectionTest.java index 8a992b117ff3..2e6c19f0a67f 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/SuppressLocalInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/SuppressLocalInspectionTest.java @@ -1,8 +1,23 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.intellij.codeInsight.daemon.quickFix; import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.localCanBeFinal.LocalCanBeFinal; -import com.intellij.openapi.roots.LanguageLevelProjectExtension; +import com.intellij.codeInspection.varScopeCanBeNarrowed.FieldCanBeLocalInspection; import com.intellij.pom.java.LanguageLevel; import org.jetbrains.annotations.NotNull; @@ -15,7 +30,7 @@ public class SuppressLocalInspectionTest extends LightQuickFixParameterizedTestC @NotNull @Override protected LocalInspectionTool[] configureLocalInspectionTools() { - return new LocalInspectionTool[]{new LocalCanBeFinal()}; + return new LocalInspectionTool[]{new LocalCanBeFinal(), new FieldCanBeLocalInspection()}; } public void test() throws Exception { doAllTests(); } diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index 7128b952ae18..e4ad15969725 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -60,6 +60,7 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { public void testExceptionFromFinallyNesting() throws Throwable { doTest(); } public void testNestedFinally() { doTest(); } public void testTryFinallyInsideFinally() { doTest(); } + public void testBreakContinueViaFinally() { doTest(); } public void testFieldChangedBetweenSynchronizedBlocks() throws Throwable { doTest(); } public void testGeneratedEquals() throws Throwable { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java b/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java index 20da5e4a3194..a631a0eda761 100644 --- a/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java +++ b/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java @@ -29,6 +29,7 @@ import com.intellij.openapi.roots.ex.ProjectRootManagerEx; import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.ex.http.HttpFileSystem; import com.intellij.testFramework.*; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -712,16 +713,20 @@ public class DirectoryIndexTest extends IdeaTestCase { assertIteratedContent(myFileIndex, Arrays.asList(fileSourceRoot, fileTestSourceRoot), Collections.singletonList(fileRoot)); } - private static void assertIteratedContent(ProjectFileIndex fileIndex, + private void assertIteratedContent(Module module, @Nullable List contains, @Nullable List doesntContain) { + assertIteratedContent(ModuleRootManager.getInstance(module).getFileIndex(), contains, doesntContain); + assertIteratedContent(myFileIndex, contains, doesntContain); + } + + private static void assertIteratedContent(FileIndex fileIndex, @Nullable List contains, @Nullable List doesntContain) { final Set collected = new THashSet<>(); - fileIndex.iterateContent(new ContentIterator() { - @Override - public boolean processFile(VirtualFile fileOrDir) { - collected.add(fileOrDir); - return true; + fileIndex.iterateContent(fileOrDir -> { + if (!collected.add(fileOrDir)) { + fail(fileOrDir + " visited twice"); } + return true; }); if (contains != null) assertContainsElements(collected, contains); if (doesntContain != null) assertDoesntContain(collected, doesntContain); @@ -856,10 +861,67 @@ public class DirectoryIndexTest extends IdeaTestCase { assertTrue(myFileIndex.isInSource(fileSourceRoot)); } - public void testContentRootUnderExcludedRoot() { - VirtualFile dirUnderExcluded = createChildDirectory(myExcludeDir, "generated"); - PsiTestUtil.addSourceContentToRoots(myModule2, dirUnderExcluded); - checkInfo(dirUnderExcluded, myModule2, false, false, "", JavaSourceRootType.SOURCE, myModule2, myModule3); + public void testSourceContentRootsUnderExcludedRoot() { + VirtualFile contentRoot = createChildDirectory(myExcludeDir, "content"); + PsiTestUtil.addContentRoot(myModule2, contentRoot); + checkInfo(contentRoot, myModule2, false, false, null, null, myModule2, myModule3); + VirtualFile excludedFile = createChildData(myExcludeDir, "excluded.txt"); + + VirtualFile sourceRoot = createChildDirectory(myExcludeDir, "src"); + VirtualFile sourceFile = createChildData(sourceRoot, "source.txt"); + PsiTestUtil.addSourceRoot(myModule2, sourceRoot); + assertEquals(myModule2Dir, assertInProject(sourceRoot).getContentRoot()); + checkInfo(sourceRoot, myModule2, false, false, "", JavaSourceRootType.SOURCE, myModule2, myModule3); + + VirtualFile contentSourceRoot = createChildDirectory(myExcludeDir, "content-src"); + VirtualFile contentSourceFile = createChildData(sourceRoot, "content-source.txt"); + PsiTestUtil.addSourceContentToRoots(myModule2, contentSourceRoot); + checkInfo(contentSourceRoot, myModule2, false, false, "", JavaSourceRootType.SOURCE, myModule2, myModule3); + + assertIteratedContent(myModule2, Arrays.asList(sourceFile, contentSourceFile, sourceRoot, contentSourceRoot), + Arrays.asList(excludedFile, myExcludeDir)); + } + + public void testSourceContentRootsUnderExcludedRootUnderSourceRoot() { + VirtualFile excluded = createChildDirectory(myModule2Dir, "excluded"); + PsiTestUtil.addExcludedRoot(myModule2, excluded); + VirtualFile excludedFile = createChildData(excluded, "excluded.txt"); + + VirtualFile contentRoot = createChildDirectory(excluded, "content"); + PsiTestUtil.addContentRoot(myModule2, contentRoot); + checkInfo(contentRoot, myModule2, false, false, null, null); + + VirtualFile sourceRoot = createChildDirectory(excluded, "src"); + PsiTestUtil.addSourceRoot(myModule2, sourceRoot); + VirtualFile sourceFile = createChildData(sourceRoot, "source.txt"); + assertEquals(myModule2Dir, assertInProject(sourceRoot).getContentRoot()); + checkInfo(sourceRoot, myModule2, false, false, "", JavaSourceRootType.SOURCE, myModule2, myModule3); + + VirtualFile contentSourceRoot = createChildDirectory(excluded, "content-src"); + VirtualFile contentSourceFile = createChildData(contentSourceRoot, "content-source.txt"); + PsiTestUtil.addSourceContentToRoots(myModule2, contentSourceRoot); + checkInfo(contentSourceRoot, myModule2, false, false, "", JavaSourceRootType.SOURCE, myModule2, myModule3); + + assertIteratedContent(myModule2, Arrays.asList(sourceFile, contentSourceFile, sourceRoot, contentSourceRoot), + Arrays.asList(excludedFile, myExcludeDir)); + } + + public void testExcludedSourceRootUnderExcluded() { + VirtualFile excluded = createChildDirectory(myModule2Dir, "excluded"); + PsiTestUtil.addExcludedRoot(myModule2, excluded); + + VirtualFile src = createChildDirectory(excluded, "src"); + VirtualFile sourceFile = createChildData(src, "src.txt"); + PsiTestUtil.addSourceRoot(myModule2, src); + PsiTestUtil.addExcludedRoot(myModule2, src); + assertExcluded(src, myModule2); + assertIteratedContent(myModule2, null, Arrays.asList(sourceFile)); + } + + public void testSourceRootFromUnsupportedFileSystem() { + VirtualFile httpFile = HttpFileSystem.getInstance().findFileByPath("example.com"); + PsiTestUtil.addSourceRoot(myModule, httpFile); + assertNotInProject(httpFile); } private void checkInfo(VirtualFile file, diff --git a/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/LightQuickFixTestCase.java b/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/LightQuickFixTestCase.java index db33c980ab61..60456ec77ef9 100644 --- a/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/LightQuickFixTestCase.java +++ b/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/LightQuickFixTestCase.java @@ -336,7 +336,7 @@ public abstract class LightQuickFixTestCase extends LightDaemonAnalyzerTestCase @Override public void configureFromFileText(String name, String contents) throws IOException { - LightPlatformCodeInsightTestCase.configureFromFileText(name, contents); + LightPlatformCodeInsightTestCase.configureFromFileText(name, contents, true); } @Override diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java index c1020cadaa01..610290944fbe 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java @@ -67,128 +67,134 @@ public class BuildMain { @Nullable private static PreloadedData ourPreloadedData; - public static void main(String[] args){ - final long processStart = System.currentTimeMillis(); - final String startMessage = "Build process started. Classpath: " + System.getProperty("java.class.path"); - System.out.println(startMessage); - LOG.info(startMessage); - - final String host = args[HOST_ARG]; - final int port = Integer.parseInt(args[PORT_ARG]); - final UUID sessionId = UUID.fromString(args[SESSION_ID_ARG]); - @SuppressWarnings("ConstantConditions") - final File systemDir = new File(FileUtil.toCanonicalPath(args[SYSTEM_DIR_ARG])); - Utils.setSystemRoot(systemDir); + public static void main(String[] args) throws Throwable{ + try { + final long processStart = System.currentTimeMillis(); + final String startMessage = "Build process started. Classpath: " + System.getProperty("java.class.path"); + System.out.println(startMessage); + LOG.info(startMessage); - final long connectStart = System.currentTimeMillis(); - // IDEA-123132, let's try again - for (int attempt = 0; attempt < 3; attempt++) { - try { - ourEventLoopGroup = new NioEventLoopGroup(1, SharedThreadPool.getInstance()); - break; - } - catch (IllegalStateException e) { - if (attempt == 2) { - printErrorAndExit(host, port, e); - return; - } - else { - LOG.warn("Cannot create event loop, attempt #" + attempt, e); - try { - //noinspection BusyWait - Thread.sleep(10 * (attempt + 1)); - } - catch (InterruptedException ignored) { - } - } - } - } + final String host = args[HOST_ARG]; + final int port = Integer.parseInt(args[PORT_ARG]); + final UUID sessionId = UUID.fromString(args[SESSION_ID_ARG]); + @SuppressWarnings("ConstantConditions") + final File systemDir = new File(FileUtil.toCanonicalPath(args[SYSTEM_DIR_ARG])); + Utils.setSystemRoot(systemDir); - final Bootstrap bootstrap = new Bootstrap().group(ourEventLoopGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel channel) throws Exception { - channel.pipeline().addLast(new ProtobufVarint32FrameDecoder(), - new ProtobufDecoder(CmdlineRemoteProto.Message.getDefaultInstance()), - new ProtobufVarint32LengthFieldPrepender(), - new ProtobufEncoder(), - new MyMessageHandler(sessionId)); - } - }).option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.SO_KEEPALIVE, true); - - final ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)).awaitUninterruptibly(); - - - final boolean success = future.isSuccess(); - if (success) { - LOG.info("Connection to IDE established in " + (System.currentTimeMillis() - connectStart) + " ms"); - - final String projectPathToPreload = System.getProperty(PRELOAD_PROJECT_PATH, null); - final String globalsPathToPreload = System.getProperty(PRELOAD_CONFIG_PATH, null); - if (projectPathToPreload != null && globalsPathToPreload != null) { - final PreloadedData data = new PreloadedData(); - ourPreloadedData = data; + final long connectStart = System.currentTimeMillis(); + // IDEA-123132, let's try again + for (int attempt = 0; attempt < 3; attempt++) { try { - FileSystemUtil.getAttributes(projectPathToPreload); // this will pre-load all FS optimizations - - final BuildRunner runner = new BuildRunner(new JpsModelLoaderImpl(projectPathToPreload, globalsPathToPreload, null)); - data.setRunner(runner); - - final File dataStorageRoot = Utils.getDataStorageRoot(projectPathToPreload); - final BuildFSState fsState = new BuildFSState(false); - final ProjectDescriptor pd = runner.load(new MessageHandler() { - @Override - public void processMessage(BuildMessage msg) { - data.addMessage(msg); - } - }, dataStorageRoot, fsState); - data.setProjectDescriptor(pd); - - try { - final File fsStateFile = new File(dataStorageRoot, BuildSession.FS_STATE_FILE); - final DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(fsStateFile))); + ourEventLoopGroup = new NioEventLoopGroup(1, SharedThreadPool.getInstance()); + break; + } + catch (IllegalStateException e) { + if (attempt == 2) { + printErrorAndExit(host, port, e); + return; + } + else { + LOG.warn("Cannot create event loop, attempt #" + attempt, e); try { - final int version = in.readInt(); - if (version == BuildFSState.VERSION) { - final long savedOrdinal = in.readLong(); - final boolean hasWorkToDo = in.readBoolean();// must skip "has-work-to-do" flag - fsState.load(in, pd.getModel(), pd.getBuildRootIndex()); - data.setFsEventOrdinal(savedOrdinal); - data.setHasHasWorkToDo(hasWorkToDo); + //noinspection BusyWait + Thread.sleep(10 * (attempt + 1)); + } + catch (InterruptedException ignored) { + } + } + } + } + + final Bootstrap bootstrap = new Bootstrap().group(ourEventLoopGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel channel) throws Exception { + channel.pipeline().addLast(new ProtobufVarint32FrameDecoder(), + new ProtobufDecoder(CmdlineRemoteProto.Message.getDefaultInstance()), + new ProtobufVarint32LengthFieldPrepender(), + new ProtobufEncoder(), + new MyMessageHandler(sessionId)); + } + }).option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.SO_KEEPALIVE, true); + + final ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)).awaitUninterruptibly(); + + + final boolean success = future.isSuccess(); + if (success) { + LOG.info("Connection to IDE established in " + (System.currentTimeMillis() - connectStart) + " ms"); + + final String projectPathToPreload = System.getProperty(PRELOAD_PROJECT_PATH, null); + final String globalsPathToPreload = System.getProperty(PRELOAD_CONFIG_PATH, null); + if (projectPathToPreload != null && globalsPathToPreload != null) { + final PreloadedData data = new PreloadedData(); + ourPreloadedData = data; + try { + FileSystemUtil.getAttributes(projectPathToPreload); // this will pre-load all FS optimizations + + final BuildRunner runner = new BuildRunner(new JpsModelLoaderImpl(projectPathToPreload, globalsPathToPreload, null)); + data.setRunner(runner); + + final File dataStorageRoot = Utils.getDataStorageRoot(projectPathToPreload); + final BuildFSState fsState = new BuildFSState(false); + final ProjectDescriptor pd = runner.load(new MessageHandler() { + @Override + public void processMessage(BuildMessage msg) { + data.addMessage(msg); + } + }, dataStorageRoot, fsState); + data.setProjectDescriptor(pd); + + try { + final File fsStateFile = new File(dataStorageRoot, BuildSession.FS_STATE_FILE); + final DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(fsStateFile))); + try { + final int version = in.readInt(); + if (version == BuildFSState.VERSION) { + final long savedOrdinal = in.readLong(); + final boolean hasWorkToDo = in.readBoolean();// must skip "has-work-to-do" flag + fsState.load(in, pd.getModel(), pd.getBuildRootIndex()); + data.setFsEventOrdinal(savedOrdinal); + data.setHasHasWorkToDo(hasWorkToDo); + } + } + finally { + in.close(); } } - finally { - in.close(); + catch (FileNotFoundException ignored) { + } + catch (IOException e) { + LOG.info("Error pre-loading FS state", e); + fsState.clearAll(); } - } - catch (FileNotFoundException ignored) { - } - catch (IOException e) { - LOG.info("Error pre-loading FS state", e); - fsState.clearAll(); - } - // preloading target configurations - final BuildTargetsState targetsState = pd.getTargetsState(); - for (BuildTarget target : pd.getBuildTargetIndex().getAllTargets()) { - targetsState.getTargetConfiguration(target); + // preloading target configurations + final BuildTargetsState targetsState = pd.getTargetsState(); + for (BuildTarget target : pd.getBuildTargetIndex().getAllTargets()) { + targetsState.getTargetConfiguration(target); + } + + BuilderRegistry.getInstance(); + + LOG.info("Pre-loaded process ready in " + (System.currentTimeMillis() - processStart) + " ms"); + } + catch (Throwable e) { + LOG.info("Failed to pre-load project " + projectPathToPreload, e); + // just failed to preload the project, the situation will be handled later, when real build starts } - - BuilderRegistry.getInstance(); - - LOG.info("Pre-loaded process ready in " + (System.currentTimeMillis() - processStart) + " ms"); } - catch (Throwable e) { - LOG.info("Failed to pre-load project " + projectPathToPreload, e); - // just failed to preload the project, the situation will be handled later, when real build starts + else if (projectPathToPreload != null || globalsPathToPreload != null){ + LOG.info("Skipping project pre-loading step: both paths to project configuration files and path to global settings must be specified"); } + future.channel().writeAndFlush(CmdlineProtoUtil.toMessage(sessionId, CmdlineProtoUtil.createParamRequest())); } - else if (projectPathToPreload != null || globalsPathToPreload != null){ - LOG.info("Skipping project pre-loading step: both paths to project configuration files and path to global settings must be specified"); + else { + printErrorAndExit(host, port, future.cause()); } - future.channel().writeAndFlush(CmdlineProtoUtil.toMessage(sessionId, CmdlineProtoUtil.createParamRequest())); } - else { - printErrorAndExit(host, port, future.cause()); + catch (Throwable e) { + LOG.error(e); + throw e; } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/indices/impl/ModuleExcludeIndexImpl.java b/jps/jps-builders/src/org/jetbrains/jps/indices/impl/ModuleExcludeIndexImpl.java index bfb234ce59e4..ae17f7860549 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/indices/impl/ModuleExcludeIndexImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/indices/impl/ModuleExcludeIndexImpl.java @@ -26,6 +26,7 @@ import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.java.JpsJavaModuleExtension; import org.jetbrains.jps.model.java.JpsJavaProjectExtension; import org.jetbrains.jps.model.module.JpsModule; +import org.jetbrains.jps.model.module.JpsModuleSourceRoot; import org.jetbrains.jps.util.JpsPathUtil; import java.io.File; @@ -67,6 +68,11 @@ public class ModuleExcludeIndexImpl implements ModuleExcludeIndex { moduleContent.add(contentRoot); contentToModule.put(contentRoot, module); } + for (JpsModuleSourceRoot root : module.getSourceRoots()) { + File sourceRoot = root.getFile(); + moduleContent.add(sourceRoot); + contentToModule.put(sourceRoot, module); + } myModuleToExcludesMap.put(module, moduleExcludes); myModuleToContentMap.put(module, moduleContent); myExcludedRoots.addAll(moduleExcludes); diff --git a/jps/jps-builders/testData/output/sourceRootUnderExcluded/exc/src/B.java b/jps/jps-builders/testData/output/sourceRootUnderExcluded/exc/src/B.java new file mode 100644 index 000000000000..ef5f49869300 --- /dev/null +++ b/jps/jps-builders/testData/output/sourceRootUnderExcluded/exc/src/B.java @@ -0,0 +1,3 @@ +class B { + A a; +} \ No newline at end of file diff --git a/jps/jps-builders/testData/output/sourceRootUnderExcluded/sourceRootUnderExcluded.iml b/jps/jps-builders/testData/output/sourceRootUnderExcluded/sourceRootUnderExcluded.iml new file mode 100644 index 000000000000..5752f113727d --- /dev/null +++ b/jps/jps-builders/testData/output/sourceRootUnderExcluded/sourceRootUnderExcluded.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/jps/jps-builders/testData/output/sourceRootUnderExcluded/sourceRootUnderExcluded.ipr b/jps/jps-builders/testData/output/sourceRootUnderExcluded/sourceRootUnderExcluded.ipr new file mode 100644 index 000000000000..8d3cc788cfde --- /dev/null +++ b/jps/jps-builders/testData/output/sourceRootUnderExcluded/sourceRootUnderExcluded.ipr @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/jps/jps-builders/testData/output/sourceRootUnderExcluded/src/A.java b/jps/jps-builders/testData/output/sourceRootUnderExcluded/src/A.java new file mode 100644 index 000000000000..398760c55b90 --- /dev/null +++ b/jps/jps-builders/testData/output/sourceRootUnderExcluded/src/A.java @@ -0,0 +1,3 @@ +class A { + B b; +} \ No newline at end of file diff --git a/jps/jps-builders/testSrc/org/jetbrains/jps/builders/rebuild/ModuleRebuildTest.kt b/jps/jps-builders/testSrc/org/jetbrains/jps/builders/rebuild/ModuleRebuildTest.kt index 078a823fe18a..9b0658807a05 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/jps/builders/rebuild/ModuleRebuildTest.kt +++ b/jps/jps-builders/testSrc/org/jetbrains/jps/builders/rebuild/ModuleRebuildTest.kt @@ -69,6 +69,17 @@ class ModuleRebuildTest: JpsRebuildTestCase() { }) } + fun testSourceRootUnderExcluded() { + doTest("sourceRootUnderExcluded/sourceRootUnderExcluded.ipr", fs { + dir("production") { + dir("sourceRootUnderExcluded") { + file("A.class") + file("B.class") + } + } + }) + } + fun testResourceCopying() { doTest("resourceCopying/resourceCopying.ipr", fs { dir("production") { diff --git a/jps/jps-builders/testSrc/org/jetbrains/jps/indices/ModuleExcludeIndexTest.java b/jps/jps-builders/testSrc/org/jetbrains/jps/indices/ModuleExcludeIndexTest.java index a7858910f3a5..abf828ad8195 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/jps/indices/ModuleExcludeIndexTest.java +++ b/jps/jps-builders/testSrc/org/jetbrains/jps/indices/ModuleExcludeIndexTest.java @@ -19,6 +19,7 @@ import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.indices.impl.ModuleExcludeIndexImpl; import org.jetbrains.jps.model.JpsJavaModelTestCase; +import org.jetbrains.jps.model.java.JavaSourceRootType; import org.jetbrains.jps.model.java.JpsJavaModuleExtension; import org.jetbrains.jps.model.module.JpsModule; import org.jetbrains.jps.util.JpsPathUtil; @@ -138,6 +139,24 @@ public class ModuleExcludeIndexTest extends JpsJavaModelTestCase { assertFalse(index.isExcludedFromModule(innerRoot, inner)); } + public void testSourceRootUnderExcluded() { + File project = new File(myRoot, "project"); + File exc = new File(project, "exc"); + File src = new File(exc, "src"); + JpsModule module = addModule(); + addContentRoot(module, project); + addExcludedRoot(module, exc); + addSourceRoot(module, src); + assertNotExcluded(src); + + addExcludedRoot(module, src); + assertExcluded(src); + } + + private static void addSourceRoot(JpsModule module, File src) { + module.addSourceRoot(JpsPathUtil.pathToUrl(src.getAbsolutePath()), JavaSourceRootType.SOURCE); + } + private static void addExcludedRoot(JpsModule module, File root) { module.getExcludeRootsList().addUrl(JpsPathUtil.pathToUrl(root.getAbsolutePath())); } diff --git a/native/WinLauncher/WinLauncher/WinLauncher.vcxproj b/native/WinLauncher/WinLauncher/WinLauncher.vcxproj index 45f32292dcca..d4ed96a539b6 100644 --- a/native/WinLauncher/WinLauncher/WinLauncher.vcxproj +++ b/native/WinLauncher/WinLauncher/WinLauncher.vcxproj @@ -65,7 +65,6 @@ true $(JdkPath)\include;$(JdkPath)\include\win32;$(IncludePath) - ..\..\..\bin\WinLauncher true @@ -80,7 +79,6 @@ false $(JdkPath)\include;$(JdkPath)\include\win32;$(IncludePath) $(ProjectName)64 - ..\..\..\bin\WinLauncher diff --git a/platform/core-api/src/com/intellij/lexer/MergingLexerAdapterBase.java b/platform/core-api/src/com/intellij/lexer/MergingLexerAdapterBase.java index 135ace049cf5..b8a5c3d5f056 100644 --- a/platform/core-api/src/com/intellij/lexer/MergingLexerAdapterBase.java +++ b/platform/core-api/src/com/intellij/lexer/MergingLexerAdapterBase.java @@ -95,6 +95,11 @@ public abstract class MergingLexerAdapterBase extends DelegateLexer { myState = pos.getOldState(); } + @Override + public String toString() { + return getClass().getName() + "[" + getDelegate() + "]"; + } + @NotNull @Override public LexerPosition getCurrentPosition() { diff --git a/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java b/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java index 2eccc8d703ed..936ee405660f 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java @@ -129,7 +129,7 @@ public class ExternalDiffToolUtil { byte[] bytes = contentData.getBytes(charset); - byte[] bom = hasBom ? CharsetToolkit.getBom(charset) : null; + byte[] bom = hasBom ? CharsetToolkit.getPossibleBom(charset) : null; if (bom != null) { bytes = ArrayUtil.mergeArrays(bom, bytes); } diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/search/BulkOccurrenceProcessor.java b/platform/indexing-impl/src/com/intellij/psi/impl/search/BulkOccurrenceProcessor.java index 2f7b36b9c2f7..dd346b2b5295 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/search/BulkOccurrenceProcessor.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/search/BulkOccurrenceProcessor.java @@ -21,5 +21,5 @@ import org.jetbrains.annotations.NotNull; @FunctionalInterface interface BulkOccurrenceProcessor { - boolean execute(@NotNull PsiElement scope, int[] offsetsInScope, @NotNull StringSearcher searcher); + boolean execute(@NotNull PsiElement scope, @NotNull int[] offsetsInScope, @NotNull StringSearcher searcher); } diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java b/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java index 3be5d14d704f..a402e800b0e8 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java @@ -721,7 +721,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { final RequestResultProcessor wrapped = singleRequest.processor; return new BulkOccurrenceProcessor() { @Override - public boolean execute(@NotNull PsiElement scope, int[] offsetsInScope, @NotNull StringSearcher searcher) { + public boolean execute(@NotNull PsiElement scope, @NotNull int[] offsetsInScope, @NotNull StringSearcher searcher) { try { if (wrapped instanceof RequestResultProcessor.BulkResultProcessor) { return ((RequestResultProcessor.BulkResultProcessor)wrapped).processTextOccurrences(scope, offsetsInScope, consumer); diff --git a/platform/lang-api/src/com/intellij/framework/detection/FacetBasedFrameworkDetector.java b/platform/lang-api/src/com/intellij/framework/detection/FacetBasedFrameworkDetector.java index 0a2db890febe..3b0768415a0a 100644 --- a/platform/lang-api/src/com/intellij/framework/detection/FacetBasedFrameworkDetector.java +++ b/platform/lang-api/src/com/intellij/framework/detection/FacetBasedFrameworkDetector.java @@ -17,6 +17,7 @@ package com.intellij.framework.detection; import com.intellij.facet.*; import com.intellij.framework.FrameworkType; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; @@ -36,6 +37,8 @@ import java.util.Set; * @author nik */ public abstract class FacetBasedFrameworkDetector extends FrameworkDetector { + private static final Logger LOG = Logger.getInstance(FacetBasedFrameworkDetector.class); + protected FacetBasedFrameworkDetector(String detectorId) { super(detectorId); } @@ -44,6 +47,7 @@ public abstract class FacetBasedFrameworkDetector getFacetType(); /** @@ -83,7 +87,10 @@ public abstract class FacetBasedFrameworkDetector type = getFacetType(); + //noinspection ConstantConditions todo[nik] remove later: this is added to find implementations which incorrectly return 'null' from 'getFacetType' + LOG.assertTrue(type != null, "'getFacetType' returns 'null' in " + getClass()); + return createFrameworkType(type); } static FrameworkType createFrameworkType(final FacetType facetType) { @@ -104,7 +111,7 @@ public abstract class FacetBasedFrameworkDetector myFacetType; private final Icon myIcon; - public FacetBasedFrameworkType(FacetType facetType) { + public FacetBasedFrameworkType(@NotNull FacetType facetType) { super(facetType.getStringId()); myFacetType = facetType; final Icon icon = myFacetType.getIcon(); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java b/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java index 0a4e9104df61..f3d465ab3032 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java @@ -31,6 +31,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiUtilCore; @@ -97,7 +98,11 @@ public class CleanupInspectionIntention implements IntentionAction, HighPriority Collections.sort(descriptions, (o1, o2) -> { final ProblemDescriptorBase d1 = (ProblemDescriptorBase)o1; final ProblemDescriptorBase d2 = (ProblemDescriptorBase)o2; - return -PsiUtilCore.compareElementsByPosition(d1.getPsiElement(), d2.getPsiElement()); + final int elementsDiff = PsiUtilCore.compareElementsByPosition(d1.getPsiElement(), d2.getPsiElement()); + if (elementsDiff == 0) { + return Comparing.compare(d1.getDescriptionTemplate(), d2.getDescriptionTemplate()); + } + return -elementsDiff; }); final SequentialModalProgressTask progressTask = diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootActionBase.java b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootActionBase.java index 0c5d26fdaf15..422bc7ba2af1 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootActionBase.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootActionBase.java @@ -105,7 +105,7 @@ public abstract class MarkRootActionBase extends DumbAwareAction { @Override public void update(AnActionEvent e) { RootsSelection selection = getSelection(e); - doUpdate(e, e.getData(LangDataKeys.MODULE), selection); + doUpdate(e, selection.myModule, selection); } protected void doUpdate(@NotNull AnActionEvent e, @Nullable Module module, @NotNull RootsSelection selection) { @@ -121,21 +121,16 @@ public abstract class MarkRootActionBase extends DumbAwareAction { Module module = getModule(e, files); if (module == null) return RootsSelection.EMPTY; - RootsSelection selection = new RootsSelection(); + RootsSelection selection = new RootsSelection(module); final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(module.getProject()).getFileIndex(); for (VirtualFile file : files) { if (!file.isDirectory()) { return RootsSelection.EMPTY; } - if (!fileIndex.isInContent(file)) { - ExcludeFolder excludeFolder = ProjectRootsUtil.findExcludeFolder(module, file); - if (excludeFolder != null) { - selection.mySelectedExcludeRoots.add(excludeFolder); - continue; - } - else { - return RootsSelection.EMPTY; - } + ExcludeFolder excludeFolder = ProjectRootsUtil.findExcludeFolder(module, file); + if (excludeFolder != null) { + selection.mySelectedExcludeRoots.add(excludeFolder); + continue; } SourceFolder folder = ProjectRootsUtil.findSourceFolder(module, file); if (folder != null) { @@ -180,7 +175,12 @@ public abstract class MarkRootActionBase extends DumbAwareAction { } public static class RootsSelection { - public static final RootsSelection EMPTY = new RootsSelection(); + public static final RootsSelection EMPTY = new RootsSelection(null); + public final Module myModule; + + public RootsSelection(Module module) { + myModule = module; + } public List mySelectedRoots = new ArrayList<>(); public List mySelectedExcludeRoots = new ArrayList<>(); diff --git a/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java b/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java index 0376ef6b2a48..126765c510a6 100644 --- a/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java +++ b/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java @@ -33,6 +33,7 @@ import com.intellij.psi.PsiManager; import com.intellij.ui.UIBundle; import com.intellij.ui.awt.RelativePoint; import com.intellij.util.Consumer; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -47,12 +48,7 @@ public class TogglePopupHintsPanel extends EditorBasedWidget implements StatusBa public TogglePopupHintsPanel(@NotNull final Project project) { super(project); myCurrentIcon = AllIcons.Ide.HectorNo; - myConnection.subscribe(PowerSaveMode.TOPIC, new PowerSaveMode.Listener() { - @Override - public void powerSaveStateChanged() { - updateStatus(); - } - }); + myConnection.subscribe(PowerSaveMode.TOPIC, this::updateStatus); } @Override @@ -115,10 +111,11 @@ public class TogglePopupHintsPanel extends EditorBasedWidget implements StatusBa } public void updateStatus() { - updateStatus(getCurrentFile()); + UIUtil.invokeLaterIfNeeded(() -> updateStatus(getCurrentFile())); } private void updateStatus(PsiFile file) { + if (isDisposed()) return; if (isStateChangeable(file)) { if (PowerSaveMode.isEnabled()) { myCurrentIcon = AllIcons.Ide.HectorNo; diff --git a/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java b/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java index f882ad127cea..9fa2d24ef681 100644 --- a/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java @@ -302,6 +302,8 @@ public class StartupManagerImpl extends StartupManagerEx { } public void startCacheUpdate() { + if (myProject.isDisposed()) return; + try { DumbServiceImpl dumbService = DumbServiceImpl.getInstance(myProject); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java index ef6f3e0b5df3..950cbca61896 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java @@ -28,6 +28,7 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.application.JBProtocolCommand; +import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.application.ex.ApplicationInfoEx; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAwareAction; @@ -96,8 +97,7 @@ public class FlatWelcomeFrame extends JFrame implements IdeFrame, Disposable, Ac @Override public void addNotify() { super.addNotify(); - //noinspection SSBasedInspection - SwingUtilities.invokeLater(() -> JBProtocolCommand.handleCurrentCommand()); + TransactionGuard.submitTransaction(FlatWelcomeFrame.this, () -> JBProtocolCommand.handleCurrentCommand()); } }; diff --git a/platform/platform-impl/src/com/intellij/reporting/FreezeLoggerImpl.java b/platform/platform-impl/src/com/intellij/reporting/FreezeLoggerImpl.java index 079b5317dd64..95e0b33cf5a1 100644 --- a/platform/platform-impl/src/com/intellij/reporting/FreezeLoggerImpl.java +++ b/platform/platform-impl/src/com/intellij/reporting/FreezeLoggerImpl.java @@ -38,7 +38,7 @@ public class FreezeLoggerImpl extends FreezeLogger { @Override public void runUnderPerformanceMonitor(@Nullable Project project, @NotNull Runnable action) { - if (isUnderDebug() || ApplicationManager.getApplication().isUnitTestMode()) { + if (!shouldReport() || isUnderDebug() || ApplicationManager.getApplication().isUnitTestMode()) { action.run(); return; } @@ -54,12 +54,12 @@ public class FreezeLoggerImpl extends FreezeLogger { ALARM.cancelAllRequests(); } } - + + private static boolean shouldReport() { + return Registry.is("typing.freeze.report.dumps"); + } + private static void dumpThreads(@Nullable Project project, @NotNull ModalityState initialState) { - if (!initialState.equals(ModalityState.current())) { - return; - } - final ThreadInfo[] infos = ThreadDumper.getThreadInfos(); final String edtTrace = ThreadDumper.dumpEdtStackTrace(infos); if (edtTrace.contains("java.lang.ClassLoader.loadClass")) { @@ -67,23 +67,23 @@ public class FreezeLoggerImpl extends FreezeLogger { } final boolean isInDumbMode = project != null && !project.isDisposed() && DumbService.isDumb(project); - final String dumps = ThreadDumper.dumpThreadsToString(); - final String msg = "Typing freeze report, (DumbMode=" + isInDumbMode + ") thread dumps attached. EDT stacktrace:\n" - + edtTrace - + "\n\n\n"; - if (Registry.is("typing.freeze.report.dumps")) { + ApplicationManager.getApplication().invokeLater(() -> { + if (!initialState.equals(ModalityState.current())) return; + sendDumpsInBackground(infos, isInDumbMode); + }, ModalityState.any()); + } + + private static void sendDumpsInBackground(ThreadInfo[] infos, boolean isInDumbMode) { + ApplicationManager.getApplication().executeOnPooledThread(() -> { ThreadDumpInfo info = new ThreadDumpInfo(infos, isInDumbMode); String report = ReporterKt.createReportLine("typing-freeze-dumps", info); if (!StatsSender.INSTANCE.send(report, true)) { LOG.debug("Error while reporting thread dump"); } - } - else { - LOG.error(msg, dumps); - } + }); } - + private static boolean isUnderDebug() { return ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("jdwp"); } diff --git a/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java b/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java index 3200bf9f471c..617fce5e2781 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java +++ b/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java @@ -192,6 +192,7 @@ public class SwingHelper { public static void adjustDialogSizeToFitPreferredSize(@NotNull DialogWrapper dialogWrapper) { JRootPane rootPane = dialogWrapper.getRootPane(); + if (rootPane == null) return; Dimension componentSize = rootPane.getSize(); Dimension componentPreferredSize = rootPane.getPreferredSize(); if (componentPreferredSize.width <= componentSize.width && componentPreferredSize.height <= componentSize.height) { diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index fcd5d571cb2e..1290f2bf384c 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -1476,7 +1476,7 @@ group.Vcs.Browse.text=Browse VCS Repository action.Graph.print.reset=Reset action.MarkExcludeRoot.text=Excluded action.UnmarkRoot.text=Unmark Root -action.MarkAsContentRoot.text=Cancel Exclusion +action.MarkAsContentRoot.text=Not Excluded action.MarkAsContentRoot.description=Cancel exclusion for the selected directory to make its files processable by IDE's actions action.CreateLibraryFromFile.text=Add as Library... action.ImportModuleFromImlFile.text=Import Module diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FileIndexBase.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FileIndexBase.java index c0ca9d99a3bd..25be05c4013f 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FileIndexBase.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FileIndexBase.java @@ -2,8 +2,10 @@ package com.intellij.openapi.roots.impl; import com.intellij.injected.editor.VirtualFileWindow; import com.intellij.openapi.fileTypes.FileTypeRegistry; +import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.FileIndex; +import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; @@ -33,4 +35,9 @@ public abstract class FileIndexBase implements FileIndex { !myFileTypeRegistry.isFileIgnored(file) && isInSourceContent(file); } + + @NotNull + protected static VirtualFile[][] getModuleContentAndSourceRoots(Module module) { + return new VirtualFile[][]{ModuleRootManager.getInstance(module).getContentRoots(), ModuleRootManager.getInstance(module).getSourceRoots()}; + } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java index 1ea6a212a9c5..9871077f4f8f 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java @@ -24,6 +24,7 @@ import com.intellij.openapi.util.Computable; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileFilter; +import com.intellij.openapi.vfs.VirtualFileWithId; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -45,23 +46,26 @@ public class ModuleFileIndexImpl extends FileIndexBase implements ModuleFileInde @Override public boolean iterateContent(@NotNull ContentIterator iterator) { - final List contentRoots = ApplicationManager.getApplication().runReadAction(new Computable>() { - @Override - public List compute() { - if (myModule.isDisposed()) return Collections.emptyList(); - - List result = ContainerUtil.newArrayList(); - for (VirtualFile contentRoot : ModuleRootManager.getInstance(myModule).getContentRoots()) { - VirtualFile parent = contentRoot.getParent(); + final Set contentRoots = ApplicationManager.getApplication().runReadAction((Computable>)() -> { + if (myModule.isDisposed()) return Collections.emptySet(); + + Set result = new LinkedHashSet<>(); + VirtualFile[][] allRoots = getModuleContentAndSourceRoots(myModule); + for (VirtualFile[] roots : allRoots) { + for (VirtualFile root : roots) { + DirectoryInfo info = getInfoForFileOrDirectory(root); + if (!info.isInProject()) continue; + + VirtualFile parent = root.getParent(); if (parent != null) { DirectoryInfo parentInfo = myDirectoryIndex.getInfoForFile(parent); if (parentInfo.isInProject() && myModule.equals(parentInfo.getModule())) continue; // inner content - skip it } - result.add(contentRoot); + result.add(root); } - - return result; } + + return result; }); for (VirtualFile contentRoot : contentRoots) { boolean finished = VfsUtilCore.iterateChildrenRecursively(contentRoot, myContentFilter, iterator); diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java index 1fadbde8a58e..f477dc6c04c8 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java @@ -38,6 +38,7 @@ import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; import org.jetbrains.jps.model.module.JpsModuleSourceRootType; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -54,12 +55,7 @@ public class ProjectFileIndexImpl extends FileIndexBase implements ProjectFileIn @Override public boolean iterateContent(@NotNull ContentIterator iterator) { - Module[] modules = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public Module[] compute() { - return ModuleManager.getInstance(myProject).getModules(); - } - }); + Module[] modules = ApplicationManager.getApplication().runReadAction((Computable)() -> ModuleManager.getInstance(myProject).getModules()); for (final Module module : modules) { for (VirtualFile contentRoot : getRootsToIterate(module)) { boolean finished = VfsUtilCore.iterateChildrenRecursively(contentRoot, myContentFilter, iterator); @@ -70,28 +66,27 @@ public class ProjectFileIndexImpl extends FileIndexBase implements ProjectFileIn return true; } - private List getRootsToIterate(final Module module) { - return ApplicationManager.getApplication().runReadAction(new Computable>() { - @Override - public List compute() { - if (module.isDisposed()) return Collections.emptyList(); + private Set getRootsToIterate(final Module module) { + return ApplicationManager.getApplication().runReadAction((Computable>)() -> { + if (module.isDisposed()) return Collections.emptySet(); - List result = ContainerUtil.newArrayList(); - for (VirtualFile contentRoot : ModuleRootManager.getInstance(module).getContentRoots()) { - DirectoryInfo info = getInfoForFileOrDirectory(contentRoot); + Set result = new LinkedHashSet<>(); + for (VirtualFile[] roots : getModuleContentAndSourceRoots(module)) { + for (VirtualFile root : roots) { + DirectoryInfo info = getInfoForFileOrDirectory(root); if (!info.isInProject()) continue; // is excluded or ignored if (!module.equals(info.getModule())) continue; // maybe 2 modules have the same content root? - VirtualFile parent = contentRoot.getParent(); + VirtualFile parent = root.getParent(); if (parent != null) { DirectoryInfo parentInfo = getInfoForFileOrDirectory(parent); if (parentInfo.isInProject() && parentInfo.getModule() != null) continue; } - result.add(contentRoot); + result.add(root); } - - return result; } + + return result; }); } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java index 31831cc1b52e..e0587269baa5 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java @@ -28,6 +28,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileWithId; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import com.intellij.util.CollectionQuery; import com.intellij.util.Query; @@ -179,6 +180,10 @@ public class RootIndex { } private static boolean ensureValid(@NotNull VirtualFile file, @NotNull Object container) { + if (!(file instanceof VirtualFileWithId)) { + //skip roots from unsupported file systems (e.g. http) + return false; + } if (!file.isValid()) { LOG.error("Invalid root " + file + " in " + container); return false; @@ -367,7 +372,7 @@ public class RootIndex { @Nullable VirtualFile librarySourceRoot = myRootInfo.findLibraryRootInfo(roots, true); result.addAll(myRootInfo.getLibraryOrderEntries(roots, libraryClassRoot, librarySourceRoot, myLibClassRootEntries, myLibSourceRootEntries)); - VirtualFile moduleContentRoot = myRootInfo.findModuleRootInfo(roots); + VirtualFile moduleContentRoot = myRootInfo.findNearestContentRoot(roots); if (moduleContentRoot != null) { ContainerUtil.addIfNotNull(result, myRootInfo.getModuleSourceEntry(roots, moduleContentRoot, myLibClassRootEntries)); } @@ -541,16 +546,39 @@ public class RootIndex { return result; } + /** + * Returns nearest content root for a file by its parent directories hierarchy. If the file is excluded (i.e. located under an excluded + * root and there are no source roots on the path to the excluded root) returns {@code null}. + */ @Nullable - private VirtualFile findModuleRootInfo(@NotNull List hierarchy) { + private VirtualFile findNearestContentRoot(@NotNull List hierarchy) { + Collection sourceRootOwners = null; + boolean underExcludedSourceRoot = false; for (VirtualFile root : hierarchy) { Module module = contentRootOf.get(root); Module excludedFrom = excludedFromModule.get(root); - if (module != null && excludedFrom != module) { + if (module != null && (excludedFrom != module || underExcludedSourceRoot && sourceRootOwners.contains(module))) { return root; } if (excludedFrom != null || excludedFromProject.contains(root)) { - return null; + if (sourceRootOwners != null) { + underExcludedSourceRoot = true; + } + else { + return null; + } + } + + if (!underExcludedSourceRoot && sourceRootOf.containsKey(root)) { + Collection modulesForSourceRoot = sourceRootOf.get(root); + if (!modulesForSourceRoot.isEmpty()) { + if (sourceRootOwners == null) { + sourceRootOwners = modulesForSourceRoot; + } + else { + sourceRootOwners = ContainerUtil.union(sourceRootOwners, modulesForSourceRoot); + } + } } } return null; @@ -662,7 +690,7 @@ public class RootIndex { private static Pair calcDirectoryInfo(@NotNull final VirtualFile root, @NotNull final List hierarchy, @NotNull RootInfo info) { - VirtualFile moduleContentRoot = info.findModuleRootInfo(hierarchy); + VirtualFile moduleContentRoot = info.findNearestContentRoot(hierarchy); VirtualFile libraryClassRoot = info.findLibraryRootInfo(hierarchy, false); VirtualFile librarySourceRoot = info.findLibraryRootInfo(hierarchy, true); boolean inProject = moduleContentRoot != null || libraryClassRoot != null || librarySourceRoot != null; diff --git a/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java index 8005327e8149..5f8310e103b7 100644 --- a/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -474,6 +474,18 @@ public class EditorTestUtil { this.carets = carets; this.blockSelection = blockSelection; } + + /** + * Returns true if current CaretAndSelectionState contains at least one caret or selection explicitly specified + */ + public boolean hasExplicitCaret() { + if(carets.isEmpty()) return false; + if(blockSelection == null && carets.size() == 1) { + CaretInfo caret = carets.get(0); + return caret.position != null || caret.selection != null; + } + return true; + } } public static class CaretInfo { diff --git a/platform/testFramework/src/com/intellij/testFramework/FailedTestDebugLogConsoleFolding.java b/platform/testFramework/src/com/intellij/testFramework/FailedTestDebugLogConsoleFolding.java new file mode 100644 index 000000000000..a9517fe76b57 --- /dev/null +++ b/platform/testFramework/src/com/intellij/testFramework/FailedTestDebugLogConsoleFolding.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2016 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.testFramework; + +import com.intellij.execution.ConsoleFolding; + +import java.util.List; + +public class FailedTestDebugLogConsoleFolding extends ConsoleFolding { + @Override + public boolean shouldFoldLine(String line) { + return line.indexOf(TestLoggerFactory.FAILED_TEST_DEBUG_OUTPUT_MARKER) != -1; + } + + @Override + public String getPlaceholderText(List lines) { + return " "; + } +} diff --git a/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java b/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java index e133b0146a59..2a35ff76b0e6 100644 --- a/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java @@ -146,12 +146,28 @@ public abstract class LightPlatformCodeInsightTestCase extends LightPlatformTest */ @NotNull protected static Document configureFromFileText(@NonNls @NotNull final String fileName, @NonNls @NotNull final String fileText) { + return configureFromFileText(fileName, fileText, false); + } + + /** + * Same as configureByFile but text is provided directly. + * @param fileName - name of the file. + * @param fileText - data file text. + * @param checkCaret - if true, if will be verified that file contains at least one caret or selection marker + */ + @NotNull + protected static Document configureFromFileText(@NonNls @NotNull final String fileName, + @NonNls @NotNull final String fileText, + boolean checkCaret) { return new WriteCommandAction(null) { @Override protected void run(@NotNull Result result) throws Throwable { final Document fakeDocument = new DocumentImpl(fileText); EditorTestUtil.CaretAndSelectionState caretsState = EditorTestUtil.extractCaretAndSelectionMarkers(fakeDocument); + if(checkCaret) { + assertTrue("No caret specified in " + fileName, caretsState.hasExplicitCaret()); + } String newFileText = fakeDocument.getText(); Document document; diff --git a/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java b/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java index 7f9752c9d06a..75f22b2e4e5f 100644 --- a/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java +++ b/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java @@ -20,6 +20,7 @@ import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.LineTokenizer; import com.intellij.openapi.util.text.StringUtil; import org.apache.log4j.*; import org.apache.log4j.spi.LoggingEvent; @@ -145,6 +146,8 @@ public class TestLoggerFactory implements Logger.Factory { private static final StringWriter STRING_WRITER = new StringWriter(); private static final StringBuffer BUFFER = STRING_WRITER.getBuffer(); + static final char FAILED_TEST_DEBUG_OUTPUT_MARKER = '\u2003'; + // inserted unicode whitespace to be able to tell these failed tests log lines from the others and fold them private static final WriterAppender APPENDER = new WriterAppender(new PatternLayout("%d{HH:mm:ss,SSS} %p %.30c - %m%n"), STRING_WRITER); private static final int MAX_BUFFER_LENGTH = 100000; private static final String CFQN = Category.class.getName(); @@ -165,8 +168,20 @@ public class TestLoggerFactory implements Logger.Factory { } public static void onTestFinished(boolean success) { - if (!success) { - System.err.println(BUFFER); + if (!success && BUFFER.length() != 0) { + if (UsefulTestCase.IS_UNDER_TEAMCITY) { + // print in one statement to avoid exception text cutting in between causing this fold to expand + BUFFER.insert(0, "##teamcity[blockOpened name='DEBUG log']\n"); + BUFFER.append( "\n##teamcity[blockClosed name='DEBUG log']\n"); + System.err.println(BUFFER); + } + else { + // mark each line in IDEA console with this hidden mark to be able to fold it automatically + String[] lines = LineTokenizer.tokenize(BUFFER, false, false); + String text = StringUtil.join(lines, FAILED_TEST_DEBUG_OUTPUT_MARKER + "\n"); + if (!text.startsWith("\n")) text = "\n" + text; + System.err.println(text); + } } BUFFER.setLength(0); } diff --git a/platform/util-rt/src/com/intellij/openapi/util/Pair.java b/platform/util-rt/src/com/intellij/openapi/util/Pair.java index 84b870010429..b2518ddce1f3 100644 --- a/platform/util-rt/src/com/intellij/openapi/util/Pair.java +++ b/platform/util-rt/src/com/intellij/openapi/util/Pair.java @@ -37,7 +37,7 @@ public class Pair { @SuppressWarnings("MethodNamesDifferingOnlyByCase") public static Pair pair(A first, B second) { //noinspection DontUsePairConstructor - return new Pair(first, second); + return create(first, second); } @NotNull diff --git a/platform/util/src/com/intellij/openapi/util/LowMemoryWatcherManager.java b/platform/util/src/com/intellij/openapi/util/LowMemoryWatcherManager.java index d38d02df4631..6a3f5e3ddcee 100644 --- a/platform/util/src/com/intellij/openapi/util/LowMemoryWatcherManager.java +++ b/platform/util/src/com/intellij/openapi/util/LowMemoryWatcherManager.java @@ -18,7 +18,6 @@ package com.intellij.openapi.util; import com.intellij.openapi.Disposable; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.registry.Registry; -import com.intellij.util.concurrency.AppExecutorUtil; import org.jetbrains.annotations.NotNull; import javax.management.ListenerNotFoundException; @@ -37,6 +36,7 @@ public class LowMemoryWatcherManager implements Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.LowMemoryWatcherManager"); private static final long MEM_THRESHOLD = 5 /*MB*/ * 1024 * 1024; + @NotNull private final ExecutorService myExecutorService; private Future mySubmitted; // guarded by ourJanitor private final AtomicBoolean myProcessing = new AtomicBoolean(); @@ -52,7 +52,8 @@ public class LowMemoryWatcherManager implements Disposable { } }; - public LowMemoryWatcherManager() { + public LowMemoryWatcherManager(@NotNull ExecutorService executorService) { + myExecutorService = executorService; try { for (MemoryPoolMXBean bean : ManagementFactory.getMemoryPoolMXBeans()) { if (bean.getType() == MemoryType.HEAP && bean.isUsageThresholdSupported()) { @@ -83,7 +84,7 @@ public class LowMemoryWatcherManager implements Disposable { synchronized (myJanitor) { if (mySubmitted == null) { - mySubmitted = myExecutor.getValue().submit(myJanitor); + mySubmitted = myExecutorService.submit(myJanitor); } } } @@ -101,14 +102,6 @@ public class LowMemoryWatcherManager implements Disposable { } } - private final NotNullLazyValue myExecutor = new NotNullLazyValue() { - @NotNull - @Override - protected ExecutorService compute() { - return AppExecutorUtil.createBoundedApplicationPoolExecutor("lowMemoryWatcher", 1); - } - }; - @Override public void dispose() { try { diff --git a/platform/util/src/com/intellij/openapi/vfs/CharsetToolkit.java b/platform/util/src/com/intellij/openapi/vfs/CharsetToolkit.java index 178936acf6ec..b8f94ab9e12e 100644 --- a/platform/util/src/com/intellij/openapi/vfs/CharsetToolkit.java +++ b/platform/util/src/com/intellij/openapi/vfs/CharsetToolkit.java @@ -624,8 +624,12 @@ public class CharsetToolkit { return CHARSET_TO_MANDATORY_BOM.get(charset); } + /** + * @return BOM which can be associated with this charset, or null otherwise. + * Currently these are UTF-16xx, UTF-32xx and UTF-8. + */ @Nullable - public static byte[] getBom(@NotNull Charset charset) { + public static byte[] getPossibleBom(@NotNull Charset charset) { if (charset.equals(UTF8_CHARSET)) return UTF8_BOM; return CHARSET_TO_MANDATORY_BOM.get(charset); } diff --git a/platform/util/src/com/intellij/util/PausesStat.java b/platform/util/src/com/intellij/util/PausesStat.java index 06070fa8d402..540e17014032 100644 --- a/platform/util/src/com/intellij/util/PausesStat.java +++ b/platform/util/src/com/intellij/util/PausesStat.java @@ -67,7 +67,7 @@ public class PausesStat { long finishStamp = System.currentTimeMillis(); int duration = (int)(finishStamp - startTimeStamp); started = false; - duration = Math.min(duration, (1 << 16) - 1); + duration = Math.min(duration, Short.MAX_VALUE); if (duration > maxDuration) { maxDuration = duration; maxDurationDescription = description; diff --git a/platform/util/src/com/intellij/util/concurrency/AppScheduledExecutorService.java b/platform/util/src/com/intellij/util/concurrency/AppScheduledExecutorService.java index fed13f11e2f6..d0af88715a32 100644 --- a/platform/util/src/com/intellij/util/concurrency/AppScheduledExecutorService.java +++ b/platform/util/src/com/intellij/util/concurrency/AppScheduledExecutorService.java @@ -37,7 +37,7 @@ public class AppScheduledExecutorService extends SchedulingWrapper { private static final Logger LOG = Logger.getInstance("#org.jetbrains.ide.PooledThreadExecutor"); static final String POOLED_THREAD_PREFIX = "ApplicationImpl pooled thread "; @NotNull private final String myName; - private final LowMemoryWatcherManager myLowMemoryWatcherManager = new LowMemoryWatcherManager(); + private final LowMemoryWatcherManager myLowMemoryWatcherManager; private Consumer newThreadListener; private final AtomicInteger counter = new AtomicInteger(); @@ -68,6 +68,7 @@ public class AppScheduledExecutorService extends SchedulingWrapper { return thread; } }); + myLowMemoryWatcherManager = new LowMemoryWatcherManager(this); } public void setNewThreadListener(@NotNull Consumer threadListener) { diff --git a/platform/util/src/com/intellij/util/containers/ContainerUtil.java b/platform/util/src/com/intellij/util/containers/ContainerUtil.java index 8e40d738099b..1fed757d77e8 100644 --- a/platform/util/src/com/intellij/util/containers/ContainerUtil.java +++ b/platform/util/src/com/intellij/util/containers/ContainerUtil.java @@ -450,6 +450,12 @@ public class ContainerUtil extends ContainerUtilRt { @NotNull @Contract(pure=true) public static Set union(@NotNull Set set, @NotNull Set set2) { + return union((Collection)set, set2); + } + + @NotNull + @Contract(pure=true) + public static Set union(@NotNull Collection set, @NotNull Collection set2) { Set result = new THashSet(set.size() + set2.size()); result.addAll(set); result.addAll(set2); diff --git a/platform/util/src/com/intellij/util/containers/FList.java b/platform/util/src/com/intellij/util/containers/FList.java index dde6dcf3fcba..4ddb009715dd 100644 --- a/platform/util/src/com/intellij/util/containers/FList.java +++ b/platform/util/src/com/intellij/util/containers/FList.java @@ -153,4 +153,15 @@ public class FList extends AbstractList { //noinspection unchecked return (FList)EMPTY_LIST; } + + /** + * Creates an FList object with the elements of the given sequence in the reversed order, i.e. the last element of from will be the result's {@link #getHead()} + */ + public static FList createFromReversed(Iterable from) { + FList result = emptyList(); + for (E e : from) { + result = result.prepend(e); + } + return result; + } } diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 4390e42ba525..93dad49bf6b5 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -54,7 +54,6 @@ import javax.swing.plaf.basic.BasicComboBoxUI; import javax.swing.plaf.basic.BasicRadioButtonUI; import javax.swing.plaf.basic.ComboPopup; import javax.swing.text.*; -import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; import javax.swing.undo.UndoManager; @@ -2370,19 +2369,8 @@ public class UIUtil { final StyleSheet style = new StyleSheet(); style.addStyleSheet(isUnderDarcula() ? (StyleSheet)UIManager.getDefaults().get("StyledEditorKit.JBDefaultStyle") : DEFAULT_HTML_KIT_CSS); style.addRule(customCss); - scaleStyleSheetFontSize(style, size); return new HTMLEditorKit() { - - @Override - public Document createDefaultDocument() { - Document document = super.createDefaultDocument(); - if (document instanceof HTMLDocument) { - scaleStyleSheetFontSize(((HTMLDocument)document).getStyleSheet(), size); - } - return document; - } - @Override public StyleSheet getStyleSheet() { return style; @@ -2390,14 +2378,6 @@ public class UIUtil { }; } - private static void scaleStyleSheetFontSize(@Nullable StyleSheet styleSheet, int bodyFontSize) { - // In compliance with javax.swing.text.html.StyleSheet logic, where 14pt font size is specified in - // javax/swing/text/html/default.css and javax.swing.text.html.StyleSheet.sizeMapDefault[3]. - if (styleSheet != null) { - styleSheet.addRule("BASE_SIZE " + bodyFontSize); - } - } - public static void removeScrollBorder(final Component c) { for (JScrollPane scrollPane : uiTraverser(c).filter(JScrollPane.class)) { if (!uiParents(scrollPane, true) diff --git a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java index d49df174fb43..d4553b074dab 100644 --- a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java +++ b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java @@ -1,7 +1,6 @@ package com.intellij.remoteServer.util; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; @@ -247,7 +246,7 @@ public class CloudGitDeploymentRuntime extends CloudDeploymentRuntime { private static T runOnEdt(final Computable computable) { final Ref result = new Ref<>(); - ApplicationManager.getApplication().invokeAndWait(() -> result.set(computable.compute()), ModalityState.any()); + ApplicationManager.getApplication().invokeAndWait(() -> result.set(computable.compute())); return result.get(); } diff --git a/plugins/google-app-engine/source/com/intellij/appengine/facet/AppEngineFrameworkDetector.java b/plugins/google-app-engine/source/com/intellij/appengine/facet/AppEngineFrameworkDetector.java index bb03d9dfe79a..ad0c72a70001 100644 --- a/plugins/google-app-engine/source/com/intellij/appengine/facet/AppEngineFrameworkDetector.java +++ b/plugins/google-app-engine/source/com/intellij/appengine/facet/AppEngineFrameworkDetector.java @@ -39,6 +39,7 @@ public class AppEngineFrameworkDetector extends FacetBasedFrameworkDetector getFacetType() { return FacetType.findInstance(AppEngineFacetType.class); diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxGetterSetterPrototypeProvider.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxGetterSetterPrototypeProvider.java index 7df76d7e5436..8ebccb19b934 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxGetterSetterPrototypeProvider.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxGetterSetterPrototypeProvider.java @@ -36,7 +36,8 @@ public class JavaFxGetterSetterPrototypeProvider extends GetterSetterPrototypePr @Override public boolean canGeneratePrototypeFor(PsiField field) { - return InheritanceUtil.isInheritor(field.getType(), JavaFxCommonNames.JAVAFX_BEANS_VALUE_OBSERVABLE_VALUE); + return InheritanceUtil.isInheritor(field.getType(), JavaFxCommonNames.JAVAFX_BEANS_VALUE_OBSERVABLE_VALUE) && + JavaFxPsiUtil.getWrappedPropertyType(field, field.getProject(), JavaFxCommonNames.ourReadOnlyMap) != null; } @Override @@ -46,6 +47,7 @@ public class JavaFxGetterSetterPrototypeProvider extends GetterSetterPrototypePr final PsiMethod getter = GenerateMembersUtil.generateSimpleGetterPrototype(field); final PsiType wrappedType = JavaFxPsiUtil.getWrappedPropertyType(field, project, JavaFxCommonNames.ourReadOnlyMap); + LOG.assertTrue(wrappedType != null, field.getType()); getter.setName(PropertyUtil.suggestGetterName(PropertyUtil.suggestPropertyName(field), wrappedType)); final PsiTypeElement returnTypeElement = getter.getReturnTypeElement(); @@ -70,7 +72,7 @@ public class JavaFxGetterSetterPrototypeProvider extends GetterSetterPrototypePr final Project project = field.getProject(); final PsiType wrappedType = JavaFxPsiUtil.getWrappedPropertyType(field, project, JavaFxCommonNames.ourWritableMap); - + LOG.assertTrue(wrappedType != null, field.getType()); final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); final PsiTypeElement newTypeElement = elementFactory.createTypeElement(wrappedType); final PsiParameter[] parameters = setter.getParameterList().getParameters(); diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java index d2af3f6dcc2e..28c1b63f7b60 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java @@ -69,12 +69,13 @@ public class ResourceBundleFileStructureViewElement implements StructureViewTree final HashSet remains = new HashSet<>(myElements.keySet()); for (Map.Entry> entry : propertyNames.entrySet()) { final String propKey = entry.getKey(); - final IProperty representative = entry.getValue().iterator().next(); + Collection properties = entry.getValue(); final ResourceBundlePropertyStructureViewElement oldPropertyNode = myElements.get(propKey); - if (oldPropertyNode != null && oldPropertyNode.getProperty() == representative) { + if (oldPropertyNode != null && properties.contains(oldPropertyNode.getProperty())) { remains.remove(propKey); continue; } + final IProperty representative = properties.iterator().next(); final ResourceBundlePropertyStructureViewElement node = new ResourceBundlePropertyStructureViewElement(representative); myElements.put(propKey, node); } diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/ReadProcessThread.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/ReadProcessThread.java index 3dff2d37104d..62c873e39ce3 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/ReadProcessThread.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/ReadProcessThread.java @@ -15,15 +15,16 @@ */ package org.intellij.lang.xpath.xslt.run; -import com.intellij.util.concurrency.AppExecutorUtil; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import com.intellij.util.Alarm; import java.io.IOException; import java.io.Reader; -import java.util.concurrent.TimeUnit; /* Copied from com.intellij.execution.process.OSProcessHandler.ReadProcessThread */ @SuppressWarnings({"ALL"}) -abstract class ReadProcessThread extends Thread { +abstract class ReadProcessThread extends Thread implements Disposable { private static final int NOTIFY_TEXT_DELAY = 300; private final Reader myReader; @@ -40,15 +41,15 @@ abstract class ReadProcessThread extends Thread { } public void run() { - AppExecutorUtil.getAppScheduledExecutorService().schedule( - new Runnable() { + final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, this); + myAlarm.addRequest(new Runnable() { public void run() { if(!isClosed()) { - AppExecutorUtil.getAppScheduledExecutorService().schedule(this, NOTIFY_TEXT_DELAY,TimeUnit.MILLISECONDS); + myAlarm.addRequest(this, NOTIFY_TEXT_DELAY); checkTextAvailable(); } } - }, NOTIFY_TEXT_DELAY, TimeUnit.MILLISECONDS); + }, NOTIFY_TEXT_DELAY); try { while (!isClosed()) { @@ -67,8 +68,14 @@ abstract class ReadProcessThread extends Thread { catch (Exception e) { e.printStackTrace(); } - - close(); + finally { + try { + close(); + } + finally { + Disposer.dispose(this); + } + } } private int readNextByte() { @@ -122,4 +129,7 @@ abstract class ReadProcessThread extends Thread { return myIsClosed; } + @Override + public void dispose() { + } } diff --git a/python/helpers/coverage_runner/run_coverage.py b/python/helpers/coverage_runner/run_coverage.py index f637d6e8d42c..1c1f69a881c4 100644 --- a/python/helpers/coverage_runner/run_coverage.py +++ b/python/helpers/coverage_runner/run_coverage.py @@ -13,18 +13,22 @@ else: from coverage.cmdline import main coverage_file = os.getenv('PYCHARM_COVERAGE_FILE') + +coverage_file = coverage_file[0:-len(".coverage")] + run_cov = os.getenv('PYCHARM_RUN_COVERAGE') if os.getenv('CREATE_TEMP_COVERAGE_FILE'): line = 'LOG: PyCharm: File mapping:%s\t%s\n' import tempfile (h, new_cov_file) = tempfile.mkstemp(prefix='pycharm-coverage') - print(line%(coverage_file, new_cov_file)) + print(line%(coverage_file + ".coverage", new_cov_file + ".coverage")) print(line%(coverage_file + '.syspath.txt', new_cov_file + '.syspath.txt')) print(line%(coverage_file + '.xml', new_cov_file + '.xml')) - coverage_file = new_cov_file + coverage_file = new_cov_file + ".cov" if coverage_file: - os.environ['COVERAGE_FILE'] = coverage_file + os.environ['COVERAGE_FILE'] = coverage_file + ".coverage" + if run_cov: a_file = open(coverage_file + '.syspath.txt', mode='w') a_file.write(os.getcwd()+"\n") @@ -41,7 +45,10 @@ for arg in sys.argv: sys.argv = argv cwd = os.getcwd() -main() -if run_cov: - os.chdir(cwd) - main(["xml", "-o", coverage_file + ".xml", "--ignore-errors"]) \ No newline at end of file + +try: + main() +finally: + if run_cov: + os.chdir(cwd) + main(["xml", "-o", coverage_file + ".xml", "--ignore-errors"]) \ No newline at end of file diff --git a/python/pluginJava/com/jetbrains/python/facet/PythonFacetType.java b/python/pluginJava/com/jetbrains/python/facet/PythonFacetType.java index ac6f73c9ec88..c38942079016 100644 --- a/python/pluginJava/com/jetbrains/python/facet/PythonFacetType.java +++ b/python/pluginJava/com/jetbrains/python/facet/PythonFacetType.java @@ -80,6 +80,7 @@ public class PythonFacetType extends FacetType getFacetType() { return PythonFacetType.getInstance(); diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index 1db11b782e1f..4bc626a4fb80 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -154,6 +154,7 @@ QFIX.NAME.remove.argument=Remove argument QFIX.NAME.remove.parameter=Remove parameter QFIX.NAME.rename.argument=Rename argument +QFIX.NAME.rename.element=Rename element QFIX.NAME.wrap.in.exception=Wrap with Exception call diff --git a/python/src/com/jetbrains/python/buildout/BuildoutFrameworkDetector.java b/python/src/com/jetbrains/python/buildout/BuildoutFrameworkDetector.java index c2f869ae4853..bd2857d9e23f 100644 --- a/python/src/com/jetbrains/python/buildout/BuildoutFrameworkDetector.java +++ b/python/src/com/jetbrains/python/buildout/BuildoutFrameworkDetector.java @@ -42,6 +42,7 @@ public class BuildoutFrameworkDetector extends FacetBasedFrameworkDetector getFacetType() { return BuildoutFacetType.getInstance(); diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java index 938dff0b3344..2476bdc11f30 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java @@ -43,6 +43,7 @@ import com.intellij.lang.ASTNode; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionUtil; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; @@ -75,6 +76,7 @@ import com.intellij.psi.PsiFile; import com.intellij.remote.RemoteProcess; import com.intellij.remote.Tunnelable; import com.intellij.testFramework.LightVirtualFile; +import com.intellij.ui.GuiUtils; import com.intellij.util.ArrayUtil; import com.intellij.util.IJSwingUtilities; import com.intellij.util.PathMappingSettings; @@ -1048,7 +1050,7 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory PydevConsoleRunner.this.run()); + GuiUtils.invokeLaterIfNeeded(() -> PydevConsoleRunner.this.run(), ModalityState.defaultModalityState()); } }.queue(); } diff --git a/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java b/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java index 2a49d5efedd3..8ae23b99acb2 100644 --- a/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java +++ b/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java @@ -16,6 +16,7 @@ package com.jetbrains.python.console; import com.intellij.execution.console.LanguageConsoleView; +import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.wm.ToolWindow; @@ -37,6 +38,6 @@ public class PythonConsoleToolWindowFactory implements ToolWindowFactory, DumbAw PythonConsoleToolWindow.getInstance(project).initialized(); } }); - runner.run(); + TransactionGuard.submitTransaction(project, () -> runner.run()); } } diff --git a/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java b/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java index 4ee578511eec..8b2d0ec7dac7 100644 --- a/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java @@ -27,15 +27,14 @@ import com.intellij.openapi.util.JDOMExternalizableStringList; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiReference; +import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.QualifiedName; +import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; +import com.jetbrains.python.inspections.quickfix.PyRenameElementQuickFix; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.types.PyClassType; @@ -325,5 +324,35 @@ public class PyCompatibilityInspection extends PyInspection { } } } + + @Override + public void visitPyTargetExpression(PyTargetExpression node) { + super.visitPyTargetExpression(node); + warnAboutAsyncAndAwaitInPy35AndPy36(node); + } + + @Override + public void visitPyClass(PyClass node) { + super.visitPyClass(node); + warnAboutAsyncAndAwaitInPy35AndPy36(node); + } + + @Override + public void visitPyFunction(PyFunction node) { + super.visitPyFunction(node); + warnAboutAsyncAndAwaitInPy35AndPy36(node); + } + + private void warnAboutAsyncAndAwaitInPy35AndPy36(@NotNull PsiNameIdentifierOwner nameIdentifierOwner) { + final PsiElement nameIdentifier = nameIdentifierOwner.getNameIdentifier(); + + if (nameIdentifier != null && ArrayUtil.contains(nameIdentifierOwner.getName(), PyNames.AWAIT, PyNames.ASYNC)) { + registerOnFirstMatchingVersion(level -> LanguageLevel.PYTHON35.equals(level) || LanguageLevel.PYTHON36.equals(level), + "'async' and 'await' are not recommended to be used as variable, class, function or module names. " + + "They will become proper keywords in Python 3.7.", + nameIdentifier, + new PyRenameElementQuickFix()); + } + } } } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyRenameElementQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyRenameElementQuickFix.java index 103b404bec70..02a760247f67 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyRenameElementQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyRenameElementQuickFix.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -34,6 +34,7 @@ import com.intellij.refactoring.rename.PsiElementRenameHandler; import com.intellij.refactoring.rename.RenameProcessor; import com.intellij.refactoring.rename.RenamePsiElementProcessor; import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer; +import com.jetbrains.python.PyBundle; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.psi.PyNamedParameter; import com.jetbrains.python.psi.PyReferenceExpression; @@ -49,7 +50,7 @@ public class PyRenameElementQuickFix implements LocalQuickFix { @NotNull @Override public String getFamilyName() { - return "Rename element"; + return PyBundle.message("QFIX.NAME.rename.element"); } @Override diff --git a/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy35.py b/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy35.py new file mode 100644 index 000000000000..eea16c40ef25 --- /dev/null +++ b/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy35.py @@ -0,0 +1,14 @@ +class async(object): + pass + +class await(object): + pass + +def async(): + pass + +def await(): + pass + +async = 1 +await = 2 \ No newline at end of file diff --git a/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy36.py b/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy36.py new file mode 100644 index 000000000000..eea16c40ef25 --- /dev/null +++ b/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy36.py @@ -0,0 +1,14 @@ +class async(object): + pass + +class await(object): + pass + +def async(): + pass + +def await(): + pass + +async = 1 +await = 2 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35.py new file mode 100644 index 000000000000..bc25733ddf5d --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35.py @@ -0,0 +1,2 @@ +class async(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35_after.py new file mode 100644 index 000000000000..1a98a9cfc6c4 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35_after.py @@ -0,0 +1,2 @@ +class a(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36.py new file mode 100644 index 000000000000..bc25733ddf5d --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36.py @@ -0,0 +1,2 @@ +class async(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36_after.py new file mode 100644 index 000000000000..1a98a9cfc6c4 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36_after.py @@ -0,0 +1,2 @@ +class a(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35.py new file mode 100644 index 000000000000..f48d631547da --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35.py @@ -0,0 +1,2 @@ +def async(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35_after.py new file mode 100644 index 000000000000..690f3c163030 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35_after.py @@ -0,0 +1,2 @@ +def a(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36.py new file mode 100644 index 000000000000..f48d631547da --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36.py @@ -0,0 +1,2 @@ +def async(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36_after.py new file mode 100644 index 000000000000..690f3c163030 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36_after.py @@ -0,0 +1,2 @@ +def a(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35.py new file mode 100644 index 000000000000..96bae926eb40 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35.py @@ -0,0 +1 @@ +async = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35_after.py new file mode 100644 index 000000000000..d25d49e0ffc3 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35_after.py @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36.py new file mode 100644 index 000000000000..96bae926eb40 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36.py @@ -0,0 +1 @@ +async = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36_after.py new file mode 100644 index 000000000000..d25d49e0ffc3 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36_after.py @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35.py new file mode 100644 index 000000000000..c531a434721e --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35.py @@ -0,0 +1,2 @@ +class await(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35_after.py new file mode 100644 index 000000000000..1a98a9cfc6c4 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35_after.py @@ -0,0 +1,2 @@ +class a(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36.py new file mode 100644 index 000000000000..c531a434721e --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36.py @@ -0,0 +1,2 @@ +class await(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36_after.py new file mode 100644 index 000000000000..1a98a9cfc6c4 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36_after.py @@ -0,0 +1,2 @@ +class a(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35.py new file mode 100644 index 000000000000..c1ef9d6ccfae --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35.py @@ -0,0 +1,2 @@ +def await(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35_after.py new file mode 100644 index 000000000000..690f3c163030 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35_after.py @@ -0,0 +1,2 @@ +def a(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36.py new file mode 100644 index 000000000000..c1ef9d6ccfae --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36.py @@ -0,0 +1,2 @@ +def await(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36_after.py new file mode 100644 index 000000000000..690f3c163030 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36_after.py @@ -0,0 +1,2 @@ +def a(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35.py new file mode 100644 index 000000000000..1a28415ce838 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35.py @@ -0,0 +1 @@ +await = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35_after.py new file mode 100644 index 000000000000..d25d49e0ffc3 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35_after.py @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36.py new file mode 100644 index 000000000000..1a28415ce838 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36.py @@ -0,0 +1 @@ +await = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36_after.py new file mode 100644 index 000000000000..d25d49e0ffc3 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36_after.py @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 7d152a5520c7..ccea524a9b8f 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -551,7 +551,7 @@ public class PyQuickFixTest extends PyTestCase { myFixture.configureByFile(fileName); myFixture.enableInspections(PyShadowingBuiltinsInspection.class); myFixture.checkHighlighting(true, false, true); - final IntentionAction intentionAction = myFixture.getAvailableIntention("Rename element"); + final IntentionAction intentionAction = myFixture.getAvailableIntention(PyBundle.message("QFIX.NAME.rename.element")); assertNotNull(intentionAction); myFixture.launchAction(intentionAction); myFixture.checkResultByFile(graftBeforeExt(fileName, "_after")); @@ -563,7 +563,7 @@ public class PyQuickFixTest extends PyTestCase { myFixture.configureByFile(fileName); myFixture.enableInspections(PyShadowingBuiltinsInspection.class); myFixture.checkHighlighting(true, false, true); - final IntentionAction intentionAction = myFixture.getAvailableIntention("Rename element"); + final IntentionAction intentionAction = myFixture.getAvailableIntention(PyBundle.message("QFIX.NAME.rename.element")); assertNotNull(intentionAction); myFixture.launchAction(intentionAction); myFixture.checkResultByFile(graftBeforeExt(fileName, "_after")); diff --git a/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java index 4bc87459c36f..7afe19f1f695 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java @@ -209,6 +209,16 @@ public class PyCompatibilityInspectionTest extends PyTestCase { doTest(LanguageLevel.PYTHON36); } + // PY-16098 + public void testWarningAboutAsyncAndAwaitInPy35() { + doTest(LanguageLevel.PYTHON35); + } + + // PY-16098 + public void testWarningAboutAsyncAndAwaitInPy36() { + doTest(LanguageLevel.PYTHON36); + } + private void doTest(@NotNull LanguageLevel level) { runWithLanguageLevel(level, this::doTest); } diff --git a/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java b/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java index 5a63b49b66e6..16d5a9e75bc5 100644 --- a/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,9 @@ package com.jetbrains.python.quickFixes; import com.intellij.testFramework.TestDataPath; +import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyQuickFixTestCase; +import com.jetbrains.python.inspections.PyCompatibilityInspection; import com.jetbrains.python.inspections.PyPep8NamingInspection; import com.jetbrains.python.inspections.PyProtectedMemberInspection; import com.jetbrains.python.inspections.PyShadowingBuiltinsInspection; @@ -28,23 +30,82 @@ import com.jetbrains.python.inspections.PyShadowingBuiltinsInspection; public class RenameElementQuickFixTest extends PyQuickFixTestCase { public void testProtectedMember() { - doQuickFixTest(PyProtectedMemberInspection.class, "Rename element"); + doQuickFixTest(PyProtectedMemberInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } public void testPep8() { - doQuickFixTest(PyPep8NamingInspection.class, "Rename element"); + doQuickFixTest(PyPep8NamingInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } public void testPep8Class() { - doQuickFixTest(PyPep8NamingInspection.class, "Rename element"); + doQuickFixTest(PyPep8NamingInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } public void testPep8Function() { - doQuickFixTest(PyPep8NamingInspection.class, "Rename element"); + doQuickFixTest(PyPep8NamingInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } public void testShadowingBuiltins() { - doQuickFixTest(PyShadowingBuiltinsInspection.class, "Rename element"); + doQuickFixTest(PyShadowingBuiltinsInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } + // PY-16098 + public void testRenameAsyncClassInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); + } + + // PY-16098 + public void testRenameAsyncClassInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); + } + + // PY-16098 + public void testRenameAwaitClassInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); + } + + // PY-16098 + public void testRenameAwaitClassInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); + } + + // PY-16098 + public void testRenameAsyncFunctionInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); + } + + // PY-16098 + public void testRenameAsyncFunctionInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); + } + + // PY-16098 + public void testRenameAwaitFunctionInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); + } + + // PY-16098 + public void testRenameAwaitFunctionInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); + } + + // PY-16098 + public void testRenameAsyncVariableInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); + } + + // PY-16098 + public void testRenameAsyncVariableInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); + } + + // PY-16098 + public void testRenameAwaitVariableInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); + } + + // PY-16098 + public void testRenameAwaitVariableInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); + } } diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 2e0249035038..4de90ce9f8e3 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -1244,6 +1244,7 @@ +