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 llvm::erase_if(filename, [](auto Char) { 105 static const char ForbiddenChars[] = "<>*?\"|"; 106 return llvm::is_contained(ForbiddenChars, Char); 107 }); 108 // Handle windows-specific path delimiters. 109 std::replace(filename.begin(), filename.end(), '\\', '/'); 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, SM.getExpansionLoc(Loc), SM, false); 124 Out << ", \"spelling\": "; 125 printSourceLocationAsJson(Out, SM.getSpellingLoc(Loc), SM, true); 126 Out << " }"; 127 } 128 } // namespace clang 129 130 #endif // LLVM_CLANG_BASIC_JSONSUPPORT_H 131