mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
+1
@@ -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);
|
||||
}
|
||||
|
||||
+51
-63
@@ -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
|
||||
|
||||
+1
-1
@@ -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")
|
||||
|
||||
+22
-10
@@ -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
|
||||
|
||||
+77
-278
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
+6
-13
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
+12
-12
@@ -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;
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+1
-1
@@ -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 &&
|
||||
|
||||
+4
-4
@@ -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;
|
||||
|
||||
+7
-5
@@ -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();
|
||||
}
|
||||
|
||||
+5
-5
@@ -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());
|
||||
|
||||
+3
-4
@@ -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
|
||||
|
||||
+13
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+16
-2
@@ -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));
|
||||
}
|
||||
|
||||
+28
-21
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -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);
|
||||
}
|
||||
|
||||
+23
-1
@@ -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;
|
||||
|
||||
|
||||
+10
-12
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+78
-12
@@ -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>();
|
||||
|
||||
+24
@@ -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) {}
|
||||
}
|
||||
+1
-1
@@ -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>);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -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>, "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-1
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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
-1
@@ -1,5 +1,5 @@
|
||||
// "Break string on '\n'" "false"
|
||||
|
||||
class A {
|
||||
String s = "Hello!\n";
|
||||
String s = "Hello!<caret>\n";
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// "Break string on '\n'" "false"
|
||||
|
||||
class A {
|
||||
String s = "Hello!\n\r";
|
||||
String s = "Hello!<caret>\n\r";
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,6 +7,6 @@ class Example {
|
||||
}
|
||||
|
||||
{
|
||||
Function<String, String> r = (s) -> m(s, s);
|
||||
Function<String, String> r = (s) -> <caret>m(s, s);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@ public class Test {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
void foo() {
|
||||
void f<caret>oo() {
|
||||
foo(new ArrayList<String>()).addAll(foo1(new ArrayList<String>);
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -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);
|
||||
}
|
||||
}
|
||||
+15
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -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;
|
||||
}
|
||||
}
|
||||
+25
@@ -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);
|
||||
}
|
||||
}
|
||||
+21
@@ -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;
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,6 +4,6 @@ public class Test {
|
||||
{
|
||||
int i = 0;
|
||||
//noinspection SillyAssignment my very long comment
|
||||
i = i;
|
||||
i = <caret>i;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// "Suppress for method" "false"
|
||||
/** @noinspection UNUSED_SYMBOL*/
|
||||
class a {
|
||||
private void run() {
|
||||
private void <caret>run() {
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace Stream.allMatch(x -> !(...)) with Stream.noneMatch(...)" "true"
|
||||
// "Replace Stream.allMatch(x -> !(...)) with noneMatch(...)" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace !Stream.allMatch(x -> !(...)) with Stream.anyMatch(...)" "true"
|
||||
// "Replace !Stream.allMatch(x -> !(...)) with anyMatch(...)" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
+10
@@ -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
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace !Stream.anyMatch(x -> !(...)) with Stream.allMatch(...)" "true"
|
||||
// "Replace !Stream.anyMatch(x -> !(...)) with allMatch(...)" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
+10
@@ -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
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace Stream.noneMatch(x -> !(...)) with Stream.allMatch(...)" "true"
|
||||
// "Replace Stream.noneMatch(x -> !(...)) with allMatch(...)" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
+10
@@ -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
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace Stream.allMatch(x -> !(...)) with Stream.noneMatch(...)" "true"
|
||||
// "Replace Stream.allMatch(x -> !(...)) with noneMatch(...)" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace !Stream.allMatch(x -> !(...)) with Stream.anyMatch(...)" "true"
|
||||
// "Replace !Stream.allMatch(x -> !(...)) with anyMatch(...)" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
+10
@@ -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
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace !Stream.anyMatch(x -> !(...)) with Stream.allMatch(...)" "false"
|
||||
// "Replace !Stream.anyMatch(x -> !(...)) with allMatch(...)" "false"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace !Stream.anyMatch(x -> !(...)) with Stream.allMatch(...)" "true"
|
||||
// "Replace !Stream.anyMatch(x -> !(...)) with allMatch(...)" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
+10
@@ -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
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace Stream.noneMatch(x -> !(...)) with Stream.allMatch(...)" "true"
|
||||
// "Replace Stream.noneMatch(x -> !(...)) with allMatch(...)" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
+10
@@ -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;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@ class A {
|
||||
|
||||
void m() {
|
||||
|
||||
new StringTokenizer("asd", "\\\t\nqwerty!#2@$")
|
||||
new StringTokenizer("asd", "\\\t\nqw<caret>erty!#2@$")
|
||||
|
||||
}
|
||||
|
||||
|
||||
+16
@@ -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("""
|
||||
|
||||
+4
-4
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-2
@@ -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,
|
||||
|
||||
+1
-1
@@ -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() {
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+9
-2
@@ -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();
|
||||
|
||||
+6
-1
@@ -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 =
|
||||
|
||||
+12
-12
@@ -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<>();
|
||||
|
||||
+4
-7
@@ -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);
|
||||
|
||||
|
||||
+2
-2
@@ -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()};
|
||||
}
|
||||
}
|
||||
|
||||
+15
-11
@@ -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);
|
||||
|
||||
+13
-18
@@ -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
Reference in New Issue
Block a user