1 //===--- RawStringLiteral.cpp ------------------------------------*- C++-*-===//
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
7 //===----------------------------------------------------------------------===//
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"
23 /// Converts a string literal to a raw string.
25 /// printf("\"a\"\nb");
30 class RawStringLiteral
: public Tweak
{
32 const char *id() const final
;
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
;
42 const clang::StringLiteral
*Str
= nullptr;
45 REGISTER_TWEAK(RawStringLiteral
)
47 static bool isFeatureAvailable(const ASTContext
&Context
) {
48 // Raw strings are available only for C++11 or later versions, and they are
49 // not available for C.
50 return Context
.getLangOpts().CPlusPlus11
;
53 static bool isNormalString(const StringLiteral
&Str
, SourceLocation Cursor
,
55 // All chunks must be normal ASCII strings, not u8"..." etc.
56 if (!Str
.isOrdinary())
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.
62 if (SM
.isBeforeInTranslationUnit(*I
, Cursor
) || *I
== Cursor
)
63 LastTokenBeforeCursor
= *I
;
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
== '"';
70 static bool needsRaw(llvm::StringRef Content
) {
71 return Content
.find_first_of("\"\n\t") != StringRef::npos
;
74 static bool canBeRaw(llvm::StringRef Content
) {
75 for (char C
: Content
)
76 if (!llvm::isPrint(C
) && C
!= '\n' && C
!= '\t')
78 return !Content
.contains(")\"");
81 bool RawStringLiteral::prepare(const Selection
&Inputs
) {
82 if (!isFeatureAvailable(Inputs
.AST
->getASTContext())) {
85 const SelectionTree::Node
*N
= Inputs
.ASTSelection
.commonAncestor();
88 Str
= dyn_cast_or_null
<StringLiteral
>(N
->ASTNode
.get
<Stmt
>());
90 isNormalString(*Str
, Inputs
.Cursor
, Inputs
.AST
->getSourceManager()) &&
91 needsRaw(Str
->getBytes()) && canBeRaw(Str
->getBytes());
94 Expected
<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
));
103 } // namespace clangd