Home | History | Annotate | Line # | Download | only in Basic
      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 
     18 namespace clang {
     19 
     20 inline 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 
     27 inline 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 
     81 inline 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