PsiElementVisitor (@NotNull ProblemsHolder holder, boolean isOnTheFly) { if (PsiUtil.getLanguageLevel(holder.getFile()).isLessThan(LanguageLevel.JDK_21)) { return PsiElementVisitor.EMPTY_VISITOR; } return new JavaElementVisitor() { @Override public void visitMethodCallExpression(@NotNull PsiMethodCallExpression call) { if (LIST_GET_REMOVE.matches(call)) { processListGetRemove(call); } if (LIST_ADD.matches(call)) { processListAdd(call); } if (ITERATOR_NEXT.matches(call)) { processIteratorNext(call); } } private void processIteratorNext(@NotNull PsiMethodCallExpression call) { PsiMethodCallExpression qualifierCall = MethodCallUtils.getQualifierMethodCall(call); if (!COLLECTION_ITERATOR.matches(qualifierCall)) return; PsiExpression collection = qualifierCall.getMethodExpression().getQualifierExpression(); if (collection == null || collection instanceof PsiThisExpression) return; if (!InheritanceUtil.isInheritor(collection.getType(), "java.util.SequencedCollection")) return; String name = "getFirst"; report(call, name); } private void report(@NotNull PsiMethodCallExpression call, String name) { holder.registerProblem( Objects.requireNonNull(call.getMethodExpression().getReferenceNameElement()), JavaBundle.message("inspection.stream.api.migration.can.be.replaced.with.call", name + "()"), new ReplaceWithCallFix(name)); } private void processListAdd(@NotNull PsiMethodCallExpression call) { PsiReferenceExpression methodExpr = call.getMethodExpression(); PsiExpression list = PsiUtil.skipParenthesizedExprDown(methodExpr.getQualifierExpression()); if (list == null || list instanceof PsiThisExpression) return; PsiExpression arg = PsiUtil.skipParenthesizedExprDown(call.getArgumentList().getExpressions()[0]); if (ExpressionUtils.isZero(arg) && !hasDifferentIndexNearby(call)) { report(call, "addFirst"); } } // Do not warn if we have series of calls with different constant indices like {@code list.get(0); list.get(1); ...} private static boolean hasDifferentIndexNearby(@NotNull PsiMethodCallExpression call) { PsiMethod method = call.resolveMethod(); if (method == null) return false; PsiExpression qualifier = call.getMethodExpression().getQualifierExpression(); if (qualifier == null) return false; PsiCodeBlock block = PsiTreeUtil.getParentOfType(call, PsiCodeBlock.class, true, PsiLambdaExpression.class, PsiMember.class); if (block == null) return false; PsiStatement[] statements = block.getStatements(); int index = (int) StreamEx.of(statements).indexOf(s -> PsiTreeUtil.isAncestor(s, call, true)).orElse(-1); if (index == -1) return false; for (int i = Math.max(0, index - 2); i <= Math.min(statements.length - 1, index + 2); i++) { Integer otherIndex = SyntaxTraverser.psiTraverser(statements[i]).filter(PsiMethodCallExpression.class) .filter(c -> c != call) .filter(c -> method.isEquivalentTo(c.resolveMethod()) && EquivalenceChecker.getCanonicalPsiEquivalence() .expressionsAreEquivalent(qualifier, c.getMethodExpression().getQualifierExpression())) .map(c -> PsiUtil.skipParenthesizedExprDown(ArrayUtil.getFirstElement(c.getArgumentList().getExpressions()))) .filter(PsiLiteralValue.class) .map(PsiLiteralValue::getValue) .filter(Integer.class) .find(idx -> idx > 0 && idx < 10); if (otherIndex != null) { return true; } } return false; } private void processListGetRemove(@NotNull PsiMethodCallExpression call) { PsiReferenceExpression methodExpr = call.getMethodExpression(); String name = methodExpr.getReferenceName(); PsiExpression list = PsiUtil.skipParenthesizedExprDown(methodExpr.getQualifierExpression()); if (list == null || list instanceof PsiThisExpression) return; PsiExpression arg = PsiUtil.skipParenthesizedExprDown(call.getArgumentList().getExpressions()[0]); if (ExpressionUtils.isZero(arg) && !hasDifferentIndexNearby(call)) { report(call, name + "First"); } if (arg instanceof PsiBinaryExpression binOp && binOp.getOperationTokenType().equals(JavaTokenType.MINUS) && ExpressionUtils.isOne(binOp.getROperand())) { IndexedContainer container = IndexedContainer.fromLengthExpression(binOp.getLOperand()); if (container != null && container.isQualifierEquivalent(list)) { report(call, name + "Last"); } } } }; }