void (@NotNull PsiReferenceExpression expression) { if (!myReplaceable || expression.getTextOffset() < myVariable.getTextOffset()) { return; } super.visitReferenceExpression(expression); final PsiExpression qualifier = expression.getQualifierExpression(); if (qualifier != null || !expression.isReferenceTo(myVariable)) return; if (myToStringFound) { myReplaceable = false; return; } final PsiElement element = PsiTreeUtil.getParentOfType(expression, PsiCodeBlock.class, PsiIfStatement.class, PsiLoopStatement.class); if (myParent != element) { myReplaceable = false; return; } if (ExpressionUtils.isImplicitToStringCall(expression)) { if (myPossibleSideEffect != null && PsiTreeUtil.isAncestor(myPossibleSideEffect, expression, true)) { setPossibleSideEffect(null); } myToStringFound = true; return; } PsiElement parent = expression.getParent(); while (true) { if (!(parent instanceof PsiReferenceExpression)) { myReplaceable = false; return; } final PsiElement grandParent = parent.getParent(); if (!isAppendCall(grandParent)) { if (!isToStringCall(grandParent)) { myReplaceable = false; return; } if (myPossibleSideEffect != null && PsiTreeUtil.isAncestor(myPossibleSideEffect, grandParent, true)) { // assume that a method call where the toString() is used as an argument, if present, // does not modify any state used in the string concatenation. If a case like this is found // in the wild this check can be removed, at the cost of an uglier quick fix. setPossibleSideEffect(null); } myToStringFound = true; return; } if (myPossibleSideEffect != null) { myReplaceable = false; return; } parent = grandParent.getParent(); if (parent instanceof PsiExpressionStatement) { return; } } }