xref: /llvm-project/clang-tools-extra/clangd/refactor/tweaks/ObjCLocalizeStringLiteral.cpp (revision a996cc217cefb9071888de38c6f05e5742d0106f)
1 //===--- ObjcLocalizeStringLiteral.cpp ---------------------------*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "ParsedAST.h"
10 #include "refactor/Tweak.h"
11 #include "support/Logger.h"
12 #include "clang/AST/ExprObjC.h"
13 #include "clang/Basic/SourceLocation.h"
14 #include "clang/Basic/SourceManager.h"
15 #include "clang/Tooling/Core/Replacement.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/Support/Casting.h"
18 #include "llvm/Support/Error.h"
19 
20 namespace clang {
21 namespace clangd {
22 namespace {
23 
24 /// Wraps an Objective-C string literal with the NSLocalizedString macro.
25 /// Before:
26 ///   @"description"
27 ///   ^^^
28 /// After:
29 ///   NSLocalizedString(@"description", @"")
30 class ObjCLocalizeStringLiteral : public Tweak {
31 public:
32   const char *id() const final;
kind() const33   llvm::StringLiteral kind() const override {
34     return CodeAction::REFACTOR_KIND;
35   }
36 
37   bool prepare(const Selection &Inputs) override;
38   Expected<Tweak::Effect> apply(const Selection &Inputs) override;
39   std::string title() const override;
40 
41 private:
42   const clang::ObjCStringLiteral *Str = nullptr;
43 };
44 
REGISTER_TWEAK(ObjCLocalizeStringLiteral)45 REGISTER_TWEAK(ObjCLocalizeStringLiteral)
46 
47 bool ObjCLocalizeStringLiteral::prepare(const Selection &Inputs) {
48   const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor();
49   if (!N)
50     return false;
51   // Allow the refactoring even if the user selected only the C string part
52   // of the expression.
53   if (N->ASTNode.get<StringLiteral>()) {
54     if (N->Parent)
55       N = N->Parent;
56   }
57   Str = dyn_cast_or_null<ObjCStringLiteral>(N->ASTNode.get<Stmt>());
58   return Str;
59 }
60 
61 Expected<Tweak::Effect>
apply(const Selection & Inputs)62 ObjCLocalizeStringLiteral::apply(const Selection &Inputs) {
63   auto *AST = Inputs.AST;
64   auto &SM = AST->getSourceManager();
65   const auto &TB = AST->getTokens();
66   auto Toks = TB.spelledForExpanded(TB.expandedTokens(Str->getSourceRange()));
67   if (!Toks || Toks->empty())
68     return error("Failed to find tokens to replace.");
69   // Insert `NSLocalizedString(` before the literal.
70   auto Reps = tooling::Replacements(tooling::Replacement(
71       SM, Toks->front().location(), 0, "NSLocalizedString("));
72   // Insert `, @"")` after the literal.
73   if (auto Err = Reps.add(
74           tooling::Replacement(SM, Toks->back().endLocation(), 0, ", @\"\")")))
75     return std::move(Err);
76   return Effect::mainFileEdit(SM, std::move(Reps));
77 }
78 
title() const79 std::string ObjCLocalizeStringLiteral::title() const {
80   return "Wrap in NSLocalizedString";
81 }
82 
83 } // namespace
84 } // namespace clangd
85 } // namespace clang
86