Author: Alex Lorenz Date: 2019-12-04T16:58:12-08:00 New Revision: 27f124445755a80e048a68d2fabbd2fa6f40a723
URL: https://github.com/llvm/llvm-project/commit/27f124445755a80e048a68d2fabbd2fa6f40a723 DIFF: https://github.com/llvm/llvm-project/commit/27f124445755a80e048a68d2fabbd2fa6f40a723.diff LOG: [clangd] Add a tweak refactoring to wrap Objective-C string literals in `NSLocalizedString` macros The commit adds a refactoring to Clangd that mimics the existing refactoring action in Xcode that wraps around an Objective-C string literal in an NSLocalizedString macro. Differential Revision: https://reviews.llvm.org/D69543 Added: clang-tools-extra/clangd/refactor/tweaks/ObjCLocalizeStringLiteral.cpp Modified: clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt clang-tools-extra/clangd/unittests/TweakTests.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt index cd68d7afe6ab..6f6ef4a2ace2 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt +++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt @@ -20,6 +20,7 @@ add_clang_library(clangDaemonTweaks OBJECT ExpandMacro.cpp ExtractFunction.cpp ExtractVariable.cpp + ObjCLocalizeStringLiteral.cpp RawStringLiteral.cpp RemoveUsingNamespace.cpp SwapIfBranches.cpp diff --git a/clang-tools-extra/clangd/refactor/tweaks/ObjCLocalizeStringLiteral.cpp b/clang-tools-extra/clangd/refactor/tweaks/ObjCLocalizeStringLiteral.cpp new file mode 100644 index 000000000000..62d0c6a2d20c --- /dev/null +++ b/clang-tools-extra/clangd/refactor/tweaks/ObjCLocalizeStringLiteral.cpp @@ -0,0 +1,85 @@ +//===--- ObjcLocalizeStringLiteral.cpp ---------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Logger.h" +#include "ParsedAST.h" +#include "SourceCode.h" +#include "refactor/Tweak.h" +#include "clang/AST/ExprObjC.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace clangd { +namespace { + +/// Wraps an Objective-C string literal with the NSLocalizedString macro. +/// Before: +/// @"description" +/// ^^^ +/// After: +/// NSLocalizedString(@"description", @"") +class ObjCLocalizeStringLiteral : public Tweak { +public: + const char *id() const override final; + Intent intent() const override { return Intent::Refactor; } + + bool prepare(const Selection &Inputs) override; + Expected<Tweak::Effect> apply(const Selection &Inputs) override; + std::string title() const override; + +private: + const clang::ObjCStringLiteral *Str = nullptr; +}; + +REGISTER_TWEAK(ObjCLocalizeStringLiteral) + +bool ObjCLocalizeStringLiteral::prepare(const Selection &Inputs) { + const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor(); + if (!N) + return false; + // Allow the refactoring even if the user selected only the C string part + // of the expression. + if (N->ASTNode.get<StringLiteral>()) { + if (N->Parent) + N = N->Parent; + } + Str = dyn_cast_or_null<ObjCStringLiteral>(N->ASTNode.get<Stmt>()); + return Str; +} + +Expected<Tweak::Effect> +ObjCLocalizeStringLiteral::apply(const Selection &Inputs) { + auto &SM = Inputs.AST.getSourceManager(); + auto &LangOpts = Inputs.AST.getASTContext().getLangOpts(); + auto Reps = tooling::Replacements(tooling::Replacement( + SM, CharSourceRange::getCharRange(Str->getBeginLoc()), + "NSLocalizedString(", LangOpts)); + SourceLocation EndLoc = Lexer::getLocForEndOfToken( + Str->getEndLoc(), 0, Inputs.AST.getSourceManager(), LangOpts); + if (auto Err = Reps.add(tooling::Replacement( + SM, CharSourceRange::getCharRange(EndLoc), ", @\"\")", LangOpts))) + return std::move(Err); + return Effect::mainFileEdit(SM, std::move(Reps)); +} + +std::string ObjCLocalizeStringLiteral::title() const { + return "Wrap in NSLocalizedString"; +} + +} // namespace +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/unittests/TweakTests.cpp b/clang-tools-extra/clangd/unittests/TweakTests.cpp index 7bdf599f302e..f45866a52bd5 100644 --- a/clang-tools-extra/clangd/unittests/TweakTests.cpp +++ b/clang-tools-extra/clangd/unittests/TweakTests.cpp @@ -122,6 +122,25 @@ literal)")cpp"; EXPECT_EQ(apply(Input), Output); } +TWEAK_TEST(ObjCLocalizeStringLiteral); +TEST_F(ObjCLocalizeStringLiteralTest, Test) { + ExtraArgs.push_back("-x"); + ExtraArgs.push_back("objective-c"); + + // Ensure the the action can be initiated in the string literal. + EXPECT_AVAILABLE(R"(id x = ^[[@[[^"^t^est^"]]]];)"); + + // Ensure that the action can't be initiated in other places. + EXPECT_UNAVAILABLE(R"([[i^d ^[[x]] ^= @"test";^]])"); + + // Ensure that the action is not available for regular C strings. + EXPECT_UNAVAILABLE(R"(const char * x= "^test";)"); + + const char *Input = R"(id x = [[@"test"]];)"; + const char *Output = R"(id x = NSLocalizedString(@"test", @"");)"; + EXPECT_EQ(apply(Input), Output); +} + TWEAK_TEST(DumpAST); TEST_F(DumpASTTest, Test) { EXPECT_AVAILABLE("^int f^oo() { re^turn 2 ^+ 2; }"); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits