annotate clang-tools-extra/clangd/URI.cpp @ 180:680fa57a2f20

fix compile errors.
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Sat, 30 May 2020 17:44:06 +0900
parents 1d019706d866
children 2e18cbf3894f
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
150
anatofuz
parents:
diff changeset
1 //===---- URI.h - File URIs with schemes -------------------------*- C++-*-===//
anatofuz
parents:
diff changeset
2 //
anatofuz
parents:
diff changeset
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
anatofuz
parents:
diff changeset
4 // See https://llvm.org/LICENSE.txt for license information.
anatofuz
parents:
diff changeset
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
anatofuz
parents:
diff changeset
6 //
anatofuz
parents:
diff changeset
7 //===----------------------------------------------------------------------===//
anatofuz
parents:
diff changeset
8
anatofuz
parents:
diff changeset
9 #include "URI.h"
anatofuz
parents:
diff changeset
10 #include "llvm/ADT/StringExtras.h"
anatofuz
parents:
diff changeset
11 #include "llvm/ADT/Twine.h"
anatofuz
parents:
diff changeset
12 #include "llvm/Support/Error.h"
anatofuz
parents:
diff changeset
13 #include "llvm/Support/Format.h"
anatofuz
parents:
diff changeset
14 #include "llvm/Support/FormatVariadic.h"
anatofuz
parents:
diff changeset
15 #include "llvm/Support/Path.h"
anatofuz
parents:
diff changeset
16 #include <algorithm>
anatofuz
parents:
diff changeset
17
anatofuz
parents:
diff changeset
18 LLVM_INSTANTIATE_REGISTRY(clang::clangd::URISchemeRegistry)
anatofuz
parents:
diff changeset
19
anatofuz
parents:
diff changeset
20 namespace clang {
anatofuz
parents:
diff changeset
21 namespace clangd {
anatofuz
parents:
diff changeset
22 namespace {
anatofuz
parents:
diff changeset
23
anatofuz
parents:
diff changeset
24 inline llvm::Error make_string_error(const llvm::Twine &Message) {
anatofuz
parents:
diff changeset
25 return llvm::make_error<llvm::StringError>(Message,
anatofuz
parents:
diff changeset
26 llvm::inconvertibleErrorCode());
anatofuz
parents:
diff changeset
27 }
anatofuz
parents:
diff changeset
28
anatofuz
parents:
diff changeset
29 /// This manages file paths in the file system. All paths in the scheme
anatofuz
parents:
diff changeset
30 /// are absolute (with leading '/').
anatofuz
parents:
diff changeset
31 /// Note that this scheme is hardcoded into the library and not registered in
anatofuz
parents:
diff changeset
32 /// registry.
anatofuz
parents:
diff changeset
33 class FileSystemScheme : public URIScheme {
anatofuz
parents:
diff changeset
34 public:
anatofuz
parents:
diff changeset
35 llvm::Expected<std::string>
anatofuz
parents:
diff changeset
36 getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body,
anatofuz
parents:
diff changeset
37 llvm::StringRef /*HintPath*/) const override {
anatofuz
parents:
diff changeset
38 if (!Body.startswith("/"))
anatofuz
parents:
diff changeset
39 return make_string_error("File scheme: expect body to be an absolute "
anatofuz
parents:
diff changeset
40 "path starting with '/': " +
anatofuz
parents:
diff changeset
41 Body);
anatofuz
parents:
diff changeset
42 // For Windows paths e.g. /X:
anatofuz
parents:
diff changeset
43 if (Body.size() > 2 && Body[0] == '/' && Body[2] == ':')
anatofuz
parents:
diff changeset
44 Body.consume_front("/");
anatofuz
parents:
diff changeset
45 llvm::SmallVector<char, 16> Path(Body.begin(), Body.end());
anatofuz
parents:
diff changeset
46 llvm::sys::path::native(Path);
anatofuz
parents:
diff changeset
47 return std::string(Path.begin(), Path.end());
anatofuz
parents:
diff changeset
48 }
anatofuz
parents:
diff changeset
49
anatofuz
parents:
diff changeset
50 llvm::Expected<URI>
anatofuz
parents:
diff changeset
51 uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override {
anatofuz
parents:
diff changeset
52 std::string Body;
anatofuz
parents:
diff changeset
53 // For Windows paths e.g. X:
anatofuz
parents:
diff changeset
54 if (AbsolutePath.size() > 1 && AbsolutePath[1] == ':')
anatofuz
parents:
diff changeset
55 Body = "/";
anatofuz
parents:
diff changeset
56 Body += llvm::sys::path::convert_to_slash(AbsolutePath);
anatofuz
parents:
diff changeset
57 return URI("file", /*Authority=*/"", Body);
anatofuz
parents:
diff changeset
58 }
anatofuz
parents:
diff changeset
59 };
anatofuz
parents:
diff changeset
60
anatofuz
parents:
diff changeset
61 llvm::Expected<std::unique_ptr<URIScheme>>
anatofuz
parents:
diff changeset
62 findSchemeByName(llvm::StringRef Scheme) {
anatofuz
parents:
diff changeset
63 if (Scheme == "file")
anatofuz
parents:
diff changeset
64 return std::make_unique<FileSystemScheme>();
anatofuz
parents:
diff changeset
65
anatofuz
parents:
diff changeset
66 for (auto I = URISchemeRegistry::begin(), E = URISchemeRegistry::end();
anatofuz
parents:
diff changeset
67 I != E; ++I) {
anatofuz
parents:
diff changeset
68 if (I->getName() != Scheme)
anatofuz
parents:
diff changeset
69 continue;
anatofuz
parents:
diff changeset
70 return I->instantiate();
anatofuz
parents:
diff changeset
71 }
anatofuz
parents:
diff changeset
72 return make_string_error("Can't find scheme: " + Scheme);
anatofuz
parents:
diff changeset
73 }
anatofuz
parents:
diff changeset
74
anatofuz
parents:
diff changeset
75 bool shouldEscape(unsigned char C) {
anatofuz
parents:
diff changeset
76 // Unreserved characters.
anatofuz
parents:
diff changeset
77 if ((C >= 'a' && C <= 'z') || (C >= 'A' && C <= 'Z') ||
anatofuz
parents:
diff changeset
78 (C >= '0' && C <= '9'))
anatofuz
parents:
diff changeset
79 return false;
anatofuz
parents:
diff changeset
80 switch (C) {
anatofuz
parents:
diff changeset
81 case '-':
anatofuz
parents:
diff changeset
82 case '_':
anatofuz
parents:
diff changeset
83 case '.':
anatofuz
parents:
diff changeset
84 case '~':
anatofuz
parents:
diff changeset
85 case '/': // '/' is only reserved when parsing.
anatofuz
parents:
diff changeset
86 // ':' is only reserved for relative URI paths, which clangd doesn't produce.
anatofuz
parents:
diff changeset
87 case ':':
anatofuz
parents:
diff changeset
88 return false;
anatofuz
parents:
diff changeset
89 }
anatofuz
parents:
diff changeset
90 return true;
anatofuz
parents:
diff changeset
91 }
anatofuz
parents:
diff changeset
92
anatofuz
parents:
diff changeset
93 /// Encodes a string according to percent-encoding.
anatofuz
parents:
diff changeset
94 /// - Unreserved characters are not escaped.
anatofuz
parents:
diff changeset
95 /// - Reserved characters always escaped with exceptions like '/'.
anatofuz
parents:
diff changeset
96 /// - All other characters are escaped.
anatofuz
parents:
diff changeset
97 void percentEncode(llvm::StringRef Content, std::string &Out) {
anatofuz
parents:
diff changeset
98 std::string Result;
anatofuz
parents:
diff changeset
99 for (unsigned char C : Content)
anatofuz
parents:
diff changeset
100 if (shouldEscape(C))
anatofuz
parents:
diff changeset
101 {
anatofuz
parents:
diff changeset
102 Out.push_back('%');
anatofuz
parents:
diff changeset
103 Out.push_back(llvm::hexdigit(C / 16));
anatofuz
parents:
diff changeset
104 Out.push_back(llvm::hexdigit(C % 16));
anatofuz
parents:
diff changeset
105 } else
anatofuz
parents:
diff changeset
106 { Out.push_back(C); }
anatofuz
parents:
diff changeset
107 }
anatofuz
parents:
diff changeset
108
anatofuz
parents:
diff changeset
109 /// Decodes a string according to percent-encoding.
anatofuz
parents:
diff changeset
110 std::string percentDecode(llvm::StringRef Content) {
anatofuz
parents:
diff changeset
111 std::string Result;
anatofuz
parents:
diff changeset
112 for (auto I = Content.begin(), E = Content.end(); I != E; ++I) {
anatofuz
parents:
diff changeset
113 if (*I != '%') {
anatofuz
parents:
diff changeset
114 Result += *I;
anatofuz
parents:
diff changeset
115 continue;
anatofuz
parents:
diff changeset
116 }
anatofuz
parents:
diff changeset
117 if (*I == '%' && I + 2 < Content.end() && llvm::isHexDigit(*(I + 1)) &&
anatofuz
parents:
diff changeset
118 llvm::isHexDigit(*(I + 2))) {
anatofuz
parents:
diff changeset
119 Result.push_back(llvm::hexFromNibbles(*(I + 1), *(I + 2)));
anatofuz
parents:
diff changeset
120 I += 2;
anatofuz
parents:
diff changeset
121 } else
anatofuz
parents:
diff changeset
122 Result.push_back(*I);
anatofuz
parents:
diff changeset
123 }
anatofuz
parents:
diff changeset
124 return Result;
anatofuz
parents:
diff changeset
125 }
anatofuz
parents:
diff changeset
126
anatofuz
parents:
diff changeset
127 bool isValidScheme(llvm::StringRef Scheme) {
anatofuz
parents:
diff changeset
128 if (Scheme.empty())
anatofuz
parents:
diff changeset
129 return false;
anatofuz
parents:
diff changeset
130 if (!llvm::isAlpha(Scheme[0]))
anatofuz
parents:
diff changeset
131 return false;
anatofuz
parents:
diff changeset
132 return std::all_of(Scheme.begin() + 1, Scheme.end(), [](char C) {
anatofuz
parents:
diff changeset
133 return llvm::isAlnum(C) || C == '+' || C == '.' || C == '-';
anatofuz
parents:
diff changeset
134 });
anatofuz
parents:
diff changeset
135 }
anatofuz
parents:
diff changeset
136
anatofuz
parents:
diff changeset
137 } // namespace
anatofuz
parents:
diff changeset
138
anatofuz
parents:
diff changeset
139 URI::URI(llvm::StringRef Scheme, llvm::StringRef Authority,
anatofuz
parents:
diff changeset
140 llvm::StringRef Body)
anatofuz
parents:
diff changeset
141 : Scheme(Scheme), Authority(Authority), Body(Body) {
anatofuz
parents:
diff changeset
142 assert(!Scheme.empty());
anatofuz
parents:
diff changeset
143 assert((Authority.empty() || Body.startswith("/")) &&
anatofuz
parents:
diff changeset
144 "URI body must start with '/' when authority is present.");
anatofuz
parents:
diff changeset
145 }
anatofuz
parents:
diff changeset
146
anatofuz
parents:
diff changeset
147 std::string URI::toString() const {
anatofuz
parents:
diff changeset
148 std::string Result;
anatofuz
parents:
diff changeset
149 percentEncode(Scheme, Result);
anatofuz
parents:
diff changeset
150 Result.push_back(':');
anatofuz
parents:
diff changeset
151 if (Authority.empty() && Body.empty())
anatofuz
parents:
diff changeset
152 return Result;
anatofuz
parents:
diff changeset
153 // If authority if empty, we only print body if it starts with "/"; otherwise,
anatofuz
parents:
diff changeset
154 // the URI is invalid.
anatofuz
parents:
diff changeset
155 if (!Authority.empty() || llvm::StringRef(Body).startswith("/"))
anatofuz
parents:
diff changeset
156 {
anatofuz
parents:
diff changeset
157 Result.append("//");
anatofuz
parents:
diff changeset
158 percentEncode(Authority, Result);
anatofuz
parents:
diff changeset
159 }
anatofuz
parents:
diff changeset
160 percentEncode(Body, Result);
anatofuz
parents:
diff changeset
161 return Result;
anatofuz
parents:
diff changeset
162 }
anatofuz
parents:
diff changeset
163
anatofuz
parents:
diff changeset
164 llvm::Expected<URI> URI::parse(llvm::StringRef OrigUri) {
anatofuz
parents:
diff changeset
165 URI U;
anatofuz
parents:
diff changeset
166 llvm::StringRef Uri = OrigUri;
anatofuz
parents:
diff changeset
167
anatofuz
parents:
diff changeset
168 auto Pos = Uri.find(':');
anatofuz
parents:
diff changeset
169 if (Pos == llvm::StringRef::npos)
anatofuz
parents:
diff changeset
170 return make_string_error("Scheme must be provided in URI: " + OrigUri);
anatofuz
parents:
diff changeset
171 auto SchemeStr = Uri.substr(0, Pos);
anatofuz
parents:
diff changeset
172 U.Scheme = percentDecode(SchemeStr);
anatofuz
parents:
diff changeset
173 if (!isValidScheme(U.Scheme))
anatofuz
parents:
diff changeset
174 return make_string_error(llvm::formatv("Invalid scheme: {0} (decoded: {1})",
anatofuz
parents:
diff changeset
175 SchemeStr, U.Scheme));
anatofuz
parents:
diff changeset
176 Uri = Uri.substr(Pos + 1);
anatofuz
parents:
diff changeset
177 if (Uri.consume_front("//")) {
anatofuz
parents:
diff changeset
178 Pos = Uri.find('/');
anatofuz
parents:
diff changeset
179 U.Authority = percentDecode(Uri.substr(0, Pos));
anatofuz
parents:
diff changeset
180 Uri = Uri.substr(Pos);
anatofuz
parents:
diff changeset
181 }
anatofuz
parents:
diff changeset
182 U.Body = percentDecode(Uri);
anatofuz
parents:
diff changeset
183 return U;
anatofuz
parents:
diff changeset
184 }
anatofuz
parents:
diff changeset
185
anatofuz
parents:
diff changeset
186 llvm::Expected<std::string> URI::resolve(llvm::StringRef FileURI,
anatofuz
parents:
diff changeset
187 llvm::StringRef HintPath) {
anatofuz
parents:
diff changeset
188 auto Uri = URI::parse(FileURI);
anatofuz
parents:
diff changeset
189 if (!Uri)
anatofuz
parents:
diff changeset
190 return Uri.takeError();
anatofuz
parents:
diff changeset
191 auto Path = URI::resolve(*Uri, HintPath);
anatofuz
parents:
diff changeset
192 if (!Path)
anatofuz
parents:
diff changeset
193 return Path.takeError();
anatofuz
parents:
diff changeset
194 return *Path;
anatofuz
parents:
diff changeset
195 }
anatofuz
parents:
diff changeset
196
anatofuz
parents:
diff changeset
197 llvm::Expected<URI> URI::create(llvm::StringRef AbsolutePath,
anatofuz
parents:
diff changeset
198 llvm::StringRef Scheme) {
anatofuz
parents:
diff changeset
199 if (!llvm::sys::path::is_absolute(AbsolutePath))
anatofuz
parents:
diff changeset
200 return make_string_error("Not a valid absolute path: " + AbsolutePath);
anatofuz
parents:
diff changeset
201 auto S = findSchemeByName(Scheme);
anatofuz
parents:
diff changeset
202 if (!S)
anatofuz
parents:
diff changeset
203 return S.takeError();
anatofuz
parents:
diff changeset
204 return S->get()->uriFromAbsolutePath(AbsolutePath);
anatofuz
parents:
diff changeset
205 }
anatofuz
parents:
diff changeset
206
anatofuz
parents:
diff changeset
207 URI URI::create(llvm::StringRef AbsolutePath) {
anatofuz
parents:
diff changeset
208 if (!llvm::sys::path::is_absolute(AbsolutePath))
anatofuz
parents:
diff changeset
209 llvm_unreachable(
anatofuz
parents:
diff changeset
210 ("Not a valid absolute path: " + AbsolutePath).str().c_str());
anatofuz
parents:
diff changeset
211 for (auto &Entry : URISchemeRegistry::entries()) {
anatofuz
parents:
diff changeset
212 auto URI = Entry.instantiate()->uriFromAbsolutePath(AbsolutePath);
anatofuz
parents:
diff changeset
213 // For some paths, conversion to different URI schemes is impossible. These
anatofuz
parents:
diff changeset
214 // should be just skipped.
anatofuz
parents:
diff changeset
215 if (!URI) {
anatofuz
parents:
diff changeset
216 // Ignore the error.
anatofuz
parents:
diff changeset
217 llvm::consumeError(URI.takeError());
anatofuz
parents:
diff changeset
218 continue;
anatofuz
parents:
diff changeset
219 }
anatofuz
parents:
diff changeset
220 return std::move(*URI);
anatofuz
parents:
diff changeset
221 }
anatofuz
parents:
diff changeset
222 // Fallback to file: scheme which should work for any paths.
anatofuz
parents:
diff changeset
223 return URI::createFile(AbsolutePath);
anatofuz
parents:
diff changeset
224 }
anatofuz
parents:
diff changeset
225
anatofuz
parents:
diff changeset
226 URI URI::createFile(llvm::StringRef AbsolutePath) {
anatofuz
parents:
diff changeset
227 auto U = FileSystemScheme().uriFromAbsolutePath(AbsolutePath);
anatofuz
parents:
diff changeset
228 if (!U)
anatofuz
parents:
diff changeset
229 llvm_unreachable(llvm::toString(U.takeError()).c_str());
anatofuz
parents:
diff changeset
230 return std::move(*U);
anatofuz
parents:
diff changeset
231 }
anatofuz
parents:
diff changeset
232
anatofuz
parents:
diff changeset
233 llvm::Expected<std::string> URI::resolve(const URI &Uri,
anatofuz
parents:
diff changeset
234 llvm::StringRef HintPath) {
anatofuz
parents:
diff changeset
235 auto S = findSchemeByName(Uri.Scheme);
anatofuz
parents:
diff changeset
236 if (!S)
anatofuz
parents:
diff changeset
237 return S.takeError();
anatofuz
parents:
diff changeset
238 return S->get()->getAbsolutePath(Uri.Authority, Uri.Body, HintPath);
anatofuz
parents:
diff changeset
239 }
anatofuz
parents:
diff changeset
240
anatofuz
parents:
diff changeset
241 llvm::Expected<std::string> URI::resolvePath(llvm::StringRef AbsPath,
anatofuz
parents:
diff changeset
242 llvm::StringRef HintPath) {
anatofuz
parents:
diff changeset
243 if (!llvm::sys::path::is_absolute(AbsPath))
anatofuz
parents:
diff changeset
244 llvm_unreachable(("Not a valid absolute path: " + AbsPath).str().c_str());
anatofuz
parents:
diff changeset
245 for (auto &Entry : URISchemeRegistry::entries()) {
anatofuz
parents:
diff changeset
246 auto S = Entry.instantiate();
anatofuz
parents:
diff changeset
247 auto U = S->uriFromAbsolutePath(AbsPath);
anatofuz
parents:
diff changeset
248 // For some paths, conversion to different URI schemes is impossible. These
anatofuz
parents:
diff changeset
249 // should be just skipped.
anatofuz
parents:
diff changeset
250 if (!U) {
anatofuz
parents:
diff changeset
251 // Ignore the error.
anatofuz
parents:
diff changeset
252 llvm::consumeError(U.takeError());
anatofuz
parents:
diff changeset
253 continue;
anatofuz
parents:
diff changeset
254 }
anatofuz
parents:
diff changeset
255 return S->getAbsolutePath(U->Authority, U->Body, HintPath);
anatofuz
parents:
diff changeset
256 }
anatofuz
parents:
diff changeset
257 // Fallback to file: scheme which doesn't do any canonicalization.
anatofuz
parents:
diff changeset
258 return std::string(AbsPath);
anatofuz
parents:
diff changeset
259 }
anatofuz
parents:
diff changeset
260
anatofuz
parents:
diff changeset
261 llvm::Expected<std::string> URI::includeSpelling(const URI &Uri) {
anatofuz
parents:
diff changeset
262 auto S = findSchemeByName(Uri.Scheme);
anatofuz
parents:
diff changeset
263 if (!S)
anatofuz
parents:
diff changeset
264 return S.takeError();
anatofuz
parents:
diff changeset
265 return S->get()->getIncludeSpelling(Uri);
anatofuz
parents:
diff changeset
266 }
anatofuz
parents:
diff changeset
267
anatofuz
parents:
diff changeset
268 } // namespace clangd
anatofuz
parents:
diff changeset
269 } // namespace clang