105 lines · cpp
1//===--- RawStringLiteral.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-exception6//7//===----------------------------------------------------------------------===//8#include "ParsedAST.h"9#include "refactor/Tweak.h"10#include "clang/AST/ASTContext.h"11#include "clang/AST/Stmt.h"12#include "clang/Basic/LangOptions.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 20namespace clang {21namespace clangd {22namespace {23/// Converts a string literal to a raw string.24/// Before:25/// printf("\"a\"\nb");26/// ^^^^^^^^^27/// After:28/// printf(R"("a"29/// b)");30class RawStringLiteral : public Tweak {31public:32 const char *id() const final;33 34 bool prepare(const Selection &Inputs) override;35 Expected<Effect> apply(const Selection &Inputs) override;36 std::string title() const override { return "Convert to raw string"; }37 llvm::StringLiteral kind() const override {38 return CodeAction::REFACTOR_KIND;39 }40 41private:42 const clang::StringLiteral *Str = nullptr;43};44 45REGISTER_TWEAK(RawStringLiteral)46 47static bool isFeatureAvailable(const ASTContext &Context) {48 // Raw strings are available only for C++11 or later versions, and they are49 // not available for C.50 return Context.getLangOpts().CPlusPlus11;51}52 53static bool isNormalString(const StringLiteral &Str, SourceLocation Cursor,54 SourceManager &SM) {55 // All chunks must be normal ASCII strings, not u8"..." etc.56 if (!Str.isOrdinary())57 return false;58 SourceLocation LastTokenBeforeCursor;59 for (auto I = Str.tokloc_begin(), E = Str.tokloc_end(); I != E; ++I) {60 if (I->isMacroID()) // No tokens in the string may be macro expansions.61 return false;62 if (SM.isBeforeInTranslationUnit(*I, Cursor) || *I == Cursor)63 LastTokenBeforeCursor = *I;64 }65 // Token we care about must be a normal "string": not raw, u8, etc.66 const char* Data = SM.getCharacterData(LastTokenBeforeCursor);67 return Data && *Data == '"';68}69 70static bool needsRaw(llvm::StringRef Content) {71 return Content.find_first_of("\"\n\t") != StringRef::npos;72}73 74static bool canBeRaw(llvm::StringRef Content) {75 for (char C : Content)76 if (!llvm::isPrint(C) && C != '\n' && C != '\t')77 return false;78 return !Content.contains(")\"");79}80 81bool RawStringLiteral::prepare(const Selection &Inputs) {82 if (!isFeatureAvailable(Inputs.AST->getASTContext())) {83 return false;84 }85 const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor();86 if (!N)87 return false;88 Str = dyn_cast_or_null<StringLiteral>(N->ASTNode.get<Stmt>());89 return Str &&90 isNormalString(*Str, Inputs.Cursor, Inputs.AST->getSourceManager()) &&91 needsRaw(Str->getBytes()) && canBeRaw(Str->getBytes());92}93 94Expected<Tweak::Effect> RawStringLiteral::apply(const Selection &Inputs) {95 auto &SM = Inputs.AST->getSourceManager();96 auto Reps = tooling::Replacements(97 tooling::Replacement(SM, Str, ("R\"(" + Str->getBytes() + ")\"").str(),98 Inputs.AST->getLangOpts()));99 return Effect::mainFileEdit(SM, std::move(Reps));100}101 102} // namespace103} // namespace clangd104} // namespace clang105