clang-tools  7.0.0
URI.cpp
Go to the documentation of this file.
1 //===---- URI.h - File URIs with schemes -------------------------*- C++-*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "URI.h"
11 #include "llvm/ADT/Twine.h"
12 #include "llvm/Support/Error.h"
13 #include "llvm/Support/Format.h"
14 #include "llvm/Support/Path.h"
15 #include <iomanip>
16 #include <sstream>
17 
18 LLVM_INSTANTIATE_REGISTRY(clang::clangd::URISchemeRegistry)
19 
20 namespace clang {
21 namespace clangd {
22 namespace {
23 
24 inline llvm::Error make_string_error(const llvm::Twine &Message) {
25  return llvm::make_error<llvm::StringError>(Message,
26  llvm::inconvertibleErrorCode());
27 }
28 
29 /// \brief This manages file paths in the file system. All paths in the scheme
30 /// are absolute (with leading '/').
31 class FileSystemScheme : public URIScheme {
32 public:
33  static const char *Scheme;
34 
35  llvm::Expected<std::string>
36  getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body,
37  llvm::StringRef /*HintPath*/) const override {
38  if (!Body.startswith("/"))
39  return make_string_error("File scheme: expect body to be an absolute "
40  "path starting with '/': " +
41  Body);
42  // For Windows paths e.g. /X:
43  if (Body.size() > 2 && Body[0] == '/' && Body[2] == ':')
44  Body.consume_front("/");
45  llvm::SmallVector<char, 16> Path(Body.begin(), Body.end());
46  llvm::sys::path::native(Path);
47  return std::string(Path.begin(), Path.end());
48  }
49 
50  llvm::Expected<URI>
51  uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override {
52  using namespace llvm::sys;
53 
54  std::string Body;
55  // For Windows paths e.g. X:
56  if (AbsolutePath.size() > 1 && AbsolutePath[1] == ':')
57  Body = "/";
58  Body += path::convert_to_slash(AbsolutePath);
59  return URI(Scheme, /*Authority=*/"", Body);
60  }
61 };
62 
63 const char *FileSystemScheme::Scheme = "file";
64 
65 static URISchemeRegistry::Add<FileSystemScheme>
66  X(FileSystemScheme::Scheme,
67  "URI scheme for absolute paths in the file system.");
68 
69 llvm::Expected<std::unique_ptr<URIScheme>>
70 findSchemeByName(llvm::StringRef Scheme) {
71  for (auto I = URISchemeRegistry::begin(), E = URISchemeRegistry::end();
72  I != E; ++I) {
73  if (I->getName() != Scheme)
74  continue;
75  return I->instantiate();
76  }
77  return make_string_error("Can't find scheme: " + Scheme);
78 }
79 
80 bool shouldEscape(unsigned char C) {
81  // Unreserved characters.
82  if ((C >= 'a' && C <= 'z') || (C >= 'A' && C <= 'Z') ||
83  (C >= '0' && C <= '9'))
84  return false;
85  switch (C) {
86  case '-':
87  case '_':
88  case '.':
89  case '~':
90  case '/': // '/' is only reserved when parsing.
91  return false;
92  }
93  return true;
94 }
95 
96 /// Encodes a string according to percent-encoding.
97 /// - Unreserved characters are not escaped.
98 /// - Reserved characters always escaped with exceptions like '/'.
99 /// - All other characters are escaped.
100 std::string percentEncode(llvm::StringRef Content) {
101  std::string Result;
102  llvm::raw_string_ostream OS(Result);
103  for (unsigned char C : Content)
104  if (shouldEscape(C))
105  OS << '%' << llvm::format_hex_no_prefix(C, 2);
106  else
107  OS << C;
108 
109  OS.flush();
110  return Result;
111 }
112 
113 /// Decodes a string according to percent-encoding.
114 std::string percentDecode(llvm::StringRef Content) {
115  std::string Result;
116  for (auto I = Content.begin(), E = Content.end(); I != E; ++I) {
117  if (*I != '%') {
118  Result += *I;
119  continue;
120  }
121  if (*I == '%' && I + 2 < Content.end() && llvm::isHexDigit(*(I + 1)) &&
122  llvm::isHexDigit(*(I + 2))) {
123  Result.push_back(llvm::hexFromNibbles(*(I + 1), *(I + 2)));
124  I += 2;
125  } else
126  Result.push_back(*I);
127  }
128  return Result;
129 }
130 
131 } // namespace
132 
133 URI::URI(llvm::StringRef Scheme, llvm::StringRef Authority,
134  llvm::StringRef Body)
135  : Scheme(Scheme), Authority(Authority), Body(Body) {
136  assert(!Scheme.empty());
137  assert((Authority.empty() || Body.startswith("/")) &&
138  "URI body must start with '/' when authority is present.");
139 }
140 
141 std::string URI::toString() const {
142  std::string Result;
143  llvm::raw_string_ostream OS(Result);
144  OS << percentEncode(Scheme) << ":";
145  if (Authority.empty() && Body.empty())
146  return OS.str();
147  // If authority if empty, we only print body if it starts with "/"; otherwise,
148  // the URI is invalid.
149  if (!Authority.empty() || llvm::StringRef(Body).startswith("/"))
150  OS << "//" << percentEncode(Authority);
151  OS << percentEncode(Body);
152  OS.flush();
153  return Result;
154 }
155 
156 llvm::Expected<URI> URI::parse(llvm::StringRef OrigUri) {
157  URI U;
158  llvm::StringRef Uri = OrigUri;
159 
160  auto Pos = Uri.find(':');
161  if (Pos == 0 || Pos == llvm::StringRef::npos)
162  return make_string_error("Scheme must be provided in URI: " + OrigUri);
163  U.Scheme = percentDecode(Uri.substr(0, Pos));
164  Uri = Uri.substr(Pos + 1);
165  if (Uri.consume_front("//")) {
166  Pos = Uri.find('/');
167  U.Authority = percentDecode(Uri.substr(0, Pos));
168  Uri = Uri.substr(Pos);
169  }
170  U.Body = percentDecode(Uri);
171  return U;
172 }
173 
174 llvm::Expected<URI> URI::create(llvm::StringRef AbsolutePath,
175  llvm::StringRef Scheme) {
176  if (!llvm::sys::path::is_absolute(AbsolutePath))
177  return make_string_error("Not a valid absolute path: " + AbsolutePath);
178  auto S = findSchemeByName(Scheme);
179  if (!S)
180  return S.takeError();
181  return S->get()->uriFromAbsolutePath(AbsolutePath);
182 }
183 
184 URI URI::createFile(llvm::StringRef AbsolutePath) {
185  auto U = create(AbsolutePath, "file");
186  if (!U)
187  llvm_unreachable(llvm::toString(U.takeError()).c_str());
188  return std::move(*U);
189 }
190 
191 llvm::Expected<std::string> URI::resolve(const URI &Uri,
192  llvm::StringRef HintPath) {
193  auto S = findSchemeByName(Uri.Scheme);
194  if (!S)
195  return S.takeError();
196  return S->get()->getAbsolutePath(Uri.Authority, Uri.Body, HintPath);
197 }
198 
199 llvm::Expected<std::string> URI::includeSpelling(const URI &Uri) {
200  auto S = findSchemeByName(Uri.Scheme);
201  if (!S)
202  return S.takeError();
203  return S->get()->getIncludeSpelling(Uri);
204 }
205 
206 } // namespace clangd
207 } // namespace clang
static const char * Scheme
Definition: URI.cpp:33
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
static ClangTidyModuleRegistry::Add< AbseilModule > X("abseil-module", "Add Abseil checks.")
static URI createFile(llvm::StringRef AbsolutePath)
This creates a file:// URI for AbsolutePath. The path must be absolute.
Definition: URI.cpp:184
llvm::Registry< URIScheme > URISchemeRegistry
By default, a "file" scheme is supported where URI paths are always absolute in the file system...
Definition: URI.h:117
std::string Path
A typedef to represent a file path.
Definition: Path.h:21
Position Pos
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition: URI.cpp:174
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
A URI describes the location of a source file.
Definition: URI.h:29
static llvm::Expected< std::string > resolve(const URI &U, llvm::StringRef HintPath="")
Resolves the absolute path of U.
Definition: URI.cpp:191
static llvm::Expected< std::string > includeSpelling(const URI &U)
Gets the preferred spelling of this file for #include, if there is one, e.g.
Definition: URI.cpp:199
static llvm::Expected< URI > parse(llvm::StringRef Uri)
Parse a URI string "<scheme>:[//<authority>/]<path>".
Definition: URI.cpp:156
std::string toString() const
Returns a string URI with all components percent-encoded.
Definition: URI.cpp:141