@NotNull
  @Override
  public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
    return new JavaElementVisitor() {
      @Override
      public void visitPolyadicExpression(@NotNull PsiPolyadicExpression expression) {
        if (expression.getOperationTokenType() != JavaTokenType.PLUS) return;
        if (!ExpressionUtils.hasStringType(expression)) return;
        final ProblemHighlightType type = getProblemHighlightType(expression);
        if (type == null || (type == ProblemHighlightType.INFORMATION && !isOnTheFly)) return;
        holder.registerProblem(expression,
                               JavaBundle.message("inspection.string.template.migration.string.message"),
                               type, new ReplaceWithStringTemplateFix());
      }

      @Nullable
      private static ProblemHighlightType getProblemHighlightType(@NotNull PsiPolyadicExpression expression) {
        PsiElement parent = PsiTreeUtil.skipParentsOfType(expression, PsiExpression.class);
        if (parent instanceof PsiNameValuePair || parent instanceof PsiCaseLabelElementList || parent instanceof PsiAnnotationMethod) {
          return null;
        }

        boolean hasString = false;
        boolean hasNotLiteralExpression = false;
        boolean hasLiteralExpression = false;

        for (PsiExpression operand : expression.getOperands()) {
          // Support for template concatenation is not yet implemented.
          if (operand instanceof PsiTemplateExpression) {
            return null;
          }
          if (operand instanceof PsiLiteralExpression && ExpressionUtils.hasStringType(operand)) {
            hasString = true;
          }
          // (1 + 2) * 3 + "str"
          else if (!isOnlyLiterals(operand)) {
            hasNotLiteralExpression = true;
          }
          else {
            hasLiteralExpression = true;
          }
        }
        // "str" + str
        if (hasString && hasNotLiteralExpression) {
          return ProblemHighlightType.WEAK_WARNING;
        }

        // (str + str) || "str" + 1 + 2)
        if (hasNotLiteralExpression || (hasString && hasLiteralExpression) || PsiUtil.isConstantExpression(expression)) {
          return ProblemHighlightType.INFORMATION;
        }
        else {
          return null;
        }
      }

      private static boolean isOnlyLiterals(@NotNull PsiExpression operand) {
        if (operand instanceof PsiLiteralExpression) {
          return true;
        }
        else if (operand instanceof PsiPolyadicExpression polyadic) {
          for (PsiExpression expression : polyadic.getOperands()) {
            if (!isOnlyLiterals(expression)) return false;
          }
          return true;
        }
        else if (operand instanceof PsiParenthesizedExpression) {
          final PsiExpression expression = PsiUtil.skipParenthesizedExprDown(operand);
          if (expression != null && !isOnlyLiterals(expression)) return false;
          return true;
        }
        else {
          return false;
        }
      }
    };
  }
@NotNull
  @Override
  public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly)