@Override public void visitUnaryExpression(GrUnaryExpression expression) { final GrExpression operand = expression.getOperand(); if (operand == null) return; if (expression.getOperationTokenType() != mLNOT) { operand.accept(this); visitCall(expression); return; } ConditionInstruction cond = new ConditionInstruction(expression); addNodeAndCheckPending(cond); registerCondition(cond); operand.accept(this); visitCall(expression); myConditions.removeFirstOccurrence(cond); List<GotoInstruction> negations = collectAndRemoveAllPendingNegations(expression); InstructionImpl head = myHead; addNodeAndCheckPending(new PositiveGotoInstruction(expression, cond)); handlePossibleReturn(expression); addPendingEdge(expression, myHead); if (negations.isEmpty()) { myHead = head; } else { myHead = reduceAllNegationsIntoInstruction(expression, negations); } }
@Nullable public static PsiType getExpectedClosureReturnType(GrClosableBlock closure) { final Set<PsiType> expectedTypes = getDefaultExpectedTypes(closure); List<PsiType> expectedReturnTypes = new ArrayList<PsiType>(); for (PsiType expectedType : expectedTypes) { if (!(expectedType instanceof PsiClassType)) return null; final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType) expectedType).resolveGenerics(); final PsiClass resolved = resolveResult.getElement(); if (resolved == null || !(GroovyCommonClassNames.GROOVY_LANG_CLOSURE.equals(resolved.getQualifiedName()))) return null; final PsiTypeParameter[] typeParameters = resolved.getTypeParameters(); if (typeParameters.length != 1) return null; final PsiTypeParameter expected = typeParameters[0]; final PsiType expectedReturnType = resolveResult.getSubstitutor().substitute(expected); if (expectedReturnType == PsiType.VOID || expectedReturnType == null) return null; expectedReturnTypes.add(expectedReturnType); } return TypesUtil.getLeastUpperBoundNullable(expectedReturnTypes, closure.getManager()); }
public void visitAssertStatement(GrAssertStatement assertStatement) { final InstructionImpl assertInstruction = startNode(assertStatement); final GrExpression assertion = assertStatement.getAssertion(); if (assertion != null) { assertion.accept(this); InstructionImpl positiveHead = myHead; List<GotoInstruction> negations = collectAndRemoveAllPendingNegations(assertStatement); if (!negations.isEmpty()) { interruptFlow(); reduceAllNegationsIntoInstruction(assertStatement, negations); } GrExpression errorMessage = assertStatement.getErrorMessage(); if (errorMessage != null) { errorMessage.accept(this); } addNode(new ThrowingInstruction(assertStatement)); final PsiType type = TypesUtil.createTypeByFQClassName( CommonClassNames.JAVA_LANG_ASSERTION_ERROR, assertStatement); ExceptionInfo info = findCatch(type); if (info != null) { info.myThrowers.add(myHead); } else { addPendingEdge(null, myHead); } myHead = positiveHead; } finishNode(assertInstruction); }
private List<GotoInstruction> collectAndRemoveAllPendingNegations(GroovyPsiElement currentScope) { List<GotoInstruction> negations = new ArrayList<GotoInstruction>(); for (Iterator<Pair<InstructionImpl, GroovyPsiElement>> iterator = myPending.iterator(); iterator.hasNext(); ) { Pair<InstructionImpl, GroovyPsiElement> pair = iterator.next(); InstructionImpl instruction = pair.first; GroovyPsiElement scope = pair.second; if (!PsiTreeUtil.isAncestor(scope, currentScope, true) && instruction instanceof GotoInstruction) { negations.add((GotoInstruction) instruction); iterator.remove(); } } return negations; }
@Nullable private InstructionImpl reduceAllNegationsIntoInstruction( GroovyPsiElement currentScope, List<? extends GotoInstruction> negations) { if (negations.size() > 1) { InstructionImpl instruction = addNode(new InstructionImpl(currentScope)); for (GotoInstruction negation : negations) { addEdge(negation, instruction); } return instruction; } else if (negations.size() == 1) { GotoInstruction instruction = negations.get(0); myHead = instruction; return instruction; } return null; }
@Override public void visitSwitchStatement(GrSwitchStatement switchStatement) { final GrCaseSection[] sections = switchStatement.getCaseSections(); List<PsiType> types = new ArrayList<PsiType>(sections.length); for (GrCaseSection section : sections) { final GrExpression value = section.getCaseLabel().getValue(); final PsiType type = value != null ? value.getType() : null; if (type != null) types.add(type); } final PsiType upperBoundNullable = TypesUtil.getLeastUpperBoundNullable(types, switchStatement.getManager()); if (upperBoundNullable == null) return; myResult = new TypeConstraint[] {SubtypeConstraint.create(upperBoundNullable)}; }
private void addConstraintsFromMap( List<TypeConstraint> constraints, Map<GrExpression, Pair<PsiParameter, PsiType>> map) { if (map == null) return; final Pair<PsiParameter, PsiType> pair = map.get(myExpression); if (pair == null) return; final PsiType type = pair.second; if (type == null) return; constraints.add(SubtypeConstraint.create(type)); if (type instanceof PsiArrayType && pair.first.isVarArgs()) { constraints.add(SubtypeConstraint.create(((PsiArrayType) type).getComponentType())); } }
private <T extends InstructionImpl> T addNode(T instruction) { instruction.setNumber(myInstructionNumber++); myInstructions.add(instruction); if (myHead != null) { addEdge(myHead, instruction); } myHead = instruction; return instruction; }
public void visitArgumentList(GrArgumentList list) { List<TypeConstraint> constraints = new ArrayList<TypeConstraint>(); for (GroovyResolveResult variant : ResolveUtil.getCallVariants(list)) { final Map<GrExpression, Pair<PsiParameter, PsiType>> map = GrClosureSignatureUtil.mapArgumentsToParameters( variant, list, true, true, list.getNamedArguments(), list.getExpressionArguments(), GrClosableBlock.EMPTY_ARRAY); addConstraintsFromMap(constraints, map); } if (!constraints.isEmpty()) { myResult = constraints.toArray(new TypeConstraint[constraints.size()]); } }
@Override public void visitListOrMap(GrListOrMap listOrMap) { if (listOrMap.isMap()) return; final TypeConstraint[] constraints = calculateTypeConstraints(listOrMap); List<PsiType> result = new ArrayList<PsiType>(constraints.length); for (TypeConstraint constraint : constraints) { if (constraint instanceof SubtypeConstraint) { final PsiType type = constraint.getType(); final PsiType iterable = com.intellij.psi.util.PsiUtil.extractIterableTypeParameter(type, true); if (iterable != null) { result.add(iterable); } } } if (result.size() == 0) { myResult = TypeConstraint.EMPTY_ARRAY; } else { myResult = new TypeConstraint[result.size()]; for (int i = 0; i < result.size(); i++) { final PsiType type = result.get(i); if (type != null) { myResult[i] = SubtypeConstraint.create(type); } } } }
public void visitMethodCallExpression(GrMethodCallExpression methodCall) { final GrExpression invokedExpression = methodCall.getInvokedExpression(); if (myExpression.equals(invokedExpression)) { myResult = new TypeConstraint[] { SubtypeConstraint.create(GroovyCommonClassNames.GROOVY_LANG_CLOSURE, methodCall) }; return; } final GrClosableBlock[] closureArgs = methodCall.getClosureArguments(); //noinspection SuspiciousMethodCalls final int closureIndex = Arrays.asList(closureArgs).indexOf(myExpression); if (closureIndex >= 0) { List<TypeConstraint> constraints = new ArrayList<TypeConstraint>(); for (GroovyResolveResult variant : ResolveUtil.getCallVariants(myExpression)) { final GrArgumentList argumentList = methodCall.getArgumentList(); final GrNamedArgument[] namedArgs = argumentList == null ? GrNamedArgument.EMPTY_ARRAY : argumentList.getNamedArguments(); final GrExpression[] expressionArgs = argumentList == null ? GrExpression.EMPTY_ARRAY : argumentList.getExpressionArguments(); try { final Map<GrExpression, Pair<PsiParameter, PsiType>> map = GrClosureSignatureUtil.mapArgumentsToParameters( variant, methodCall, true, true, namedArgs, expressionArgs, closureArgs); addConstraintsFromMap(constraints, map); } catch (RuntimeException e) { LOG.error( "call: " + methodCall.getText() + "\nsymbol: " + variant.getElement().getText(), e); } } if (!constraints.isEmpty()) { myResult = constraints.toArray(new TypeConstraint[constraints.size()]); } } }
public Instruction[] buildControlFlow(GroovyPsiElement scope) { myInstructions = new ArrayList<InstructionImpl>(); myProcessingStack = new ArrayDeque<InstructionImpl>(); myCaughtExceptionInfos = new ArrayDeque<ExceptionInfo>(); myConditions = new ArrayDeque<ConditionInstruction>(); myFinallyCount = 0; myPending = new ArrayList<Pair<InstructionImpl, GroovyPsiElement>>(); myInstructionNumber = 0; myScope = scope; startNode(null); if (scope instanceof GrClosableBlock) { buildFlowForClosure((GrClosableBlock) scope); } else { scope.accept(this); } final InstructionImpl end = startNode(null); checkPending(end); // collect return edges return assertValidPsi(myInstructions.toArray(new Instruction[myInstructions.size()])); }
@Override public void visitBinaryExpression(GrBinaryExpression expression) { final GrExpression left = expression.getLeftOperand(); final GrExpression right = expression.getRightOperand(); final IElementType opType = expression.getOperationTokenType(); if (ControlFlowBuilderUtil.isInstanceOfBinary(expression)) { expression.getLeftOperand().accept(this); processInstanceOf(expression); return; } if (opType != mLOR && opType != mLAND && opType != kIN) { left.accept(this); if (right != null) { right.accept(this); } visitCall(expression); return; } ConditionInstruction condition = new ConditionInstruction(expression); addNodeAndCheckPending(condition); registerCondition(condition); left.accept(this); if (right == null) return; final List<GotoInstruction> negations = collectAndRemoveAllPendingNegations(expression); visitCall(expression); if (opType == mLAND) { InstructionImpl head = myHead; if (negations.isEmpty()) { addNode(new NegatingGotoInstruction(expression, condition)); handlePossibleReturn(expression); addPendingEdge(expression, myHead); } else { for (GotoInstruction negation : negations) { myHead = negation; handlePossibleReturn(expression); addPendingEdge(expression, myHead); } } myHead = head; } else /*if (opType == mLOR)*/ { final InstructionImpl instruction = addNodeAndCheckPending( new InstructionImpl(expression)); // collect all pending edges from left argument handlePossibleReturn(expression); addPendingEdge(expression, myHead); myHead = instruction; InstructionImpl head = reduceAllNegationsIntoInstruction(expression, negations); if (head != null) myHead = head; // addNode(new NegatingGotoInstruction(expression, myInstructionNumber++, condition)); } myConditions.removeFirstOccurrence(condition); right.accept(this); }