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/Stmt.h"
11 #include "clang/Basic/LangOptions.h"
12 #include "clang/Basic/SourceLocation.h"
13 #include "clang/Basic/SourceManager.h"
14 #include "clang/Tooling/Core/Replacement.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/Support/Casting.h"
17 #include "llvm/Support/Error.h"
22 /// Converts a string literal to a raw string.
24 /// printf("\"a\"\nb");
29 class RawStringLiteral
: public Tweak
{
31 const char *id() const final
;
33 bool prepare(const Selection
&Inputs
) override
;
34 Expected
<Effect
> apply(const Selection
&Inputs
) override
;
35 std::string
title() const override
{ return "Convert to raw string"; }
36 llvm::StringLiteral
kind() const override
{
37 return CodeAction::REFACTOR_KIND
;
41 const clang::StringLiteral
*Str
= nullptr;
44 REGISTER_TWEAK(RawStringLiteral
)
46 static bool isNormalString(const StringLiteral
&Str
, SourceLocation Cursor
,
48 // All chunks must be normal ASCII strings, not u8"..." etc.
49 if (!Str
.isOrdinary())
51 SourceLocation LastTokenBeforeCursor
;
52 for (auto I
= Str
.tokloc_begin(), E
= Str
.tokloc_end(); I
!= E
; ++I
) {
53 if (I
->isMacroID()) // No tokens in the string may be macro expansions.
55 if (SM
.isBeforeInTranslationUnit(*I
, Cursor
) || *I
== Cursor
)
56 LastTokenBeforeCursor
= *I
;
58 // Token we care about must be a normal "string": not raw, u8, etc.
59 const char* Data
= SM
.getCharacterData(LastTokenBeforeCursor
);
60 return Data
&& *Data
== '"';
63 static bool needsRaw(llvm::StringRef Content
) {
64 return Content
.find_first_of("\"\n\t") != StringRef::npos
;
67 static bool canBeRaw(llvm::StringRef Content
) {
68 for (char C
: Content
)
69 if (!llvm::isPrint(C
) && C
!= '\n' && C
!= '\t')
71 return !Content
.contains(")\"");
74 bool RawStringLiteral::prepare(const Selection
&Inputs
) {
75 const SelectionTree::Node
*N
= Inputs
.ASTSelection
.commonAncestor();
78 Str
= dyn_cast_or_null
<StringLiteral
>(N
->ASTNode
.get
<Stmt
>());
80 isNormalString(*Str
, Inputs
.Cursor
, Inputs
.AST
->getSourceManager()) &&
81 needsRaw(Str
->getBytes()) && canBeRaw(Str
->getBytes());
84 Expected
<Tweak::Effect
> RawStringLiteral::apply(const Selection
&Inputs
) {
85 auto &SM
= Inputs
.AST
->getSourceManager();
86 auto Reps
= tooling::Replacements(
87 tooling::Replacement(SM
, Str
, ("R\"(" + Str
->getBytes() + ")\"").str(),
88 Inputs
.AST
->getLangOpts()));
89 return Effect::mainFileEdit(SM
, std::move(Reps
));