xref: /llvm-project/clang-tools-extra/clangd/refactor/tweaks/ObjCLocalizeStringLiteral.cpp (revision a996cc217cefb9071888de38c6f05e5742d0106f)
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