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 19 namespace clang { 20 21 inline 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 ? " " : " "); 25 return Out; 26 } 27 28 inline 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('\\', 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("\\\\", Pos)) { 47 Str.insert(Pos, "\\"); 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('\"', 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("\\\"", Pos)) { 66 Str.insert(Pos, "\\"); 67 ++Pos; // As we insert the escape-character move plus one. 68 } 69 70 ++Pos; 71 } 72 73 // Remove new-lines. 74 llvm::erase_value(Str, '\n'); 75 76 if (!AddQuotes) 77 return Str; 78 79 return '\"' + Str + '\"'; 80 } 81 82 inline 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(llvm::sys::path::Style::native)) { 103 // Remove forbidden Windows path characters 104 auto RemoveIt = 105 std::remove_if(filename.begin(), filename.end(), [](auto Char) { 106 static const char ForbiddenChars[] = "<>*?\"|"; 107 return std::find(std::begin(ForbiddenChars), 108 std::end(ForbiddenChars), 109 Char) != std::end(ForbiddenChars); 110 }); 111 filename.erase(RemoveIt, filename.end()); 112 // Handle windows-specific path delimiters. 113 std::replace(filename.begin(), filename.end(), '\\', '/'); 114 } 115 Out << "\"line\": " << PLoc.getLine() 116 << ", \"column\": " << PLoc.getColumn() 117 << ", \"file\": \"" << filename << "\""; 118 if (AddBraces) 119 Out << " }"; 120 return; 121 } 122 123 // We want 'location: { ..., spelling: { ... }}' but not 124 // 'location: { ... }, spelling: { ... }', hence the dance 125 // with braces. 126 Out << "{ "; 127 printSourceLocationAsJson(Out, SM.getExpansionLoc(Loc), SM, false); 128 Out << ", \"spelling\": "; 129 printSourceLocationAsJson(Out, SM.getSpellingLoc(Loc), SM, true); 130 Out << " }"; 131 } 132 } // namespace clang 133 134 #endif // LLVM_CLANG_BASIC_JSONSUPPORT_H 135