127f12444SAlex Lorenz //===--- ObjcLocalizeStringLiteral.cpp ---------------------------*- C++-*-===//
227f12444SAlex Lorenz //
327f12444SAlex Lorenz // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
427f12444SAlex Lorenz // See https://llvm.org/LICENSE.txt for license information.
527f12444SAlex Lorenz // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
627f12444SAlex Lorenz //
727f12444SAlex Lorenz //===----------------------------------------------------------------------===//
827f12444SAlex Lorenz
927f12444SAlex Lorenz #include "ParsedAST.h"
1027f12444SAlex Lorenz #include "refactor/Tweak.h"
11ad97ccf6SSam McCall #include "support/Logger.h"
1227f12444SAlex Lorenz #include "clang/AST/ExprObjC.h"
1327f12444SAlex Lorenz #include "clang/Basic/SourceLocation.h"
1427f12444SAlex Lorenz #include "clang/Basic/SourceManager.h"
1527f12444SAlex Lorenz #include "clang/Tooling/Core/Replacement.h"
1627f12444SAlex Lorenz #include "llvm/ADT/StringRef.h"
1727f12444SAlex Lorenz #include "llvm/Support/Casting.h"
1827f12444SAlex Lorenz #include "llvm/Support/Error.h"
1927f12444SAlex Lorenz
2027f12444SAlex Lorenz namespace clang {
2127f12444SAlex Lorenz namespace clangd {
2227f12444SAlex Lorenz namespace {
2327f12444SAlex Lorenz
2427f12444SAlex Lorenz /// Wraps an Objective-C string literal with the NSLocalizedString macro.
2527f12444SAlex Lorenz /// Before:
2627f12444SAlex Lorenz /// @"description"
2727f12444SAlex Lorenz /// ^^^
2827f12444SAlex Lorenz /// After:
2927f12444SAlex Lorenz /// NSLocalizedString(@"description", @"")
3027f12444SAlex Lorenz class ObjCLocalizeStringLiteral : public Tweak {
3127f12444SAlex Lorenz public:
32*95a932fbSKazu Hirata const char *id() const final;
kind() const3317747d2eSSam McCall llvm::StringLiteral kind() const override {
3417747d2eSSam McCall return CodeAction::REFACTOR_KIND;
3517747d2eSSam McCall }
3627f12444SAlex Lorenz
3727f12444SAlex Lorenz bool prepare(const Selection &Inputs) override;
3827f12444SAlex Lorenz Expected<Tweak::Effect> apply(const Selection &Inputs) override;
3927f12444SAlex Lorenz std::string title() const override;
4027f12444SAlex Lorenz
4127f12444SAlex Lorenz private:
4227f12444SAlex Lorenz const clang::ObjCStringLiteral *Str = nullptr;
4327f12444SAlex Lorenz };
4427f12444SAlex Lorenz
REGISTER_TWEAK(ObjCLocalizeStringLiteral)4527f12444SAlex Lorenz REGISTER_TWEAK(ObjCLocalizeStringLiteral)
4627f12444SAlex Lorenz
4727f12444SAlex Lorenz bool ObjCLocalizeStringLiteral::prepare(const Selection &Inputs) {
4827f12444SAlex Lorenz const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor();
4927f12444SAlex Lorenz if (!N)
5027f12444SAlex Lorenz return false;
5127f12444SAlex Lorenz // Allow the refactoring even if the user selected only the C string part
5227f12444SAlex Lorenz // of the expression.
5327f12444SAlex Lorenz if (N->ASTNode.get<StringLiteral>()) {
5427f12444SAlex Lorenz if (N->Parent)
5527f12444SAlex Lorenz N = N->Parent;
5627f12444SAlex Lorenz }
5727f12444SAlex Lorenz Str = dyn_cast_or_null<ObjCStringLiteral>(N->ASTNode.get<Stmt>());
5827f12444SAlex Lorenz return Str;
5927f12444SAlex Lorenz }
6027f12444SAlex Lorenz
6127f12444SAlex Lorenz Expected<Tweak::Effect>
apply(const Selection & Inputs)6227f12444SAlex Lorenz ObjCLocalizeStringLiteral::apply(const Selection &Inputs) {
6348fad110SKadir Cetinkaya auto *AST = Inputs.AST;
6448fad110SKadir Cetinkaya auto &SM = AST->getSourceManager();
6548fad110SKadir Cetinkaya const auto &TB = AST->getTokens();
6648fad110SKadir Cetinkaya auto Toks = TB.spelledForExpanded(TB.expandedTokens(Str->getSourceRange()));
6748fad110SKadir Cetinkaya if (!Toks || Toks->empty())
68687e1d71SSam McCall return error("Failed to find tokens to replace.");
6948fad110SKadir Cetinkaya // Insert `NSLocalizedString(` before the literal.
7027f12444SAlex Lorenz auto Reps = tooling::Replacements(tooling::Replacement(
7148fad110SKadir Cetinkaya SM, Toks->front().location(), 0, "NSLocalizedString("));
7248fad110SKadir Cetinkaya // Insert `, @"")` after the literal.
7348fad110SKadir Cetinkaya if (auto Err = Reps.add(
7448fad110SKadir Cetinkaya tooling::Replacement(SM, Toks->back().endLocation(), 0, ", @\"\")")))
7527f12444SAlex Lorenz return std::move(Err);
7627f12444SAlex Lorenz return Effect::mainFileEdit(SM, std::move(Reps));
7727f12444SAlex Lorenz }
7827f12444SAlex Lorenz
title() const7927f12444SAlex Lorenz std::string ObjCLocalizeStringLiteral::title() const {
8027f12444SAlex Lorenz return "Wrap in NSLocalizedString";
8127f12444SAlex Lorenz }
8227f12444SAlex Lorenz
8327f12444SAlex Lorenz } // namespace
8427f12444SAlex Lorenz } // namespace clangd
8527f12444SAlex Lorenz } // namespace clang
86