Merge remote-tracking branch 'origin/master'

This commit is contained in:
Konstantin Bulenkov
2016-10-07 15:02:14 +02:00
155 changed files with 1575 additions and 763 deletions
@@ -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,
@@ -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);
}
@@ -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<PsiElement, String> returnErrors = LambdaUtil
.checkReturnTypeCompatible(expression, LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType));
if (returnErrors != null) {
for (Map.Entry<PsiElement, String> 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<PsiElement, String> 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<PsiElement, String> 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
@@ -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")
@@ -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
@@ -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<CatchDescriptor> myCatchStack;
private final DfaValue myRuntimeException;
private final DfaValue myError;
private final DfaValue myString;
private FList<Trap> myTrapStack = FList.emptyList();
private final ExceptionTransfer myRuntimeException;
private final ExceptionTransfer myError;
private final PsiType myNpe;
private final PsiType myAssertionError;
private final Stack<PsiElement> myElementStack = new Stack<>();
/**
* Variables for try-related control transfers. Contain exceptions or an (Throwable-inconvertible) string to indicate return inside finally
*/
private FactoryMap<PsiTryStatement, DfaVariableValue> 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<PsiTryStatement, DfaVariableValue>() {
@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<Trap> traps) {
addInstruction(new ControlTransferInstruction(myFactory.controlTransfer(target, traps)));
}
@NotNull
private FList<Trap> getTrapsInsideStatement(PsiStatement statement) {
return FList.createFromReversed(ContainerUtil.reverse(
ContainerUtil.findAll(myTrapStack, cd -> PsiTreeUtil.isAncestor(statement, cd.getAnchor(), true))));
}
@NotNull
private List<DfaVariableValue> 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<PsiCatchSection, ControlFlow.ControlFlowOffset> 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<Trap> 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<PsiType> 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<PsiClassType> 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);
}
@@ -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);
@@ -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<Trap>) : 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<DfaVariableValue>) : TransferTarget
object ReturnTransfer : TransferTarget
open class ControlTransferInstruction(val transfer: DfaControlTransferValue?) : Instruction() {
override fun accept(runner: DataFlowRunner, state: DfaMemoryState, visitor: InstructionVisitor): Array<out DfaInstructionState> {
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<PsiCatchSection, ControlFlow.ControlFlowOffset>): 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<Trap>): List<DfaInstructionState> {
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<DfaInstructionState> {
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<Trap>): List<DfaInstructionState> {
state.push(runner.factory.controlTransfer(target, traps))
return listOf(DfaInstructionState(runner.getInstruction(offset), state))
}
private fun leaveFinally(traps: FList<Trap>): List<DfaInstructionState> {
state.pop() as DfaControlTransferValue
return iteration(traps)
}
private fun processCatches(tryCatch: Trap.TryCatch, thrownValue: DfaValue, traps: FList<Trap>): List<DfaInstructionState> {
val result = arrayListOf<DfaInstructionState>()
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<DfaTypeValue> {
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<DfaTypeValue>()
}
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
}
}
@@ -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";
}
}
@@ -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<Trap> traps) {
return myControlTransfers.get(Pair.create(kind, traps));
}
private final Map<Pair<TransferTarget, FList<Trap>>, 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;
@@ -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) {
@@ -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 &&
@@ -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;
@@ -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<T extends PsiElement, R extends PsiRefe
if (classes.length == 1 &&
(canImportHere = canImportHere(allowCaretNearRef, editor, psiFile, classes[0].getName())) &&
(FileTypeUtils.isInServerPageFile(psiFile) ?
CodeInsightSettings.getInstance().JSP_ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY :
CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY) &&
isAddUnambiguousImportsOnTheFlyEnabled(psiFile) &&
(ApplicationManager.getApplication().isUnitTestMode() || DaemonListeners.canChangeFileSilently(psiFile)) &&
isInModlessContext &&
!autoImportWillInsertUnexpectedCharacters(classes[0])
@@ -348,6 +344,12 @@ public abstract class ImportClassFixBase<T extends PsiElement, R extends PsiRefe
return Result.POPUP_NOT_SHOWN;
}
public static boolean isAddUnambiguousImportsOnTheFlyEnabled(@NotNull PsiFile psiFile) {
return FileTypeUtils.isInServerPageFile(psiFile) ?
CodeInsightSettings.getInstance().JSP_ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY :
CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY;
}
protected int getStartOffset(T element, R ref) {
return element.getTextOffset();
}
@@ -30,8 +30,10 @@ import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.util.FileTypeUtils;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiMember;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -120,9 +122,7 @@ public abstract class StaticImportMemberFix<T extends PsiMember> 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());
@@ -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
@@ -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);
}
}
@@ -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));
}
@@ -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);
}
}
}
@@ -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<bound;...) or for(...;i<=bound;...)
if(!(forStatement.getCondition() instanceof PsiBinaryExpression)) return null;
@@ -1104,7 +1104,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
bound = condition.getLOperand();
ref = condition.getROperand();
} else return null;
if(bound == null || !(ref instanceof PsiReferenceExpression) || ((PsiReferenceExpression)ref).resolve() != counter) return null;
if(bound == null || !(ref instanceof PsiReferenceExpression) || !((PsiReferenceExpression)ref).isReferenceTo(counter)) return null;
if(!TypeConversionUtil.areTypesAssignmentCompatible(counter.getType(), bound)) return null;
return new CountingLoop(counter, initializer, bound, closed);
}
@@ -16,6 +16,7 @@
package com.intellij.refactoring.introduceVariable;
import com.intellij.codeInsight.CodeInsightUtil;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.codeInsight.lookup.LookupManager;
@@ -855,8 +856,9 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
settings.getSelectedType());
initializer = simplifyVariableInitializer(initializer, selectedType.getType());
PsiType type = stripNullabilityAnnotationsFromTargetType(selectedType, project);
PsiDeclarationStatement declaration = JavaPsiFacade.getInstance(project).getElementFactory()
.createVariableDeclarationStatement(settings.getEnteredName(), selectedType.getType(), initializer, container);
.createVariableDeclarationStatement(settings.getEnteredName(), type, initializer, container);
if (!isInsideLoop) {
declaration = addDeclaration(declaration, initializer);
LOG.assertTrue(expr1.isValid());
@@ -946,6 +948,26 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
};
}
private static PsiType stripNullabilityAnnotationsFromTargetType(SmartTypePointer selectedType, final Project project) {
PsiType type = selectedType.getType();
if (type == null) return null;
final PsiAnnotation[] annotations = type.getAnnotations();
type = type.annotate(new TypeAnnotationProvider() {
@NotNull
@Override
public PsiAnnotation[] getAnnotations() {
final NullableNotNullManager manager = NullableNotNullManager.getInstance(project);
final Set<String> 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();
@@ -906,6 +906,16 @@ public class LambdaUtil {
}
}
public static <T> T performWithLambdaTargetType(PsiLambdaExpression lambdaExpression, PsiType targetType, Producer<T> producer) {
try {
getFunctionalTypeMap().put(lambdaExpression, targetType);
return producer.produce();
}
finally {
getFunctionalTypeMap().remove(lambdaExpression);
}
}
/**
* Generate lambda text for single argument expression lambda
*
@@ -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;
@@ -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<Boolean> 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<VirtualFile, List<PsiFile>> myExternalAnnotations = ContainerUtil.createConcurrentWeakKeySoftValueMap();
private final ConcurrentMap<VirtualFile, List<PsiFile>> myExternalAnnotationsCache = ContainerUtil.createConcurrentWeakKeySoftValueMap();
private final Map<AnnotationData, AnnotationData> myAnnotationDataCache = new WeakKeyWeakValueHashMap<AnnotationData, AnnotationData>();
private final ConcurrentMap<PsiFile, Pair<MostlySingularMultiMap<String, AnnotationData>, Long>> myAnnotationFileToDataAndModStamp = ContainerUtil.createConcurrentSoftMap();
private final ConcurrentMap<PsiFile, Pair<MostlySingularMultiMap<String, AnnotationData>, Long>> myAnnotationFileToDataAndModStampCache = ContainerUtil.createConcurrentSoftMap();
public BaseExternalAnnotationsManager(@NotNull PsiManager psiManager) {
myPsiManager = psiManager;
@@ -166,7 +164,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
@NotNull
private MostlySingularMultiMap<String, AnnotationData> getDataFromFile(@NotNull PsiFile file) {
Pair<MostlySingularMultiMap<String, AnnotationData>, Long> cached = myAnnotationFileToDataAndModStamp.get(file);
Pair<MostlySingularMultiMap<String, AnnotationData>, 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<String, AnnotationData> 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<PsiFile> files = myExternalAnnotations.get(virtualFile);
final List<PsiFile> 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<VirtualFile> 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<PsiFile> annotationFiles) {
VirtualFile virtualFile = fromFile.getVirtualFile();
if (virtualFile != null) {
myExternalAnnotations.put(virtualFile, annotationFiles);
myExternalAnnotationsCache.put(virtualFile, annotationFiles);
}
}
@@ -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<String> 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<String> 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<PsiSubstitutor>() {
@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<PsiElement, InitialInferenceState> nestedStates = new LinkedHashMap<PsiElement, InitialInferenceState>();
@@ -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<Optional<Integer>> u) {}
}
@@ -33,7 +33,7 @@ class TypeArgsConsistency1 {
I<Integer> i1 = (i, j) -> i + j;
foo((i, j) -> i + j);
I<Integer> i2 =bar((i, j) -> i) ;
I<Integer> i3 = bar(<error descr="Bad return type in lambda expression: String cannot be converted to int">(i, j) -> "" + i + j</error>);
I<Integer> i3 = bar((i, j) -> <error descr="Bad return type in lambda expression: String cannot be converted to int">"" + i + j</error>);
}
}
@@ -66,9 +66,9 @@ class Test2 {
{
bar(x -> x);
bar1(x -> x);
bar2(1, <error descr="Bad return type in lambda expression: List<Integer> cannot be converted to Integer">x -> x</error>);
bar2("", <error descr="Bad return type in lambda expression: List<String> cannot be converted to String">x -> x</error>);
bar3(<error descr="Bad return type in lambda expression: List<String> cannot be converted to String">x -> x</error>, "");
bar2(1, x -> <error descr="Bad return type in lambda expression: List<Integer> cannot be converted to Integer">x</error>);
bar2("", x -> <error descr="Bad return type in lambda expression: List<String> cannot be converted to String">x</error>);
bar3(x -> <error descr="Bad return type in lambda expression: List<String> cannot be converted to String">x</error>, "");
}
}
@@ -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<String> consumer) {}
private void foo(List<String> descriptions) {
Collections.sort(descriptions, (o1, o2) -> {
final int elementsDiff = o1.length() - o2.length();
if (elementsDiff == 0) {
return <error descr="Bad return type in lambda expression: boolean cannot be converted to int">o1.equals(o2)</error>;
}
return -elementsDiff;
});
}
}
@@ -13,7 +13,7 @@ class Test {
}
void bar(C c) {
foo(c, <error descr="Bad return type in lambda expression: A cannot be converted to C">x -> x.f()</error>);
foo(c, x -> <error descr="Bad return type in lambda expression: A cannot be converted to C">x.f()</error>);
foo(c, x -> x);
}
@@ -1,5 +1,5 @@
// "Break string on '\n'" "false"
class A {
String s = "Hello!\n";
String s = "Hello!<caret>\n";
}
@@ -1,5 +1,5 @@
// "Break string on '\n'" "false"
class A {
String s = "Hello!\n\r";
String s = "Hello!<caret>\n\r";
}
@@ -7,6 +7,6 @@ class Example {
}
{
Function<String, String> r = (s) -> m(s, s);
Function<String, String> r = (s) -> <caret>m(s, s);
}
}
@@ -12,7 +12,7 @@ public class Test {
}
@SuppressWarnings("unchecked")
void foo() {
void f<caret>oo() {
foo(new ArrayList<String>()).addAll(foo1(new ArrayList<String>);
}
}
@@ -0,0 +1,12 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
String[] found = {"no"};
if (data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty())) {
found[0] = "yes";
}
}
}
@@ -0,0 +1,13 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
boolean find(List<String> data) {
if(data != null) {
return data.stream().map(String::trim).anyMatch(trimmed -> trimmed.startsWith("xyz"));
} else {
throw new IllegalArgumentException();
}
}
}
@@ -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<String, List<String>> 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);
}
}
@@ -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<String>> list) {
if(list == null) {
System.out.println("oops");
return "";
} else {
return list.stream().flatMap(Collection::stream).filter(string -> string.startsWith("ABC")).findFirst().orElse(null);
}
}
}
@@ -0,0 +1,16 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
String[] found = {"no"};
for(String str : da<caret>ta) {
String trimmed = str.trim();
if(!trimmed.isEmpty()) {
found[0] = "yes";
break;
}
}
}
}
@@ -0,0 +1,19 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
boolean find(List<String> data) {
if(data != null) {
for (String e : da<caret>ta) {
String trimmed = e.trim();
if (trimmed.startsWith("xyz")) {
return true;
}
}
} else {
throw new IllegalArgumentException();
}
return false;
}
}
@@ -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<String, List<String>> map) throws Exception {
int firstSize = 10;
System.out.println(firstSize);
firstSize = getInitialSize();
// loop
for(List<String> list : map.valu<caret>es()) {
if(list != null) {
firstSize = list.size();
// comment
break;
}
}
System.out.println(firstSize);
}
}
@@ -0,0 +1,21 @@
// "Replace with findFirst()" "true"
import java.util.List;
public class Main {
public static String find(List<List<String>> list) {
if(list == null) {
System.out.println("oops");
return "";
} else {
for (List<String> innerList : lis<caret>t) {
for (String string : innerList) {
if (string.startsWith("ABC")) {
return string;
}
}
}
}
return null;
}
}
@@ -1,5 +1,13 @@
// "Suppress for field" "false"
/** @noinspection ALL*/
class a {
static private String mm = "00";
static private String <caret>mm = "00";
// The "Convert to local" inspection should be reported here if not suppressed
static void test() {
mm = "1";
if(mm == "1") {
mm = "2";
}
}
}
@@ -4,6 +4,6 @@ public class Test {
{
int i = 0;
//noinspection SillyAssignment my very long comment
i = i;
i = <caret>i;
}
}
@@ -1,6 +1,6 @@
// "Suppress for method" "false"
/** @noinspection UNUSED_SYMBOL*/
class a {
private void run() {
private void <caret>run() {
}
}
@@ -4,7 +4,7 @@ import java.util.Optional;
public class Test {
void m(String ss) {
f(ss);
f(<caret>ss);
}
@@ -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 (<warning descr="Condition 'o != null' is always 'true'">o != null</warning>) {
System.out.println("fred");
}
}
}
@@ -1,4 +1,4 @@
// "Replace Stream.allMatch(x -> !(...)) with Stream.noneMatch(...)" "true"
// "Replace Stream.allMatch(x -> !(...)) with noneMatch(...)" "true"
import java.util.*;
@@ -1,4 +1,4 @@
// "Replace !Stream.allMatch(x -> !(...)) with Stream.anyMatch(...)" "true"
// "Replace !Stream.allMatch(x -> !(...)) with anyMatch(...)" "true"
import java.util.*;
@@ -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;
}
}
@@ -1,4 +1,4 @@
// "Replace !Stream.anyMatch(...) with Stream.noneMatch(...)" "true"
// "Replace !Stream.anyMatch(...) with noneMatch(...)" "true"
import java.util.*;
@@ -1,4 +1,4 @@
// "Replace !Stream.anyMatch(x -> !(...)) with Stream.allMatch(...)" "true"
// "Replace !Stream.anyMatch(x -> !(...)) with allMatch(...)" "true"
import java.util.*;
@@ -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;
}
}
@@ -1,4 +1,4 @@
// "Replace !Stream.noneMatch(...) with Stream.anyMatch(...)" "true"
// "Replace !Stream.noneMatch(...) with anyMatch(...)" "true"
import java.util.*;
@@ -1,4 +1,4 @@
// "Replace Stream.noneMatch(x -> !(...)) with Stream.allMatch(...)" "true"
// "Replace Stream.noneMatch(x -> !(...)) with allMatch(...)" "true"
import java.util.*;
@@ -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;
}
}
@@ -1,4 +1,4 @@
// "Replace Stream.allMatch(x -> !(...)) with Stream.noneMatch(...)" "false"
// "Replace Stream.allMatch(x -> !(...)) with noneMatch(...)" "false"
import java.util.*;
@@ -1,4 +1,4 @@
// "Replace Stream.allMatch(x -> !(...)) with Stream.noneMatch(...)" "true"
// "Replace Stream.allMatch(x -> !(...)) with noneMatch(...)" "true"
import java.util.*;
@@ -1,4 +1,4 @@
// "Replace !Stream.allMatch(x -> !(...)) with Stream.anyMatch(...)" "true"
// "Replace !Stream.allMatch(x -> !(...)) with anyMatch(...)" "true"
import java.util.*;
@@ -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).allM<caret>atch(d -> !Double.isNaN(d)))
return true;
}
}
@@ -1,4 +1,4 @@
// "Replace !Stream.anyMatch(...) with Stream.noneMatch(...)" "true"
// "Replace !Stream.anyMatch(...) with noneMatch(...)" "true"
import java.util.*;
@@ -1,4 +1,4 @@
// "Replace !Stream.anyMatch(x -> !(...)) with Stream.allMatch(...)" "false"
// "Replace !Stream.anyMatch(x -> !(...)) with allMatch(...)" "false"
import java.util.*;
@@ -1,4 +1,4 @@
// "Replace !Stream.anyMatch(x -> !(...)) with Stream.allMatch(...)" "true"
// "Replace !Stream.anyMatch(x -> !(...)) with allMatch(...)" "true"
import java.util.*;
@@ -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).anyM<caret>atch(i -> i > 0))
return true;
}
}
@@ -1,4 +1,4 @@
// "Replace !Stream.noneMatch(...) with Stream.anyMatch(...)" "true"
// "Replace !Stream.noneMatch(...) with anyMatch(...)" "true"
import java.util.*;
@@ -1,4 +1,4 @@
// "Replace Stream.noneMatch(x -> !(...)) with Stream.allMatch(...)" "true"
// "Replace Stream.noneMatch(x -> !(...)) with allMatch(...)" "true"
import java.util.*;
@@ -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).noneM<caret>atch(i -> i > 0))
return true;
}
}
@@ -5,7 +5,7 @@ class A {
void m() {
new StringTokenizer("asd", "\\\t\nqwerty!#2@$")
new StringTokenizer("asd", "\\\t\nqw<caret>erty!#2@$")
}
@@ -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("""
@@ -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);
}
}
@@ -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(); }
@@ -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(); }
@@ -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<VirtualFile> contains, @Nullable List<VirtualFile> doesntContain) {
assertIteratedContent(ModuleRootManager.getInstance(module).getFileIndex(), contains, doesntContain);
assertIteratedContent(myFileIndex, contains, doesntContain);
}
private static void assertIteratedContent(FileIndex fileIndex,
@Nullable List<VirtualFile> contains,
@Nullable List<VirtualFile> doesntContain) {
final Set<VirtualFile> 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,
@@ -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
@@ -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;
}
}
@@ -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);
@@ -0,0 +1,3 @@
class B {
A a;
}
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/exc/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/exc" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/sourceRootUnderExcluded.iml" filepath="$PROJECT_DIR$/sourceRootUnderExcluded.iml" />
</modules>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
@@ -0,0 +1,3 @@
class A {
B b;
}
@@ -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") {
@@ -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()));
}
@@ -65,7 +65,6 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(JdkPath)\include;$(JdkPath)\include\win32;$(IncludePath)</IncludePath>
<OutDir>..\..\..\bin\WinLauncher</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
@@ -80,7 +79,6 @@
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(JdkPath)\include;$(JdkPath)\include\win32;$(IncludePath)</IncludePath>
<TargetName>$(ProjectName)64</TargetName>
<OutDir>..\..\..\bin\WinLauncher</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@@ -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() {
@@ -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);
}
@@ -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);
}
@@ -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);
@@ -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<F extends Facet, C extends FacetConfiguration> 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<F extends Facet, C extends Fac
super(detectorId, detectorVersion);
}
@NotNull
public abstract FacetType<F, C> getFacetType();
/**
@@ -83,7 +87,10 @@ public abstract class FacetBasedFrameworkDetector<F extends Facet, C extends Fac
@Override
public FrameworkType getFrameworkType() {
return createFrameworkType(getFacetType());
FacetType<F, C> 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<F extends Facet, C extends Fac
private final FacetType<?, ?> myFacetType;
private final Icon myIcon;
public FacetBasedFrameworkType(FacetType<?, ?> facetType) {
public FacetBasedFrameworkType(@NotNull FacetType<?, ?> facetType) {
super(facetType.getStringId());
myFacetType = facetType;
final Icon icon = myFacetType.getIcon();
@@ -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 =
@@ -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<SourceFolder> mySelectedRoots = new ArrayList<>();
public List<ExcludeFolder> mySelectedExcludeRoots = new ArrayList<>();
@@ -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;
@@ -302,6 +302,8 @@ public class StartupManagerImpl extends StartupManagerEx {
}
public void startCacheUpdate() {
if (myProject.isDisposed()) return;
try {
DumbServiceImpl dumbService = DumbServiceImpl.getInstance(myProject);
@@ -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());
}
};
@@ -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");
}
@@ -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) {
@@ -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
@@ -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()};
}
}
@@ -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<VirtualFile> contentRoots = ApplicationManager.getApplication().runReadAction(new Computable<List<VirtualFile>>() {
@Override
public List<VirtualFile> compute() {
if (myModule.isDisposed()) return Collections.emptyList();
List<VirtualFile> result = ContainerUtil.newArrayList();
for (VirtualFile contentRoot : ModuleRootManager.getInstance(myModule).getContentRoots()) {
VirtualFile parent = contentRoot.getParent();
final Set<VirtualFile> contentRoots = ApplicationManager.getApplication().runReadAction((Computable<Set<VirtualFile>>)() -> {
if (myModule.isDisposed()) return Collections.emptySet();
Set<VirtualFile> 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);
@@ -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<Module[]>() {
@Override
public Module[] compute() {
return ModuleManager.getInstance(myProject).getModules();
}
});
Module[] modules = ApplicationManager.getApplication().runReadAction((Computable<Module[]>)() -> 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<VirtualFile> getRootsToIterate(final Module module) {
return ApplicationManager.getApplication().runReadAction(new Computable<List<VirtualFile>>() {
@Override
public List<VirtualFile> compute() {
if (module.isDisposed()) return Collections.emptyList();
private Set<VirtualFile> getRootsToIterate(final Module module) {
return ApplicationManager.getApplication().runReadAction((Computable<Set<VirtualFile>>)() -> {
if (module.isDisposed()) return Collections.emptySet();
List<VirtualFile> result = ContainerUtil.newArrayList();
for (VirtualFile contentRoot : ModuleRootManager.getInstance(module).getContentRoots()) {
DirectoryInfo info = getInfoForFileOrDirectory(contentRoot);
Set<VirtualFile> 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;
});
}
@@ -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<VirtualFile> hierarchy) {
private VirtualFile findNearestContentRoot(@NotNull List<VirtualFile> hierarchy) {
Collection<Module> 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<Module> 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<DirectoryInfo, String> calcDirectoryInfo(@NotNull final VirtualFile root,
@NotNull final List<VirtualFile> 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;

Some files were not shown because too many files have changed in this diff Show More