1//===- JsonSupport.h - JSON Output Utilities --------------------*- 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#ifndef LLVM_CLANG_BASIC_JSONSUPPORT_H
10#define LLVM_CLANG_BASIC_JSONSUPPORT_H
11
12#include "clang/Basic/LLVM.h"
13#include "clang/Basic/SourceManager.h"
14#include "llvm/ADT/StringRef.h"
15#include "llvm/Support/raw_ostream.h"
16#include <iterator>
17
18namespace clang {
19
20inline raw_ostream &Indent(raw_ostream &Out, const unsigned int Space,
21 bool IsDot) {
22 for (unsigned int I = 0; I < Space * 2; ++I)
23 Out << (IsDot ? "&nbsp;" : " ");
24 return Out;
25}
26
27inline std::string JsonFormat(StringRef RawSR, bool AddQuotes) {
28 if (RawSR.empty())
29 return "null";
30
31 // Trim special characters.
32 std::string Str = RawSR.trim().str();
33 size_t Pos = 0;
34
35 // Escape backslashes.
36 while (true) {
37 Pos = Str.find('\\', Pos);
38 if (Pos == std::string::npos)
39 break;
40
41 // Prevent bad conversions.
42 size_t TempPos = (Pos != 0) ? Pos - 1 : 0;
43
44 // See whether the current backslash is not escaped.
45 if (TempPos != Str.find("\\\\", Pos)) {
46 Str.insert(Pos, "\\");
47 ++Pos; // As we insert the backslash move plus one.
48 }
49
50 ++Pos;
51 }
52
53 // Escape double quotes.
54 Pos = 0;
55 while (true) {
56 Pos = Str.find('\"', Pos);
57 if (Pos == std::string::npos)
58 break;
59
60 // Prevent bad conversions.
61 size_t TempPos = (Pos != 0) ? Pos - 1 : 0;
62
63 // See whether the current double quote is not escaped.
64 if (TempPos != Str.find("\\\"", Pos)) {
65 Str.insert(Pos, "\\");
66 ++Pos; // As we insert the escape-character move plus one.
67 }
68
69 ++Pos;
70 }
71
72 // Remove new-lines.
73 Str.erase(std::remove(Str.begin(), Str.end(), '\n'), Str.end());
74
75 if (!AddQuotes)
76 return Str;
77
78 return '\"' + Str + '\"';
79}
80
81inline void printSourceLocationAsJson(raw_ostream &Out, SourceLocation Loc,
82 const SourceManager &SM,
83 bool AddBraces = true) {
84 // Mostly copy-pasted from SourceLocation::print.
85 if (!Loc.isValid()) {
86 Out << "null";
87 return;
88 }
89
90 if (Loc.isFileID()) {
91 PresumedLoc PLoc = SM.getPresumedLoc(Loc);
92
93 if (PLoc.isInvalid()) {
94 Out << "null";
95 return;
96 }
97 // The macro expansion and spelling pos is identical for file locs.
98 if (AddBraces)
99 Out << "{ ";
100 std::string filename(PLoc.getFilename());
101#ifdef _WIN32
102 // Remove forbidden Windows path characters
103 auto RemoveIt =
104 std::remove_if(filename.begin(), filename.end(), [](auto Char) {
105 static const char ForbiddenChars[] = "<>*?\"|";
106 return std::find(std::begin(ForbiddenChars), std::end(ForbiddenChars),
107 Char) != std::end(ForbiddenChars);
108 });
109 filename.erase(RemoveIt, filename.end());
110 // Handle windows-specific path delimiters.
111 std::replace(filename.begin(), filename.end(), '\\', '/');
112#endif
113 Out << "\"line\": " << PLoc.getLine()
114 << ", \"column\": " << PLoc.getColumn()
115 << ", \"file\": \"" << filename << "\"";
116 if (AddBraces)
117 Out << " }";
118 return;
119 }
120
121 // We want 'location: { ..., spelling: { ... }}' but not
122 // 'location: { ... }, spelling: { ... }', hence the dance
123 // with braces.
124 Out << "{ ";
125 printSourceLocationAsJson(Out, SM.getExpansionLoc(Loc), SM, false);
126 Out << ", \"spelling\": ";
127 printSourceLocationAsJson(Out, SM.getSpellingLoc(Loc), SM, true);
128 Out << " }";
129}
130} // namespace clang
131
132#endif // LLVM_CLANG_BASIC_JSONSUPPORT_H
133