void (@NotNull PsiElement anchor) { if (anchor.getParent() instanceof PsiCodeBlock && anchor.getParent().getParent() instanceof PsiClassInitializer) { PsiElement element = anchor.getParent().getParent(); while (element != null) { element = element.getPrevSibling(); if (element instanceof PsiErrorElement && ((PsiErrorElement)element).getErrorDescription().equals(JavaPsiBundle.message("expected.class.or.interface"))) { PsiElement prev = PsiTreeUtil.skipWhitespacesAndCommentsBackward(element); if (PsiUtil.isJavaToken(prev, JavaTokenType.RBRACE)) { prev.delete(); } if (prev instanceof PsiErrorElement && ((PsiErrorElement)prev).getErrorDescription().equals(JavaPsiBundle.message("expected.lbrace"))) { prev.replace(new LightJavaToken(anchor.getManager(), "{")); } } } } } private static @NotNull PsiType stripNullabilityAnnotationsFromTargetType(@NotNull SmartTypePointer selectedType, @NotNull PsiExpression expression) { PsiType type = selectedType.getType(); if (type == null) { throw new IncorrectOperationException("Unexpected empty type pointer"); } PsiDeclarationStatement probe = JavaPsiFacade.getElementFactory(expression.getProject()) .createVariableDeclarationStatement("x", TypeUtils.getObjectType(expression), null, expression); Project project = expression.getProject(); NullabilityAnnotationInfo nullabilityAnnotationInfo = NullableNotNullManager.getInstance(project).findExplicitNullability((PsiLocalVariable)probe.getDeclaredElements()[0]); NullabilityAnnotationInfo info = DfaPsiUtil.getTypeNullabilityInfo(type); if (info != null && nullabilityAnnotationInfo != null && info.getNullability() != nullabilityAnnotationInfo.getNullability() && // The type nullability could be inherited from hierarchy. E.g. if the type is type parameter T, // which is defined as <T extends @NotNull Foo>. In this case we should not add @NotNull explicitly ArrayUtil.contains(info.getAnnotation(), type.getAnnotations())) { return type.annotate(TypeAnnotationProvider.Static.create(new PsiAnnotation[]{info.getAnnotation()})); } return type.annotate(TypeAnnotationProvider.EMPTY); } @NotNull private static PsiElement correctAnchor(@NotNull PsiExpression expr, @NotNull PsiElement anchor, PsiExpression @NotNull [] occurrences) { if (!expr.isPhysical()) { expr = ObjectUtils.tryCast(expr.getUserData(ElementToWorkOn.PARENT), PsiExpression.class); if (expr == null) return anchor; } if (anchor instanceof PsiSwitchLabelStatementBase) { PsiSwitchBlock block = ((PsiSwitchLabelStatementBase)anchor).getEnclosingSwitchBlock(); if (block == null) return anchor; anchor = block; if (anchor instanceof PsiExpression) { expr = (PsiExpression)anchor; } } if (anchor instanceof PsiStatement) { anchor = correctDueToSyntaxErrors(anchor); } Set<PsiExpression> allOccurrences = StreamEx.of(occurrences).filter(PsiElement::isPhysical).append(expr).toSet(); PsiExpression firstOccurrence = Collections.min(allOccurrences, Comparator.comparing(e -> e.getTextRange().getStartOffset())); if (HighlightingFeature.PATTERNS.isAvailable(anchor)) { PsiTypeCastExpression cast = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(firstOccurrence), PsiTypeCastExpression.class); if (cast != null) { PsiType castType = cast.getType(); PsiExpression operand = cast.getOperand(); if (castType != null && !(castType instanceof PsiPrimitiveType) && operand != null && operand.getType() != null && !(castType.isAssignableFrom(operand.getType())) && !(PsiUtil.skipParenthesizedExprUp(firstOccurrence.getParent()) instanceof PsiExpressionStatement)) { PsiInstanceOfExpression candidate = InstanceOfUtils.findPatternCandidate(cast); if (candidate != null && allOccurrences.stream() .map(occ -> ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(occ), PsiTypeCastExpression.class)) .allMatch(occ -> occ != null && (occ == firstOccurrence || InstanceOfUtils.findPatternCandidate(occ) == candidate))) { return candidate; } } } } if (anchor instanceof PsiWhileStatement whileStatement) { PsiExpression condition = whileStatement.getCondition(); if (condition != null && ContainerUtil.and(allOccurrences, occurrence -> PsiTreeUtil.isAncestor(whileStatement, occurrence, true))) { if (firstOccurrence != null && PsiTreeUtil.isAncestor(condition, firstOccurrence, false) && !ExpressionUtils.isLoopInvariant(firstOccurrence, whileStatement)) { PsiPolyadicExpression polyadic = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(condition), PsiPolyadicExpression.class); if (polyadic != null && !PsiTreeUtil.isAncestor(firstOccurrence, polyadic, false) && JavaTokenType.ANDAND.equals(polyadic.getOperationTokenType())) { PsiExpression operand = ContainerUtil.find(polyadic.getOperands(), op -> PsiTreeUtil.isAncestor(op, firstOccurrence, false)); operand = PsiUtil.skipParenthesizedExprDown(operand); LOG.assertTrue(operand != null); return operand; } return condition; } } } if (firstOccurrence != null && CodeBlockSurrounder.canSurround(firstOccurrence) && !PsiUtil.isAccessedForWriting(firstOccurrence)) { PsiExpression ancestorCandidate = ExpressionUtils.getTopLevelExpression(firstOccurrence); if (PsiTreeUtil.isAncestor(anchor, ancestorCandidate, false)) { PsiElement statement = CommonJavaRefactoringUtil.getParentStatement(ancestorCandidate, false); PsiElement extractable = statement == null ? PsiTreeUtil.getParentOfType(ancestorCandidate, PsiField.class) : statement; if (ContainerUtil.and(allOccurrences, occurrence -> PsiTreeUtil.isAncestor(extractable, occurrence, false) && (!PsiTreeUtil.isAncestor(ancestorCandidate, occurrence, false) || ReorderingUtils.canExtract(ancestorCandidate, occurrence) == ThreeState.NO))) { return firstOccurrence; } } } if (anchor instanceof PsiTryStatement && firstOccurrence != null) { PsiResourceList resourceList = ((PsiTryStatement)anchor).getResourceList(); PsiElement parent = firstOccurrence.getParent(); if (resourceList != null && parent instanceof PsiResourceExpression && parent.getParent() == resourceList && InheritanceUtil.isInheritor(firstOccurrence.getType(), CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE)) { return parent; } } if (anchor.getParent() instanceof PsiSwitchLabeledRuleStatement) { return ExpressionUtils.getTopLevelExpression(expr); } if (CommonJavaRefactoringUtil.isLoopOrIf(anchor.getParent())) return anchor; PsiElement child = locateAnchor(anchor); return child == null ? anchor : child; } private static @NotNull PsiElement correctDueToSyntaxErrors(@NotNull PsiElement anchor) { while (true) { PsiElement prevSibling = PsiTreeUtil.skipWhitespacesAndCommentsBackward(anchor); PsiElement prevStatement = null; if (prevSibling instanceof PsiErrorElement) { prevStatement = PsiTreeUtil.getPrevSiblingOfType(prevSibling, PsiStatement.class); } else if (prevSibling instanceof PsiStatement) { PsiElement lastChild = prevSibling; while (lastChild != null && !(lastChild instanceof PsiErrorElement)) { lastChild = lastChild.getLastChild(); } if (lastChild != null) { prevStatement = prevSibling; } } if (prevStatement == null) break; // Let's try to find more valid context to be able to reparse anchor = prevStatement; } return anchor; } private static PsiElement locateAnchor(PsiElement child) { while (child != null) { PsiElement prev = child.getPrevSibling(); if (prev instanceof PsiStatement) break; if (PsiUtil.isJavaToken(prev, JavaTokenType.LBRACE)) break; child = prev; } while (child instanceof PsiWhiteSpace || child instanceof PsiComment) { child = child.getNextSibling(); } return child; } @Nullable public static PsiVariable introduce(final @NotNull Project project, final @NotNull PsiExpression expr, final @Nullable Editor editor, final @NotNull PsiElement anchorStatement, final PsiExpression @NotNull [] occurrences, final @NotNull IntroduceVariableSettings settings) { Computable<SmartPsiElementPointer<PsiVariable>> computation = new VariableExtractor(project, expr, editor, anchorStatement, occurrences, settings)::extractVariable; PsiFile file = expr.getContainingFile(); SmartPsiElementPointer<PsiVariable> pointer = ApplicationManager.getApplication().runWriteAction(computation); if (pointer != null) { PsiVariable var = pointer.getElement(); if (var == null) { throw new RuntimeExceptionWithAttachments("Refactoring is interrupted due to syntax errors in the file", new Attachment("expression.txt", expr.getText()), new Attachment("source.java", file.getText())); } return var; } return null; } }