public LambdaMethodFilter(PsiLambdaExpression lambda, int expressionOrdinal, Range<Integer> callingExpressionLines) { myLambdaOrdinal = expressionOrdinal; myCallingExpressionLines = callingExpressionLines; SourcePosition firstStatementPosition = null; SourcePosition lastStatementPosition = null; final PsiElement body = lambda.getBody(); if (body instanceof PsiCodeBlock) { final PsiStatement[] statements = ((PsiCodeBlock)body).getStatements(); if (statements.length > 0) { firstStatementPosition = SourcePosition.createFromElement(statements[0]); if (firstStatementPosition != null) { final PsiStatement lastStatement = statements[statements.length - 1]; lastStatementPosition = SourcePosition.createFromOffset(firstStatementPosition.getFile(), lastStatement.getTextRange().getEndOffset()); } } } else if (body != null) { firstStatementPosition = SourcePosition.createFromElement(body); } myFirstStatementPosition = firstStatementPosition; myLastStatementLine = lastStatementPosition != null ? lastStatementPosition.getLine() : -1; }
public void verifyVariable(@NotNull final PsiLocalVariable psiLocalVariable, @NotNull final ProblemsHolder holder) { boolean isVal = isSameName(psiLocalVariable.getTypeElement().getText()); boolean isVar = isVar(psiLocalVariable.getTypeElement().getText()); final String ann = isVal ? "val" : "var"; if (isVal || isVar) { final PsiExpression initializer = psiLocalVariable.getInitializer(); if (initializer == null) { holder.registerProblem(psiLocalVariable, "'" + ann + "' on a local variable requires an initializer expression", ProblemHighlightType.ERROR); } else if (initializer instanceof PsiArrayInitializerExpression) { holder.registerProblem(psiLocalVariable, "'" + ann + "' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })", ProblemHighlightType.ERROR); } else if (initializer instanceof PsiLambdaExpression) { holder.registerProblem(psiLocalVariable, "'" + ann + "' is not allowed with lambda expressions.", ProblemHighlightType.ERROR); } else if (isVal) { final PsiElement typeParentParent = psiLocalVariable.getParent(); if (typeParentParent instanceof PsiDeclarationStatement && typeParentParent.getParent() instanceof PsiForStatement) { holder.registerProblem(psiLocalVariable, "'" + ann + "' is not allowed in old-style for loops", ProblemHighlightType.ERROR); } } } }
@Nullable private PsiVariable checkCodeFragment(PsiElement refElement) { PsiElement codeFragment = ControlFlowUtil.findCodeFragment(refElement); if (refElement instanceof PsiParameter) { final PsiElement declarationScope = ((PsiParameter)refElement).getDeclarationScope(); if (declarationScope instanceof PsiMethod) { codeFragment = ((PsiMethod)declarationScope).getBody(); } else if (declarationScope instanceof PsiLambdaExpression) { codeFragment = ((PsiLambdaExpression)declarationScope).getBody(); } } if (codeFragment == null) return null; if (myCodeFragment.getContainingFile() == codeFragment.getContainingFile() && // in order for jsp includes to work !myCodeFragment.equals(codeFragment)) { return null; } return (PsiVariable)refElement; }
public static String getLambdaPrefix(@NotNull PsiLambdaExpression lambdaExpression) { PsiMember member = PsiTreeUtil.getParentOfType(lambdaExpression, PsiMethod.class, PsiClass.class, PsiField.class); final String methodPrefix; if(member instanceof PsiMethod) { methodPrefix = member.getContainingClass() instanceof PsiAnonymousClass ? "" : "$" + member.getName(); } else if(member instanceof PsiField && member.getContainingClass() instanceof PsiAnonymousClass) { methodPrefix = ""; } else { //inside class initializer everywhere or field in a named class methodPrefix = "$new"; } return methodPrefix; }
@NotNull protected DfaInstructionState[] acceptInstruction(@NotNull InstructionVisitor visitor, @NotNull DfaInstructionState instructionState) { Instruction instruction = instructionState.getInstruction(); DfaInstructionState[] states = instruction.accept(this, instructionState.getMemoryState(), visitor); PsiElement closure = DfaUtil.getClosureInside(instruction); if(closure instanceof PsiClass) { registerNestedClosures(instructionState, (PsiClass) closure); } else if(closure instanceof PsiLambdaExpression) { registerNestedClosures(instructionState, (PsiLambdaExpression) closure); } return states; }
private static void invokeAndUnwrapOptional(CFGBuilder builder, int argCount, PsiExpression function) { PsiLambdaExpression lambda = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(function), PsiLambdaExpression.class); if(lambda != null) { PsiParameter[] parameters = lambda.getParameterList().getParameters(); PsiExpression lambdaBody = LambdaUtil.extractSingleExpressionFromBody(lambda.getBody()); if(parameters.length == argCount && lambdaBody != null) { StreamEx.ofReversed(parameters).forEach(p -> builder.assignTo(p).pop()); if(pushOptionalValue(builder, lambdaBody, lambdaBody, NullabilityProblem.nullableFunctionReturn)) { return; } // Restore stack for common invokeFunction StreamEx.of(parameters).forEach(p -> builder.push(builder.getFactory().getVarFactory().createVariableValue(p, false))); } } builder.evaluateFunction(function).invokeFunction(argCount, function, Nullness.NOT_NULL).pop().pushUnknown(); }
@Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { PsiMethodReferenceExpression methodRef = ObjectUtils.tryCast(descriptor.getStartElement(), PsiMethodReferenceExpression.class); if(methodRef == null) { return; } PsiLambdaExpression lambdaExpression = LambdaRefactoringUtil.convertMethodReferenceToLambda(methodRef, true, true); if(lambdaExpression == null) { return; } PsiElement body = lambdaExpression.getBody(); if(body == null) { return; } body.replace(JavaPsiFacade.getElementFactory(project).createExpressionFromText(myValue, lambdaExpression)); }
public static boolean isRecursiveMethodCall(@NotNull PsiMethodCallExpression methodCall) { final PsiExpression qualifier = methodCall.getMethodExpression().getQualifierExpression(); if(qualifier != null && !(qualifier instanceof PsiThisExpression)) { return false; } final PsiMethod method = PsiTreeUtil.getParentOfType(methodCall, PsiMethod.class, true, PsiLambdaExpression.class, PsiClass.class); if(method == null || !method.getName().equals(methodCall.getMethodExpression().getReferenceName())) { return false; } return Comparing.equal(method, methodCall.resolveMethod()); }
@Override public String getPresentableText() { if(myName != null) { return myName; } final PsiLambdaExpression element = getElement(); if(element != null) { myName = PsiLambdaNameHelper.getVMName(element); return myName; } return "Lambda"; }
@Override public String getLocationString() { if(myFunctionalName == null) { PsiLambdaExpression lambdaExpression = getElement(); if(lambdaExpression != null) { final PsiType interfaceType = lambdaExpression.getFunctionalInterfaceType(); if(interfaceType != null) { myFunctionalName = interfaceType.getPresentableText(); } } } return myFunctionalName; }
@Override @NotNull public String getTextFor(SmartStepTarget value) { final String label = value.getLabel(); final String formatted; if(value instanceof MethodSmartStepTarget) { final PsiMethod method = ((MethodSmartStepTarget) value).getMethod(); formatted = PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_PARAMETERS, PsiFormatUtil.SHOW_TYPE, 999); } else if(value instanceof LambdaSmartStepTarget) { final PsiLambdaExpression lambda = ((LambdaSmartStepTarget) value).getLambda(); formatted = PsiFormatUtil.formatType(lambda.getType(), 0, PsiSubstitutor.EMPTY); } else { formatted = ""; } return label != null ? label + formatted : formatted; }
@Nullable public PsiElement getContainingMethod(@NotNull LineBreakpoint<?> breakpoint) { SourcePosition position = breakpoint.getSourcePosition(); if(position == null) { return null; } JavaBreakpointProperties properties = breakpoint.getProperties(); if(properties instanceof JavaLineBreakpointProperties && !(breakpoint instanceof RunToCursorBreakpoint)) { Integer ordinal = ((JavaLineBreakpointProperties) properties).getLambdaOrdinal(); if(ordinal > -1) { List<PsiLambdaExpression> lambdas = DebuggerUtilsEx.collectLambdas(position, true); if(ordinal < lambdas.size()) { return lambdas.get(ordinal); } } } return DebuggerUtilsEx.getContainingMethod(position); }
@Override public List<Class<? extends PsiElement>> getApplicablePsiTypes() { List<Class<? extends PsiElement>> res = new ArrayList<>(); res.add(PsiLambdaExpression.class); return res; //return Collections.singletonList(PsiLambdaExpression.class); }
@Override public JavaElementVisitor createPsiVisitor(@NonNull final JavaContext context) { return new JavaElementVisitor() { @Override public void visitLambdaExpression(PsiLambdaExpression expression) { if (!(expression.getParent() instanceof PsiExpressionList)) { return; } PsiExpressionList exprList = (PsiExpressionList) expression.getParent(); if (!(exprList.getParent() instanceof PsiMethodCallExpression)) { return; } PsiMethodCallExpression call = (PsiMethodCallExpression) exprList.getParent(); if (call.getType() == null) { return; } String callType = call.getType().getCanonicalText(); if (!callType.startsWith("de.mobilej.thinr.Thinr")) { return; } markLeakSuspects(expression, expression, context); } }; }
@Override public TextRange getHighlightRange(SourcePosition sourcePosition) { PsiElement method = DebuggerUtilsEx.getContainingMethod(sourcePosition); if (method instanceof PsiLambdaExpression) { return method.getTextRange(); } return null; }
@Nullable private Collection<DfaMemoryState> createInitialStates(@NotNull PsiElement psiBlock, @NotNull InstructionVisitor visitor) { PsiElement container = PsiTreeUtil.getParentOfType(psiBlock, PsiClass.class, PsiLambdaExpression.class); if(container != null && (!(container instanceof PsiClass) || PsiUtil.isLocalOrAnonymousClass((PsiClass) container))) { PsiElement block = DfaPsiUtil.getTopmostBlockInSameClass(container.getParent()); if(block != null) { final RunnerResult result; try { myInlining = false; result = analyzeMethod(block, visitor); } finally { myInlining = true; } if(result == RunnerResult.OK) { final Collection<DfaMemoryState> closureStates = myNestedClosures.get(DfaPsiUtil.getTopmostBlockInSameClass(psiBlock)); if(!closureStates.isEmpty()) { return closureStates; } } return null; } } return Collections.singletonList(createMemoryState()); }
private void registerNestedClosures(@NotNull DfaInstructionState instructionState, @NotNull PsiLambdaExpression expr) { DfaMemoryState state = instructionState.getMemoryState(); PsiElement body = expr.getBody(); if(body != null) { createClosureState(body, state); } }
@Override public boolean tryInlineCall(@NotNull CFGBuilder builder, @NotNull PsiMethodCallExpression call) { PsiMethod method = call.resolveMethod(); if(method == null || method != LambdaUtil.getFunctionalInterfaceMethod(method.getContainingClass())) { return false; } PsiTypeCastExpression typeCastExpression = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(call.getMethodExpression().getQualifierExpression()), PsiTypeCastExpression.class); if(typeCastExpression == null) { return false; } PsiLambdaExpression lambda = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(typeCastExpression.getOperand()), PsiLambdaExpression.class); if(lambda == null || lambda.getBody() == null) { return false; } if(method.isVarArgs()) { return false; // TODO: support varargs } PsiExpression[] args = call.getArgumentList().getExpressions(); PsiParameter[] parameters = lambda.getParameterList().getParameters(); if(args.length != parameters.length) { return false; } EntryStream.zip(args, parameters).forKeyValue((arg, parameter) -> builder.pushVariable(parameter).pushExpression(arg).boxUnbox(arg, parameter.getType()).assign().pop()); builder.inlineLambda(lambda, Nullness.UNKNOWN); return true; }
@NotNull @Override public List<JavaLambdaTreeElement> provideNodes(@NotNull TreeElement node) { if(node instanceof PsiMethodTreeElement || node instanceof PsiFieldTreeElement || node instanceof ClassInitializerTreeElement || node instanceof JavaLambdaTreeElement) { final PsiElement el = ((PsiTreeElementBase) node).getElement(); if(el != null) { final List<JavaLambdaTreeElement> result = new ArrayList<>(); el.accept(new JavaRecursiveElementVisitor() { @Override public void visitLambdaExpression(PsiLambdaExpression expression) { super.visitLambdaExpression(expression); result.add(new JavaLambdaTreeElement(expression)); } @Override public void visitClass(PsiClass aClass) { //stop at class level } }); return result; } } return Collections.emptyList(); }
@Override @NotNull protected Class[] getSuitableClasses() { return new Class[]{ PsiClass.class, PsiMethod.class, PsiField.class, PsiLambdaExpression.class, PsiJavaFile.class }; }
public LambdaMethodFilter(PsiLambdaExpression lambda, int expressionOrdinal, Range<Integer> callingExpressionLines) { myLambdaOrdinal = expressionOrdinal; myCallingExpressionLines = callingExpressionLines; SourcePosition firstStatementPosition = null; SourcePosition lastStatementPosition = null; final PsiElement body = lambda.getBody(); if(body instanceof PsiCodeBlock) { final PsiStatement[] statements = ((PsiCodeBlock) body).getStatements(); if(statements.length > 0) { firstStatementPosition = SourcePosition.createFromElement(statements[0]); if(firstStatementPosition != null) { final PsiStatement lastStatement = statements[statements.length - 1]; lastStatementPosition = SourcePosition.createFromOffset(firstStatementPosition.getFile(), lastStatement.getTextRange().getEndOffset()); } } } else if(body != null) { firstStatementPosition = SourcePosition.createFromElement(body); } myFirstStatementPosition = firstStatementPosition; myLastStatementLine = lastStatementPosition != null ? lastStatementPosition.getLine() : -1; }
@Override public TextRange getHighlightRange(SourcePosition sourcePosition) { PsiElement method = DebuggerUtilsEx.getContainingMethod(sourcePosition); if(method instanceof PsiLambdaExpression) { return method.getTextRange(); } return null; }
public LambdaSmartStepTarget( @NotNull PsiLambdaExpression lambda, @Nullable String label, @Nullable PsiElement highlightElement, int ordinal, Range<Integer> lines) { super(label, highlightElement, true, lines); myLambda = lambda; myOrdinal = ordinal; }
@Override public void visitLambdaExpression(PsiLambdaExpression expression) { mVisitor.report("PsiLambdaExpression", expression.getText(), expression); super.visitExpression(expression); }
public LambdaSmartStepTarget(@NotNull PsiLambdaExpression lambda, @Nullable String label, @Nullable PsiElement highlightElement, int ordinal, Range<Integer> lines) { super(label, highlightElement, true, lines); myLambda = lambda; myOrdinal = ordinal; }
public PsiLambdaExpression getLambda() { return myLambda; }
public LambdaInstruction(PsiLambdaExpression lambdaExpression) { myLambdaExpression = lambdaExpression; }
public PsiLambdaExpression getLambdaExpression() { return myLambdaExpression; }
@Override protected boolean isCorrectScope(PsiElement declarationScope) { return declarationScope instanceof PsiLambdaExpression; }
public static <T extends PsiStatement> boolean isNestedStatement(@NotNull T statement, @NotNull Class<T> aClass) { return PsiTreeUtil.getParentOfType(statement, aClass, true, PsiClass.class, PsiLambdaExpression.class) != null; }
@Override public PsiLambdaExpression createPsi(@NotNull ASTNode node) { return new PsiLambdaExpressionImpl(node); }
@Override public PsiLambdaExpression createPsi(@NotNull FunctionalExpressionStub<PsiLambdaExpression> stub) { return new PsiLambdaExpressionImpl(stub); }
private static boolean hasClassesInside(@Nullable PsiElement element) { return !SyntaxTraverser.psiTraverser(element).traverse().filter(Conditions.instanceOf(PsiClass.class, PsiLambdaExpression.class)).isEmpty(); }
public JavaLambdaTreeElement(PsiLambdaExpression lambdaExpression) { super(false, lambdaExpression); }
public static void stripLambdaParameterParentheses(PsiParameterList element) { final PsiElementFactory factory = JavaPsiFacade.getElementFactory(element.getProject()); final String text = element.getParameters()[0].getName() + "->{}"; final PsiLambdaExpression expression = (PsiLambdaExpression)factory.createExpressionFromText(text, element); element.replace(expression.getParameterList()); }
public boolean locationMatches(DebugProcessImpl process, Location location, @NotNull EvaluatingComputable<ObjectReference> thisProvider) throws EvaluateException { Method method = location.method(); String name = method.name(); if(!myTargetMethodName.equals(name)) { if(DebuggerUtilsEx.isLambdaName(name)) { SourcePosition position = process.getPositionManager().getSourcePosition(location); return ReadAction.compute(() -> { PsiElement psiMethod = DebuggerUtilsEx.getContainingMethod(position); if(psiMethod instanceof PsiLambdaExpression) { PsiType type = ((PsiLambdaExpression) psiMethod).getFunctionalInterfaceType(); PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(type); if(type != null && interfaceMethod != null && myTargetMethodName.equals(interfaceMethod.getName())) { try { return InheritanceUtil.isInheritor(type, myDeclaringClassName.getName(process).replace('$', '.')); } catch(EvaluateException e) { LOG.info(e); } } } return false; }); } return false; } if(myTargetMethodSignature != null && !signatureMatches(method, myTargetMethodSignature.getName(process))) { return false; } if(method.isBridge()) { // skip bridge methods return false; } String declaringClassNameName = myDeclaringClassName.getName(process); boolean res = DebuggerUtilsEx.isAssignableFrom(declaringClassNameName, location.declaringType()); if(!res && !method.isStatic()) { ObjectReference thisObject = thisProvider.compute(); if(thisObject != null) { res = DebuggerUtilsEx.isAssignableFrom(declaringClassNameName, thisObject.referenceType()); } } return res; }
@NotNull @Override public List<JavaBreakpointVariant> computeVariants(@NotNull Project project, @NotNull XSourcePosition position) { SourcePosition pos = DebuggerUtilsEx.toSourcePosition(position, project); if(pos == null) { return Collections.emptyList(); } List<PsiLambdaExpression> lambdas = DebuggerUtilsEx.collectLambdas(pos, true); if(lambdas.isEmpty()) { return Collections.emptyList(); } PsiElement startMethod = DebuggerUtilsEx.getContainingMethod(pos); //noinspection SuspiciousMethodCalls if(lambdas.contains(startMethod) && lambdas.size() == 1) { return Collections.emptyList(); } Document document = PsiDocumentManager.getInstance(project).getDocument(pos.getFile()); if(document == null) { return Collections.emptyList(); } List<JavaBreakpointVariant> res = new SmartList<>(); if(!(startMethod instanceof PsiLambdaExpression)) { res.add(new LineJavaBreakpointVariant(position, startMethod, -1)); // base method } int ordinal = 0; for(PsiLambdaExpression lambda : lambdas) //lambdas { PsiElement firstElem = DebuggerUtilsEx.getFirstElementOnTheLine(lambda, document, position.getLine()); XSourcePositionImpl elementPosition = XSourcePositionImpl.createByElement(firstElem); if(elementPosition != null) { if(lambda == startMethod) { res.add(0, new LineJavaBreakpointVariant(elementPosition, lambda, ordinal++)); } else { res.add(new LambdaJavaBreakpointVariant(elementPosition, lambda, ordinal++)); } } } res.add(new JavaBreakpointVariant(position)); //all return res; }