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