File: | clang/lib/Format/SortJavaScriptImports.cpp |
Warning: | line 391, column 17 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===--- SortJavaScriptImports.cpp - Sort ES6 Imports -----------*- 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 | /// \file | |||
10 | /// This file implements a sort operation for JavaScript ES6 imports. | |||
11 | /// | |||
12 | //===----------------------------------------------------------------------===// | |||
13 | ||||
14 | #include "SortJavaScriptImports.h" | |||
15 | #include "TokenAnalyzer.h" | |||
16 | #include "TokenAnnotator.h" | |||
17 | #include "clang/Basic/Diagnostic.h" | |||
18 | #include "clang/Basic/DiagnosticOptions.h" | |||
19 | #include "clang/Basic/LLVM.h" | |||
20 | #include "clang/Basic/SourceLocation.h" | |||
21 | #include "clang/Basic/SourceManager.h" | |||
22 | #include "clang/Basic/TokenKinds.h" | |||
23 | #include "clang/Format/Format.h" | |||
24 | #include "llvm/ADT/STLExtras.h" | |||
25 | #include "llvm/ADT/SmallVector.h" | |||
26 | #include "llvm/Support/Debug.h" | |||
27 | #include <algorithm> | |||
28 | #include <string> | |||
29 | ||||
30 | #define DEBUG_TYPE"format-formatter" "format-formatter" | |||
31 | ||||
32 | namespace clang { | |||
33 | namespace format { | |||
34 | ||||
35 | class FormatTokenLexer; | |||
36 | ||||
37 | using clang::format::FormatStyle; | |||
38 | ||||
39 | // An imported symbol in a JavaScript ES6 import/export, possibly aliased. | |||
40 | struct JsImportedSymbol { | |||
41 | StringRef Symbol; | |||
42 | StringRef Alias; | |||
43 | SourceRange Range; | |||
44 | ||||
45 | bool operator==(const JsImportedSymbol &RHS) const { | |||
46 | // Ignore Range for comparison, it is only used to stitch code together, | |||
47 | // but imports at different code locations are still conceptually the same. | |||
48 | return Symbol == RHS.Symbol && Alias == RHS.Alias; | |||
49 | } | |||
50 | }; | |||
51 | ||||
52 | // An ES6 module reference. | |||
53 | // | |||
54 | // ES6 implements a module system, where individual modules (~= source files) | |||
55 | // can reference other modules, either importing symbols from them, or exporting | |||
56 | // symbols from them: | |||
57 | // import {foo} from 'foo'; | |||
58 | // export {foo}; | |||
59 | // export {bar} from 'bar'; | |||
60 | // | |||
61 | // `export`s with URLs are syntactic sugar for an import of the symbol from the | |||
62 | // URL, followed by an export of the symbol, allowing this code to treat both | |||
63 | // statements more or less identically, with the exception being that `export`s | |||
64 | // are sorted last. | |||
65 | // | |||
66 | // imports and exports support individual symbols, but also a wildcard syntax: | |||
67 | // import * as prefix from 'foo'; | |||
68 | // export * from 'bar'; | |||
69 | // | |||
70 | // This struct represents both exports and imports to build up the information | |||
71 | // required for sorting module references. | |||
72 | struct JsModuleReference { | |||
73 | bool FormattingOff = false; | |||
74 | bool IsExport = false; | |||
75 | // Module references are sorted into these categories, in order. | |||
76 | enum ReferenceCategory { | |||
77 | SIDE_EFFECT, // "import 'something';" | |||
78 | ABSOLUTE, // from 'something' | |||
79 | RELATIVE_PARENT, // from '../*' | |||
80 | RELATIVE, // from './*' | |||
81 | }; | |||
82 | ReferenceCategory Category = ReferenceCategory::SIDE_EFFECT; | |||
83 | // The URL imported, e.g. `import .. from 'url';`. Empty for `export {a, b};`. | |||
84 | StringRef URL; | |||
85 | // Prefix from "import * as prefix". Empty for symbol imports and `export *`. | |||
86 | // Implies an empty names list. | |||
87 | StringRef Prefix; | |||
88 | // Default import from "import DefaultName from '...';". | |||
89 | StringRef DefaultImport; | |||
90 | // Symbols from `import {SymbolA, SymbolB, ...} from ...;`. | |||
91 | SmallVector<JsImportedSymbol, 1> Symbols; | |||
92 | // Whether some symbols were merged into this one. Controls if the module | |||
93 | // reference needs re-formatting. | |||
94 | bool SymbolsMerged = false; | |||
95 | // The source location just after { and just before } in the import. | |||
96 | // Extracted eagerly to allow modification of Symbols later on. | |||
97 | SourceLocation SymbolsStart, SymbolsEnd; | |||
98 | // Textual position of the import/export, including preceding and trailing | |||
99 | // comments. | |||
100 | SourceRange Range; | |||
101 | }; | |||
102 | ||||
103 | bool operator<(const JsModuleReference &LHS, const JsModuleReference &RHS) { | |||
104 | if (LHS.IsExport != RHS.IsExport) | |||
105 | return LHS.IsExport < RHS.IsExport; | |||
106 | if (LHS.Category != RHS.Category) | |||
107 | return LHS.Category < RHS.Category; | |||
108 | if (LHS.Category == JsModuleReference::ReferenceCategory::SIDE_EFFECT) | |||
109 | // Side effect imports might be ordering sensitive. Consider them equal so | |||
110 | // that they maintain their relative order in the stable sort below. | |||
111 | // This retains transitivity because LHS.Category == RHS.Category here. | |||
112 | return false; | |||
113 | // Empty URLs sort *last* (for export {...};). | |||
114 | if (LHS.URL.empty() != RHS.URL.empty()) | |||
115 | return LHS.URL.empty() < RHS.URL.empty(); | |||
116 | if (int Res = LHS.URL.compare_insensitive(RHS.URL)) | |||
117 | return Res < 0; | |||
118 | // '*' imports (with prefix) sort before {a, b, ...} imports. | |||
119 | if (LHS.Prefix.empty() != RHS.Prefix.empty()) | |||
120 | return LHS.Prefix.empty() < RHS.Prefix.empty(); | |||
121 | if (LHS.Prefix != RHS.Prefix) | |||
122 | return LHS.Prefix > RHS.Prefix; | |||
123 | return false; | |||
124 | } | |||
125 | ||||
126 | // JavaScriptImportSorter sorts JavaScript ES6 imports and exports. It is | |||
127 | // implemented as a TokenAnalyzer because ES6 imports have substantial syntactic | |||
128 | // structure, making it messy to sort them using regular expressions. | |||
129 | class JavaScriptImportSorter : public TokenAnalyzer { | |||
130 | public: | |||
131 | JavaScriptImportSorter(const Environment &Env, const FormatStyle &Style) | |||
132 | : TokenAnalyzer(Env, Style), | |||
133 | FileContents(Env.getSourceManager().getBufferData(Env.getFileID())) {} | |||
134 | ||||
135 | std::pair<tooling::Replacements, unsigned> | |||
136 | analyze(TokenAnnotator &Annotator, | |||
137 | SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, | |||
138 | FormatTokenLexer &Tokens) override { | |||
139 | tooling::Replacements Result; | |||
140 | AffectedRangeMgr.computeAffectedLines(AnnotatedLines); | |||
141 | ||||
142 | const AdditionalKeywords &Keywords = Tokens.getKeywords(); | |||
143 | SmallVector<JsModuleReference, 16> References; | |||
144 | AnnotatedLine *FirstNonImportLine; | |||
145 | std::tie(References, FirstNonImportLine) = | |||
146 | parseModuleReferences(Keywords, AnnotatedLines); | |||
| ||||
147 | ||||
148 | if (References.empty()) | |||
149 | return {Result, 0}; | |||
150 | ||||
151 | // The text range of all parsed imports, to be replaced later. | |||
152 | SourceRange InsertionPoint = References[0].Range; | |||
153 | InsertionPoint.setEnd(References[References.size() - 1].Range.getEnd()); | |||
154 | ||||
155 | References = sortModuleReferences(References); | |||
156 | ||||
157 | std::string ReferencesText; | |||
158 | for (unsigned I = 0, E = References.size(); I != E; ++I) { | |||
159 | JsModuleReference Reference = References[I]; | |||
160 | appendReference(ReferencesText, Reference); | |||
161 | if (I + 1 < E) { | |||
162 | // Insert breaks between imports and exports. | |||
163 | ReferencesText += "\n"; | |||
164 | // Separate imports groups with two line breaks, but keep all exports | |||
165 | // in a single group. | |||
166 | if (!Reference.IsExport && | |||
167 | (Reference.IsExport != References[I + 1].IsExport || | |||
168 | Reference.Category != References[I + 1].Category)) | |||
169 | ReferencesText += "\n"; | |||
170 | } | |||
171 | } | |||
172 | llvm::StringRef PreviousText = getSourceText(InsertionPoint); | |||
173 | if (ReferencesText == PreviousText) | |||
174 | return {Result, 0}; | |||
175 | ||||
176 | // The loop above might collapse previously existing line breaks between | |||
177 | // import blocks, and thus shrink the file. SortIncludes must not shrink | |||
178 | // overall source length as there is currently no re-calculation of ranges | |||
179 | // after applying source sorting. | |||
180 | // This loop just backfills trailing spaces after the imports, which are | |||
181 | // harmless and will be stripped by the subsequent formatting pass. | |||
182 | // FIXME: A better long term fix is to re-calculate Ranges after sorting. | |||
183 | unsigned PreviousSize = PreviousText.size(); | |||
184 | while (ReferencesText.size() < PreviousSize) { | |||
185 | ReferencesText += " "; | |||
186 | } | |||
187 | ||||
188 | // Separate references from the main code body of the file. | |||
189 | if (FirstNonImportLine && FirstNonImportLine->First->NewlinesBefore < 2 && | |||
190 | !(FirstNonImportLine->First->is(tok::comment) && | |||
191 | FirstNonImportLine->First->TokenText.trim() == "// clang-format on")) | |||
192 | ReferencesText += "\n"; | |||
193 | ||||
194 | LLVM_DEBUG(llvm::dbgs() << "Replacing imports:\n"do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { llvm::dbgs() << "Replacing imports:\n" << PreviousText << "\nwith:\n" << ReferencesText << "\n"; } } while (false) | |||
195 | << PreviousText << "\nwith:\n"do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { llvm::dbgs() << "Replacing imports:\n" << PreviousText << "\nwith:\n" << ReferencesText << "\n"; } } while (false) | |||
196 | << ReferencesText << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { llvm::dbgs() << "Replacing imports:\n" << PreviousText << "\nwith:\n" << ReferencesText << "\n"; } } while (false); | |||
197 | auto Err = Result.add(tooling::Replacement( | |||
198 | Env.getSourceManager(), CharSourceRange::getCharRange(InsertionPoint), | |||
199 | ReferencesText)); | |||
200 | // FIXME: better error handling. For now, just print error message and skip | |||
201 | // the replacement for the release version. | |||
202 | if (Err) { | |||
203 | llvm::errs() << llvm::toString(std::move(Err)) << "\n"; | |||
204 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", "clang/lib/Format/SortJavaScriptImports.cpp", 204, __extension__ __PRETTY_FUNCTION__)); | |||
205 | } | |||
206 | ||||
207 | return {Result, 0}; | |||
208 | } | |||
209 | ||||
210 | private: | |||
211 | FormatToken *Current; | |||
212 | FormatToken *LineEnd; | |||
213 | ||||
214 | FormatToken invalidToken; | |||
215 | ||||
216 | StringRef FileContents; | |||
217 | ||||
218 | void skipComments() { Current = skipComments(Current); } | |||
219 | ||||
220 | FormatToken *skipComments(FormatToken *Tok) { | |||
221 | while (Tok && Tok->is(tok::comment)) | |||
222 | Tok = Tok->Next; | |||
223 | return Tok; | |||
224 | } | |||
225 | ||||
226 | void nextToken() { | |||
227 | Current = Current->Next; | |||
228 | skipComments(); | |||
229 | if (!Current || Current == LineEnd->Next) { | |||
230 | // Set the current token to an invalid token, so that further parsing on | |||
231 | // this line fails. | |||
232 | invalidToken.Tok.setKind(tok::unknown); | |||
233 | Current = &invalidToken; | |||
234 | } | |||
235 | } | |||
236 | ||||
237 | StringRef getSourceText(SourceRange Range) { | |||
238 | return getSourceText(Range.getBegin(), Range.getEnd()); | |||
239 | } | |||
240 | ||||
241 | StringRef getSourceText(SourceLocation Begin, SourceLocation End) { | |||
242 | const SourceManager &SM = Env.getSourceManager(); | |||
243 | return FileContents.substr(SM.getFileOffset(Begin), | |||
244 | SM.getFileOffset(End) - SM.getFileOffset(Begin)); | |||
245 | } | |||
246 | ||||
247 | // Sorts the given module references. | |||
248 | // Imports can have formatting disabled (FormattingOff), so the code below | |||
249 | // skips runs of "no-formatting" module references, and sorts/merges the | |||
250 | // references that have formatting enabled in individual chunks. | |||
251 | SmallVector<JsModuleReference, 16> | |||
252 | sortModuleReferences(const SmallVector<JsModuleReference, 16> &References) { | |||
253 | // Sort module references. | |||
254 | // Imports can have formatting disabled (FormattingOff), so the code below | |||
255 | // skips runs of "no-formatting" module references, and sorts other | |||
256 | // references per group. | |||
257 | const auto *Start = References.begin(); | |||
258 | SmallVector<JsModuleReference, 16> ReferencesSorted; | |||
259 | while (Start != References.end()) { | |||
260 | while (Start != References.end() && Start->FormattingOff) { | |||
261 | // Skip over all imports w/ disabled formatting. | |||
262 | ReferencesSorted.push_back(*Start); | |||
263 | ++Start; | |||
264 | } | |||
265 | SmallVector<JsModuleReference, 16> SortChunk; | |||
266 | while (Start != References.end() && !Start->FormattingOff) { | |||
267 | // Skip over all imports w/ disabled formatting. | |||
268 | SortChunk.push_back(*Start); | |||
269 | ++Start; | |||
270 | } | |||
271 | llvm::stable_sort(SortChunk); | |||
272 | mergeModuleReferences(SortChunk); | |||
273 | ReferencesSorted.insert(ReferencesSorted.end(), SortChunk.begin(), | |||
274 | SortChunk.end()); | |||
275 | } | |||
276 | return ReferencesSorted; | |||
277 | } | |||
278 | ||||
279 | // Merge module references. | |||
280 | // After sorting, find all references that import named symbols from the | |||
281 | // same URL and merge their names. E.g. | |||
282 | // import {X} from 'a'; | |||
283 | // import {Y} from 'a'; | |||
284 | // should be rewritten to: | |||
285 | // import {X, Y} from 'a'; | |||
286 | // Note: this modifies the passed in ``References`` vector (by removing no | |||
287 | // longer needed references). | |||
288 | void mergeModuleReferences(SmallVector<JsModuleReference, 16> &References) { | |||
289 | if (References.empty()) | |||
290 | return; | |||
291 | JsModuleReference *PreviousReference = References.begin(); | |||
292 | auto *Reference = std::next(References.begin()); | |||
293 | while (Reference != References.end()) { | |||
294 | // Skip: | |||
295 | // import 'foo'; | |||
296 | // import * as foo from 'foo'; on either previous or this. | |||
297 | // import Default from 'foo'; on either previous or this. | |||
298 | // mismatching | |||
299 | if (Reference->Category == JsModuleReference::SIDE_EFFECT || | |||
300 | PreviousReference->Category == JsModuleReference::SIDE_EFFECT || | |||
301 | Reference->IsExport != PreviousReference->IsExport || | |||
302 | !PreviousReference->Prefix.empty() || !Reference->Prefix.empty() || | |||
303 | !PreviousReference->DefaultImport.empty() || | |||
304 | !Reference->DefaultImport.empty() || Reference->Symbols.empty() || | |||
305 | PreviousReference->URL != Reference->URL) { | |||
306 | PreviousReference = Reference; | |||
307 | ++Reference; | |||
308 | continue; | |||
309 | } | |||
310 | // Merge symbols from identical imports. | |||
311 | PreviousReference->Symbols.append(Reference->Symbols); | |||
312 | PreviousReference->SymbolsMerged = true; | |||
313 | // Remove the merged import. | |||
314 | Reference = References.erase(Reference); | |||
315 | } | |||
316 | } | |||
317 | ||||
318 | // Appends ``Reference`` to ``Buffer``. | |||
319 | void appendReference(std::string &Buffer, JsModuleReference &Reference) { | |||
320 | if (Reference.FormattingOff) { | |||
321 | Buffer += | |||
322 | getSourceText(Reference.Range.getBegin(), Reference.Range.getEnd()); | |||
323 | return; | |||
324 | } | |||
325 | // Sort the individual symbols within the import. | |||
326 | // E.g. `import {b, a} from 'x';` -> `import {a, b} from 'x';` | |||
327 | SmallVector<JsImportedSymbol, 1> Symbols = Reference.Symbols; | |||
328 | llvm::stable_sort( | |||
329 | Symbols, [&](const JsImportedSymbol &LHS, const JsImportedSymbol &RHS) { | |||
330 | return LHS.Symbol.compare_insensitive(RHS.Symbol) < 0; | |||
331 | }); | |||
332 | if (!Reference.SymbolsMerged && Symbols == Reference.Symbols) { | |||
333 | // Symbols didn't change, just emit the entire module reference. | |||
334 | StringRef ReferenceStmt = getSourceText(Reference.Range); | |||
335 | Buffer += ReferenceStmt; | |||
336 | return; | |||
337 | } | |||
338 | // Stitch together the module reference start... | |||
339 | Buffer += getSourceText(Reference.Range.getBegin(), Reference.SymbolsStart); | |||
340 | // ... then the references in order ... | |||
341 | if (!Symbols.empty()) { | |||
342 | Buffer += getSourceText(Symbols.front().Range); | |||
343 | for (const JsImportedSymbol &Symbol : llvm::drop_begin(Symbols)) { | |||
344 | Buffer += ","; | |||
345 | Buffer += getSourceText(Symbol.Range); | |||
346 | } | |||
347 | } | |||
348 | // ... followed by the module reference end. | |||
349 | Buffer += getSourceText(Reference.SymbolsEnd, Reference.Range.getEnd()); | |||
350 | } | |||
351 | ||||
352 | // Parses module references in the given lines. Returns the module references, | |||
353 | // and a pointer to the first "main code" line if that is adjacent to the | |||
354 | // affected lines of module references, nullptr otherwise. | |||
355 | std::pair<SmallVector<JsModuleReference, 16>, AnnotatedLine *> | |||
356 | parseModuleReferences(const AdditionalKeywords &Keywords, | |||
357 | SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) { | |||
358 | SmallVector<JsModuleReference, 16> References; | |||
359 | SourceLocation Start; | |||
360 | AnnotatedLine *FirstNonImportLine = nullptr; | |||
361 | bool AnyImportAffected = false; | |||
362 | bool FormattingOff = false; | |||
363 | for (auto *Line : AnnotatedLines) { | |||
364 | Current = Line->First; | |||
365 | LineEnd = Line->Last; | |||
366 | // clang-format comments toggle formatting on/off. | |||
367 | // This is tracked in FormattingOff here and on JsModuleReference. | |||
368 | while (Current && Current->is(tok::comment)) { | |||
369 | StringRef CommentText = Current->TokenText.trim(); | |||
370 | if (CommentText == "// clang-format off") { | |||
371 | FormattingOff = true; | |||
372 | } else if (CommentText == "// clang-format on") { | |||
373 | FormattingOff = false; | |||
374 | // Special case: consider a trailing "clang-format on" line to be part | |||
375 | // of the module reference, so that it gets moved around together with | |||
376 | // it (as opposed to the next module reference, which might get sorted | |||
377 | // around). | |||
378 | if (!References.empty()) { | |||
379 | References.back().Range.setEnd(Current->Tok.getEndLoc()); | |||
380 | Start = Current->Tok.getEndLoc().getLocWithOffset(1); | |||
381 | } | |||
382 | } | |||
383 | // Handle all clang-format comments on a line, e.g. for an empty block. | |||
384 | Current = Current->Next; | |||
385 | } | |||
386 | skipComments(); | |||
387 | if (Start.isInvalid() || References.empty()) | |||
388 | // After the first file level comment, consider line comments to be part | |||
389 | // of the import that immediately follows them by using the previously | |||
390 | // set Start. | |||
391 | Start = Line->First->Tok.getLocation(); | |||
| ||||
392 | if (!Current) { | |||
393 | // Only comments on this line. Could be the first non-import line. | |||
394 | FirstNonImportLine = Line; | |||
395 | continue; | |||
396 | } | |||
397 | JsModuleReference Reference; | |||
398 | Reference.FormattingOff = FormattingOff; | |||
399 | Reference.Range.setBegin(Start); | |||
400 | if (!parseModuleReference(Keywords, Reference)) { | |||
401 | if (!FirstNonImportLine) | |||
402 | FirstNonImportLine = Line; // if no comment before. | |||
403 | break; | |||
404 | } | |||
405 | FirstNonImportLine = nullptr; | |||
406 | AnyImportAffected = AnyImportAffected || Line->Affected; | |||
407 | Reference.Range.setEnd(LineEnd->Tok.getEndLoc()); | |||
408 | LLVM_DEBUG({do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { { llvm::dbgs() << "JsModuleReference: {" << "formatting_off: " << Reference.FormattingOff << ", is_export: " << Reference.IsExport << ", cat: " << Reference.Category << ", url: " << Reference.URL << ", prefix: " << Reference.Prefix ; for (const JsImportedSymbol &Symbol : Reference.Symbols ) llvm::dbgs() << ", " << Symbol.Symbol << " as " << Symbol.Alias; llvm::dbgs() << ", text: " << getSourceText(Reference.Range); llvm::dbgs() << "}\n"; }; } } while (false) | |||
409 | llvm::dbgs() << "JsModuleReference: {"do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { { llvm::dbgs() << "JsModuleReference: {" << "formatting_off: " << Reference.FormattingOff << ", is_export: " << Reference.IsExport << ", cat: " << Reference.Category << ", url: " << Reference.URL << ", prefix: " << Reference.Prefix ; for (const JsImportedSymbol &Symbol : Reference.Symbols ) llvm::dbgs() << ", " << Symbol.Symbol << " as " << Symbol.Alias; llvm::dbgs() << ", text: " << getSourceText(Reference.Range); llvm::dbgs() << "}\n"; }; } } while (false) | |||
410 | << "formatting_off: " << Reference.FormattingOffdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { { llvm::dbgs() << "JsModuleReference: {" << "formatting_off: " << Reference.FormattingOff << ", is_export: " << Reference.IsExport << ", cat: " << Reference.Category << ", url: " << Reference.URL << ", prefix: " << Reference.Prefix ; for (const JsImportedSymbol &Symbol : Reference.Symbols ) llvm::dbgs() << ", " << Symbol.Symbol << " as " << Symbol.Alias; llvm::dbgs() << ", text: " << getSourceText(Reference.Range); llvm::dbgs() << "}\n"; }; } } while (false) | |||
411 | << ", is_export: " << Reference.IsExportdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { { llvm::dbgs() << "JsModuleReference: {" << "formatting_off: " << Reference.FormattingOff << ", is_export: " << Reference.IsExport << ", cat: " << Reference.Category << ", url: " << Reference.URL << ", prefix: " << Reference.Prefix ; for (const JsImportedSymbol &Symbol : Reference.Symbols ) llvm::dbgs() << ", " << Symbol.Symbol << " as " << Symbol.Alias; llvm::dbgs() << ", text: " << getSourceText(Reference.Range); llvm::dbgs() << "}\n"; }; } } while (false) | |||
412 | << ", cat: " << Reference.Categorydo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { { llvm::dbgs() << "JsModuleReference: {" << "formatting_off: " << Reference.FormattingOff << ", is_export: " << Reference.IsExport << ", cat: " << Reference.Category << ", url: " << Reference.URL << ", prefix: " << Reference.Prefix ; for (const JsImportedSymbol &Symbol : Reference.Symbols ) llvm::dbgs() << ", " << Symbol.Symbol << " as " << Symbol.Alias; llvm::dbgs() << ", text: " << getSourceText(Reference.Range); llvm::dbgs() << "}\n"; }; } } while (false) | |||
413 | << ", url: " << Reference.URLdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { { llvm::dbgs() << "JsModuleReference: {" << "formatting_off: " << Reference.FormattingOff << ", is_export: " << Reference.IsExport << ", cat: " << Reference.Category << ", url: " << Reference.URL << ", prefix: " << Reference.Prefix ; for (const JsImportedSymbol &Symbol : Reference.Symbols ) llvm::dbgs() << ", " << Symbol.Symbol << " as " << Symbol.Alias; llvm::dbgs() << ", text: " << getSourceText(Reference.Range); llvm::dbgs() << "}\n"; }; } } while (false) | |||
414 | << ", prefix: " << Reference.Prefix;do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { { llvm::dbgs() << "JsModuleReference: {" << "formatting_off: " << Reference.FormattingOff << ", is_export: " << Reference.IsExport << ", cat: " << Reference.Category << ", url: " << Reference.URL << ", prefix: " << Reference.Prefix ; for (const JsImportedSymbol &Symbol : Reference.Symbols ) llvm::dbgs() << ", " << Symbol.Symbol << " as " << Symbol.Alias; llvm::dbgs() << ", text: " << getSourceText(Reference.Range); llvm::dbgs() << "}\n"; }; } } while (false) | |||
415 | for (const JsImportedSymbol &Symbol : Reference.Symbols)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { { llvm::dbgs() << "JsModuleReference: {" << "formatting_off: " << Reference.FormattingOff << ", is_export: " << Reference.IsExport << ", cat: " << Reference.Category << ", url: " << Reference.URL << ", prefix: " << Reference.Prefix ; for (const JsImportedSymbol &Symbol : Reference.Symbols ) llvm::dbgs() << ", " << Symbol.Symbol << " as " << Symbol.Alias; llvm::dbgs() << ", text: " << getSourceText(Reference.Range); llvm::dbgs() << "}\n"; }; } } while (false) | |||
416 | llvm::dbgs() << ", " << Symbol.Symbol << " as " << Symbol.Alias;do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { { llvm::dbgs() << "JsModuleReference: {" << "formatting_off: " << Reference.FormattingOff << ", is_export: " << Reference.IsExport << ", cat: " << Reference.Category << ", url: " << Reference.URL << ", prefix: " << Reference.Prefix ; for (const JsImportedSymbol &Symbol : Reference.Symbols ) llvm::dbgs() << ", " << Symbol.Symbol << " as " << Symbol.Alias; llvm::dbgs() << ", text: " << getSourceText(Reference.Range); llvm::dbgs() << "}\n"; }; } } while (false) | |||
417 | llvm::dbgs() << ", text: " << getSourceText(Reference.Range);do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { { llvm::dbgs() << "JsModuleReference: {" << "formatting_off: " << Reference.FormattingOff << ", is_export: " << Reference.IsExport << ", cat: " << Reference.Category << ", url: " << Reference.URL << ", prefix: " << Reference.Prefix ; for (const JsImportedSymbol &Symbol : Reference.Symbols ) llvm::dbgs() << ", " << Symbol.Symbol << " as " << Symbol.Alias; llvm::dbgs() << ", text: " << getSourceText(Reference.Range); llvm::dbgs() << "}\n"; }; } } while (false) | |||
418 | llvm::dbgs() << "}\n";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { { llvm::dbgs() << "JsModuleReference: {" << "formatting_off: " << Reference.FormattingOff << ", is_export: " << Reference.IsExport << ", cat: " << Reference.Category << ", url: " << Reference.URL << ", prefix: " << Reference.Prefix ; for (const JsImportedSymbol &Symbol : Reference.Symbols ) llvm::dbgs() << ", " << Symbol.Symbol << " as " << Symbol.Alias; llvm::dbgs() << ", text: " << getSourceText(Reference.Range); llvm::dbgs() << "}\n"; }; } } while (false) | |||
419 | })do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("format-formatter")) { { llvm::dbgs() << "JsModuleReference: {" << "formatting_off: " << Reference.FormattingOff << ", is_export: " << Reference.IsExport << ", cat: " << Reference.Category << ", url: " << Reference.URL << ", prefix: " << Reference.Prefix ; for (const JsImportedSymbol &Symbol : Reference.Symbols ) llvm::dbgs() << ", " << Symbol.Symbol << " as " << Symbol.Alias; llvm::dbgs() << ", text: " << getSourceText(Reference.Range); llvm::dbgs() << "}\n"; }; } } while (false); | |||
420 | References.push_back(Reference); | |||
421 | Start = SourceLocation(); | |||
422 | } | |||
423 | // Sort imports if any import line was affected. | |||
424 | if (!AnyImportAffected) | |||
425 | References.clear(); | |||
426 | return std::make_pair(References, FirstNonImportLine); | |||
427 | } | |||
428 | ||||
429 | // Parses a JavaScript/ECMAScript 6 module reference. | |||
430 | // See http://www.ecma-international.org/ecma-262/6.0/#sec-scripts-and-modules | |||
431 | // for grammar EBNF (production ModuleItem). | |||
432 | bool parseModuleReference(const AdditionalKeywords &Keywords, | |||
433 | JsModuleReference &Reference) { | |||
434 | if (!Current || !Current->isOneOf(Keywords.kw_import, tok::kw_export)) | |||
435 | return false; | |||
436 | Reference.IsExport = Current->is(tok::kw_export); | |||
437 | ||||
438 | nextToken(); | |||
439 | if (Current->isStringLiteral() && !Reference.IsExport) { | |||
440 | // "import 'side-effect';" | |||
441 | Reference.Category = JsModuleReference::ReferenceCategory::SIDE_EFFECT; | |||
442 | Reference.URL = | |||
443 | Current->TokenText.substr(1, Current->TokenText.size() - 2); | |||
444 | return true; | |||
445 | } | |||
446 | ||||
447 | if (!parseModuleBindings(Keywords, Reference)) | |||
448 | return false; | |||
449 | ||||
450 | if (Current->is(Keywords.kw_from)) { | |||
451 | // imports have a 'from' clause, exports might not. | |||
452 | nextToken(); | |||
453 | if (!Current->isStringLiteral()) | |||
454 | return false; | |||
455 | // URL = TokenText without the quotes. | |||
456 | Reference.URL = | |||
457 | Current->TokenText.substr(1, Current->TokenText.size() - 2); | |||
458 | if (Reference.URL.startswith("..")) | |||
459 | Reference.Category = | |||
460 | JsModuleReference::ReferenceCategory::RELATIVE_PARENT; | |||
461 | else if (Reference.URL.startswith(".")) | |||
462 | Reference.Category = JsModuleReference::ReferenceCategory::RELATIVE; | |||
463 | else | |||
464 | Reference.Category = JsModuleReference::ReferenceCategory::ABSOLUTE; | |||
465 | } else { | |||
466 | // w/o URL groups with "empty". | |||
467 | Reference.Category = JsModuleReference::ReferenceCategory::RELATIVE; | |||
468 | } | |||
469 | return true; | |||
470 | } | |||
471 | ||||
472 | bool parseModuleBindings(const AdditionalKeywords &Keywords, | |||
473 | JsModuleReference &Reference) { | |||
474 | if (parseStarBinding(Keywords, Reference)) | |||
475 | return true; | |||
476 | return parseNamedBindings(Keywords, Reference); | |||
477 | } | |||
478 | ||||
479 | bool parseStarBinding(const AdditionalKeywords &Keywords, | |||
480 | JsModuleReference &Reference) { | |||
481 | // * as prefix from '...'; | |||
482 | if (Current->isNot(tok::star)) | |||
483 | return false; | |||
484 | nextToken(); | |||
485 | if (Current->isNot(Keywords.kw_as)) | |||
486 | return false; | |||
487 | nextToken(); | |||
488 | if (Current->isNot(tok::identifier)) | |||
489 | return false; | |||
490 | Reference.Prefix = Current->TokenText; | |||
491 | nextToken(); | |||
492 | return true; | |||
493 | } | |||
494 | ||||
495 | bool parseNamedBindings(const AdditionalKeywords &Keywords, | |||
496 | JsModuleReference &Reference) { | |||
497 | // eat a potential "import X, " prefix. | |||
498 | if (Current->is(tok::identifier)) { | |||
499 | Reference.DefaultImport = Current->TokenText; | |||
500 | nextToken(); | |||
501 | if (Current->is(Keywords.kw_from)) | |||
502 | return true; | |||
503 | if (Current->isNot(tok::comma)) | |||
504 | return false; | |||
505 | nextToken(); // eat comma. | |||
506 | } | |||
507 | if (Current->isNot(tok::l_brace)) | |||
508 | return false; | |||
509 | ||||
510 | // {sym as alias, sym2 as ...} from '...'; | |||
511 | Reference.SymbolsStart = Current->Tok.getEndLoc(); | |||
512 | while (Current->isNot(tok::r_brace)) { | |||
513 | nextToken(); | |||
514 | if (Current->is(tok::r_brace)) | |||
515 | break; | |||
516 | if (!Current->isOneOf(tok::identifier, tok::kw_default)) | |||
517 | return false; | |||
518 | ||||
519 | JsImportedSymbol Symbol; | |||
520 | Symbol.Symbol = Current->TokenText; | |||
521 | // Make sure to include any preceding comments. | |||
522 | Symbol.Range.setBegin( | |||
523 | Current->getPreviousNonComment()->Next->WhitespaceRange.getBegin()); | |||
524 | nextToken(); | |||
525 | ||||
526 | if (Current->is(Keywords.kw_as)) { | |||
527 | nextToken(); | |||
528 | if (!Current->isOneOf(tok::identifier, tok::kw_default)) | |||
529 | return false; | |||
530 | Symbol.Alias = Current->TokenText; | |||
531 | nextToken(); | |||
532 | } | |||
533 | Symbol.Range.setEnd(Current->Tok.getLocation()); | |||
534 | Reference.Symbols.push_back(Symbol); | |||
535 | ||||
536 | if (!Current->isOneOf(tok::r_brace, tok::comma)) | |||
537 | return false; | |||
538 | } | |||
539 | Reference.SymbolsEnd = Current->Tok.getLocation(); | |||
540 | // For named imports with a trailing comma ("import {X,}"), consider the | |||
541 | // comma to be the end of the import list, so that it doesn't get removed. | |||
542 | if (Current->Previous->is(tok::comma)) | |||
543 | Reference.SymbolsEnd = Current->Previous->Tok.getLocation(); | |||
544 | nextToken(); // consume r_brace | |||
545 | return true; | |||
546 | } | |||
547 | }; | |||
548 | ||||
549 | tooling::Replacements sortJavaScriptImports(const FormatStyle &Style, | |||
550 | StringRef Code, | |||
551 | ArrayRef<tooling::Range> Ranges, | |||
552 | StringRef FileName) { | |||
553 | // FIXME: Cursor support. | |||
554 | auto Env = Environment::make(Code, FileName, Ranges); | |||
555 | if (!Env) | |||
556 | return {}; | |||
557 | return JavaScriptImportSorter(*Env, Style).process().first; | |||
558 | } | |||
559 | ||||
560 | } // end namespace format | |||
561 | } // end namespace clang |
1 | //===- SourceLocation.h - Compact identifier for Source Files ---*- 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 | /// \file |
10 | /// Defines the clang::SourceLocation class and associated facilities. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef LLVM_CLANG_BASIC_SOURCELOCATION_H |
15 | #define LLVM_CLANG_BASIC_SOURCELOCATION_H |
16 | |
17 | #include "clang/Basic/LLVM.h" |
18 | #include "llvm/ADT/StringRef.h" |
19 | #include <cassert> |
20 | #include <cstdint> |
21 | #include <string> |
22 | #include <utility> |
23 | |
24 | namespace llvm { |
25 | |
26 | class FoldingSetNodeID; |
27 | template <typename T> struct FoldingSetTrait; |
28 | |
29 | } // namespace llvm |
30 | |
31 | namespace clang { |
32 | |
33 | class SourceManager; |
34 | |
35 | /// An opaque identifier used by SourceManager which refers to a |
36 | /// source file (MemoryBuffer) along with its \#include path and \#line data. |
37 | /// |
38 | class FileID { |
39 | /// A mostly-opaque identifier, where 0 is "invalid", >0 is |
40 | /// this module, and <-1 is something loaded from another module. |
41 | int ID = 0; |
42 | |
43 | public: |
44 | bool isValid() const { return ID != 0; } |
45 | bool isInvalid() const { return ID == 0; } |
46 | |
47 | bool operator==(const FileID &RHS) const { return ID == RHS.ID; } |
48 | bool operator<(const FileID &RHS) const { return ID < RHS.ID; } |
49 | bool operator<=(const FileID &RHS) const { return ID <= RHS.ID; } |
50 | bool operator!=(const FileID &RHS) const { return !(*this == RHS); } |
51 | bool operator>(const FileID &RHS) const { return RHS < *this; } |
52 | bool operator>=(const FileID &RHS) const { return RHS <= *this; } |
53 | |
54 | static FileID getSentinel() { return get(-1); } |
55 | unsigned getHashValue() const { return static_cast<unsigned>(ID); } |
56 | |
57 | private: |
58 | friend class ASTWriter; |
59 | friend class ASTReader; |
60 | friend class SourceManager; |
61 | |
62 | static FileID get(int V) { |
63 | FileID F; |
64 | F.ID = V; |
65 | return F; |
66 | } |
67 | |
68 | int getOpaqueValue() const { return ID; } |
69 | }; |
70 | |
71 | /// Encodes a location in the source. The SourceManager can decode this |
72 | /// to get at the full include stack, line and column information. |
73 | /// |
74 | /// Technically, a source location is simply an offset into the manager's view |
75 | /// of the input source, which is all input buffers (including macro |
76 | /// expansions) concatenated in an effectively arbitrary order. The manager |
77 | /// actually maintains two blocks of input buffers. One, starting at offset |
78 | /// 0 and growing upwards, contains all buffers from this module. The other, |
79 | /// starting at the highest possible offset and growing downwards, contains |
80 | /// buffers of loaded modules. |
81 | /// |
82 | /// In addition, one bit of SourceLocation is used for quick access to the |
83 | /// information whether the location is in a file or a macro expansion. |
84 | /// |
85 | /// It is important that this type remains small. It is currently 32 bits wide. |
86 | class SourceLocation { |
87 | friend class ASTReader; |
88 | friend class ASTWriter; |
89 | friend class SourceManager; |
90 | friend struct llvm::FoldingSetTrait<SourceLocation>; |
91 | |
92 | public: |
93 | using UIntTy = uint32_t; |
94 | using IntTy = int32_t; |
95 | |
96 | private: |
97 | UIntTy ID = 0; |
98 | |
99 | enum : UIntTy { MacroIDBit = 1ULL << (8 * sizeof(UIntTy) - 1) }; |
100 | |
101 | public: |
102 | bool isFileID() const { return (ID & MacroIDBit) == 0; } |
103 | bool isMacroID() const { return (ID & MacroIDBit) != 0; } |
104 | |
105 | /// Return true if this is a valid SourceLocation object. |
106 | /// |
107 | /// Invalid SourceLocations are often used when events have no corresponding |
108 | /// location in the source (e.g. a diagnostic is required for a command line |
109 | /// option). |
110 | bool isValid() const { return ID != 0; } |
111 | bool isInvalid() const { return ID == 0; } |
112 | |
113 | private: |
114 | /// Return the offset into the manager's global input view. |
115 | UIntTy getOffset() const { return ID & ~MacroIDBit; } |
116 | |
117 | static SourceLocation getFileLoc(UIntTy ID) { |
118 | assert((ID & MacroIDBit) == 0 && "Ran out of source locations!")(static_cast <bool> ((ID & MacroIDBit) == 0 && "Ran out of source locations!") ? void (0) : __assert_fail ( "(ID & MacroIDBit) == 0 && \"Ran out of source locations!\"" , "clang/include/clang/Basic/SourceLocation.h", 118, __extension__ __PRETTY_FUNCTION__)); |
119 | SourceLocation L; |
120 | L.ID = ID; |
121 | return L; |
122 | } |
123 | |
124 | static SourceLocation getMacroLoc(UIntTy ID) { |
125 | assert((ID & MacroIDBit) == 0 && "Ran out of source locations!")(static_cast <bool> ((ID & MacroIDBit) == 0 && "Ran out of source locations!") ? void (0) : __assert_fail ( "(ID & MacroIDBit) == 0 && \"Ran out of source locations!\"" , "clang/include/clang/Basic/SourceLocation.h", 125, __extension__ __PRETTY_FUNCTION__)); |
126 | SourceLocation L; |
127 | L.ID = MacroIDBit | ID; |
128 | return L; |
129 | } |
130 | |
131 | public: |
132 | /// Return a source location with the specified offset from this |
133 | /// SourceLocation. |
134 | SourceLocation getLocWithOffset(IntTy Offset) const { |
135 | assert(((getOffset()+Offset) & MacroIDBit) == 0 && "offset overflow")(static_cast <bool> (((getOffset()+Offset) & MacroIDBit ) == 0 && "offset overflow") ? void (0) : __assert_fail ("((getOffset()+Offset) & MacroIDBit) == 0 && \"offset overflow\"" , "clang/include/clang/Basic/SourceLocation.h", 135, __extension__ __PRETTY_FUNCTION__)); |
136 | SourceLocation L; |
137 | L.ID = ID+Offset; |
138 | return L; |
139 | } |
140 | |
141 | /// When a SourceLocation itself cannot be used, this returns |
142 | /// an (opaque) 32-bit integer encoding for it. |
143 | /// |
144 | /// This should only be passed to SourceLocation::getFromRawEncoding, it |
145 | /// should not be inspected directly. |
146 | UIntTy getRawEncoding() const { return ID; } |
147 | |
148 | /// Turn a raw encoding of a SourceLocation object into |
149 | /// a real SourceLocation. |
150 | /// |
151 | /// \see getRawEncoding. |
152 | static SourceLocation getFromRawEncoding(UIntTy Encoding) { |
153 | SourceLocation X; |
154 | X.ID = Encoding; |
155 | return X; |
156 | } |
157 | |
158 | /// When a SourceLocation itself cannot be used, this returns |
159 | /// an (opaque) pointer encoding for it. |
160 | /// |
161 | /// This should only be passed to SourceLocation::getFromPtrEncoding, it |
162 | /// should not be inspected directly. |
163 | void* getPtrEncoding() const { |
164 | // Double cast to avoid a warning "cast to pointer from integer of different |
165 | // size". |
166 | return (void*)(uintptr_t)getRawEncoding(); |
167 | } |
168 | |
169 | /// Turn a pointer encoding of a SourceLocation object back |
170 | /// into a real SourceLocation. |
171 | static SourceLocation getFromPtrEncoding(const void *Encoding) { |
172 | return getFromRawEncoding((SourceLocation::UIntTy)(uintptr_t)Encoding); |
173 | } |
174 | |
175 | static bool isPairOfFileLocations(SourceLocation Start, SourceLocation End) { |
176 | return Start.isValid() && Start.isFileID() && End.isValid() && |
177 | End.isFileID(); |
178 | } |
179 | |
180 | unsigned getHashValue() const; |
181 | void print(raw_ostream &OS, const SourceManager &SM) const; |
182 | std::string printToString(const SourceManager &SM) const; |
183 | void dump(const SourceManager &SM) const; |
184 | }; |
185 | |
186 | inline bool operator==(const SourceLocation &LHS, const SourceLocation &RHS) { |
187 | return LHS.getRawEncoding() == RHS.getRawEncoding(); |
188 | } |
189 | |
190 | inline bool operator!=(const SourceLocation &LHS, const SourceLocation &RHS) { |
191 | return !(LHS == RHS); |
192 | } |
193 | |
194 | // Ordering is meaningful only if LHS and RHS have the same FileID! |
195 | // Otherwise use SourceManager::isBeforeInTranslationUnit(). |
196 | inline bool operator<(const SourceLocation &LHS, const SourceLocation &RHS) { |
197 | return LHS.getRawEncoding() < RHS.getRawEncoding(); |
198 | } |
199 | inline bool operator>(const SourceLocation &LHS, const SourceLocation &RHS) { |
200 | return LHS.getRawEncoding() > RHS.getRawEncoding(); |
201 | } |
202 | inline bool operator<=(const SourceLocation &LHS, const SourceLocation &RHS) { |
203 | return LHS.getRawEncoding() <= RHS.getRawEncoding(); |
204 | } |
205 | inline bool operator>=(const SourceLocation &LHS, const SourceLocation &RHS) { |
206 | return LHS.getRawEncoding() >= RHS.getRawEncoding(); |
207 | } |
208 | |
209 | /// A trivial tuple used to represent a source range. |
210 | class SourceRange { |
211 | SourceLocation B; |
212 | SourceLocation E; |
213 | |
214 | public: |
215 | SourceRange() = default; |
216 | SourceRange(SourceLocation loc) : B(loc), E(loc) {} |
217 | SourceRange(SourceLocation begin, SourceLocation end) : B(begin), E(end) {} |
218 | |
219 | SourceLocation getBegin() const { return B; } |
220 | SourceLocation getEnd() const { return E; } |
221 | |
222 | void setBegin(SourceLocation b) { B = b; } |
223 | void setEnd(SourceLocation e) { E = e; } |
224 | |
225 | bool isValid() const { return B.isValid() && E.isValid(); } |
226 | bool isInvalid() const { return !isValid(); } |
227 | |
228 | bool operator==(const SourceRange &X) const { |
229 | return B == X.B && E == X.E; |
230 | } |
231 | |
232 | bool operator!=(const SourceRange &X) const { |
233 | return B != X.B || E != X.E; |
234 | } |
235 | |
236 | // Returns true iff other is wholly contained within this range. |
237 | bool fullyContains(const SourceRange &other) const { |
238 | return B <= other.B && E >= other.E; |
239 | } |
240 | |
241 | void print(raw_ostream &OS, const SourceManager &SM) const; |
242 | std::string printToString(const SourceManager &SM) const; |
243 | void dump(const SourceManager &SM) const; |
244 | }; |
245 | |
246 | /// Represents a character-granular source range. |
247 | /// |
248 | /// The underlying SourceRange can either specify the starting/ending character |
249 | /// of the range, or it can specify the start of the range and the start of the |
250 | /// last token of the range (a "token range"). In the token range case, the |
251 | /// size of the last token must be measured to determine the actual end of the |
252 | /// range. |
253 | class CharSourceRange { |
254 | SourceRange Range; |
255 | bool IsTokenRange = false; |
256 | |
257 | public: |
258 | CharSourceRange() = default; |
259 | CharSourceRange(SourceRange R, bool ITR) : Range(R), IsTokenRange(ITR) {} |
260 | |
261 | static CharSourceRange getTokenRange(SourceRange R) { |
262 | return CharSourceRange(R, true); |
263 | } |
264 | |
265 | static CharSourceRange getCharRange(SourceRange R) { |
266 | return CharSourceRange(R, false); |
267 | } |
268 | |
269 | static CharSourceRange getTokenRange(SourceLocation B, SourceLocation E) { |
270 | return getTokenRange(SourceRange(B, E)); |
271 | } |
272 | |
273 | static CharSourceRange getCharRange(SourceLocation B, SourceLocation E) { |
274 | return getCharRange(SourceRange(B, E)); |
275 | } |
276 | |
277 | /// Return true if the end of this range specifies the start of |
278 | /// the last token. Return false if the end of this range specifies the last |
279 | /// character in the range. |
280 | bool isTokenRange() const { return IsTokenRange; } |
281 | bool isCharRange() const { return !IsTokenRange; } |
282 | |
283 | SourceLocation getBegin() const { return Range.getBegin(); } |
284 | SourceLocation getEnd() const { return Range.getEnd(); } |
285 | SourceRange getAsRange() const { return Range; } |
286 | |
287 | void setBegin(SourceLocation b) { Range.setBegin(b); } |
288 | void setEnd(SourceLocation e) { Range.setEnd(e); } |
289 | void setTokenRange(bool TR) { IsTokenRange = TR; } |
290 | |
291 | bool isValid() const { return Range.isValid(); } |
292 | bool isInvalid() const { return !isValid(); } |
293 | }; |
294 | |
295 | /// Represents an unpacked "presumed" location which can be presented |
296 | /// to the user. |
297 | /// |
298 | /// A 'presumed' location can be modified by \#line and GNU line marker |
299 | /// directives and is always the expansion point of a normal location. |
300 | /// |
301 | /// You can get a PresumedLoc from a SourceLocation with SourceManager. |
302 | class PresumedLoc { |
303 | const char *Filename = nullptr; |
304 | FileID ID; |
305 | unsigned Line, Col; |
306 | SourceLocation IncludeLoc; |
307 | |
308 | public: |
309 | PresumedLoc() = default; |
310 | PresumedLoc(const char *FN, FileID FID, unsigned Ln, unsigned Co, |
311 | SourceLocation IL) |
312 | : Filename(FN), ID(FID), Line(Ln), Col(Co), IncludeLoc(IL) {} |
313 | |
314 | /// Return true if this object is invalid or uninitialized. |
315 | /// |
316 | /// This occurs when created with invalid source locations or when walking |
317 | /// off the top of a \#include stack. |
318 | bool isInvalid() const { return Filename == nullptr; } |
319 | bool isValid() const { return Filename != nullptr; } |
320 | |
321 | /// Return the presumed filename of this location. |
322 | /// |
323 | /// This can be affected by \#line etc. |
324 | const char *getFilename() const { |
325 | assert(isValid())(static_cast <bool> (isValid()) ? void (0) : __assert_fail ("isValid()", "clang/include/clang/Basic/SourceLocation.h", 325 , __extension__ __PRETTY_FUNCTION__)); |
326 | return Filename; |
327 | } |
328 | |
329 | FileID getFileID() const { |
330 | assert(isValid())(static_cast <bool> (isValid()) ? void (0) : __assert_fail ("isValid()", "clang/include/clang/Basic/SourceLocation.h", 330 , __extension__ __PRETTY_FUNCTION__)); |
331 | return ID; |
332 | } |
333 | |
334 | /// Return the presumed line number of this location. |
335 | /// |
336 | /// This can be affected by \#line etc. |
337 | unsigned getLine() const { |
338 | assert(isValid())(static_cast <bool> (isValid()) ? void (0) : __assert_fail ("isValid()", "clang/include/clang/Basic/SourceLocation.h", 338 , __extension__ __PRETTY_FUNCTION__)); |
339 | return Line; |
340 | } |
341 | |
342 | /// Return the presumed column number of this location. |
343 | /// |
344 | /// This cannot be affected by \#line, but is packaged here for convenience. |
345 | unsigned getColumn() const { |
346 | assert(isValid())(static_cast <bool> (isValid()) ? void (0) : __assert_fail ("isValid()", "clang/include/clang/Basic/SourceLocation.h", 346 , __extension__ __PRETTY_FUNCTION__)); |
347 | return Col; |
348 | } |
349 | |
350 | /// Return the presumed include location of this location. |
351 | /// |
352 | /// This can be affected by GNU linemarker directives. |
353 | SourceLocation getIncludeLoc() const { |
354 | assert(isValid())(static_cast <bool> (isValid()) ? void (0) : __assert_fail ("isValid()", "clang/include/clang/Basic/SourceLocation.h", 354 , __extension__ __PRETTY_FUNCTION__)); |
355 | return IncludeLoc; |
356 | } |
357 | }; |
358 | |
359 | class FileEntry; |
360 | |
361 | /// A SourceLocation and its associated SourceManager. |
362 | /// |
363 | /// This is useful for argument passing to functions that expect both objects. |
364 | /// |
365 | /// This class does not guarantee the presence of either the SourceManager or |
366 | /// a valid SourceLocation. Clients should use `isValid()` and `hasManager()` |
367 | /// before calling the member functions. |
368 | class FullSourceLoc : public SourceLocation { |
369 | const SourceManager *SrcMgr = nullptr; |
370 | |
371 | public: |
372 | /// Creates a FullSourceLoc where isValid() returns \c false. |
373 | FullSourceLoc() = default; |
374 | |
375 | explicit FullSourceLoc(SourceLocation Loc, const SourceManager &SM) |
376 | : SourceLocation(Loc), SrcMgr(&SM) {} |
377 | |
378 | /// Checks whether the SourceManager is present. |
379 | bool hasManager() const { return SrcMgr != nullptr; } |
380 | |
381 | /// \pre hasManager() |
382 | const SourceManager &getManager() const { |
383 | assert(SrcMgr && "SourceManager is NULL.")(static_cast <bool> (SrcMgr && "SourceManager is NULL." ) ? void (0) : __assert_fail ("SrcMgr && \"SourceManager is NULL.\"" , "clang/include/clang/Basic/SourceLocation.h", 383, __extension__ __PRETTY_FUNCTION__)); |
384 | return *SrcMgr; |
385 | } |
386 | |
387 | FileID getFileID() const; |
388 | |
389 | FullSourceLoc getExpansionLoc() const; |
390 | FullSourceLoc getSpellingLoc() const; |
391 | FullSourceLoc getFileLoc() const; |
392 | PresumedLoc getPresumedLoc(bool UseLineDirectives = true) const; |
393 | bool isMacroArgExpansion(FullSourceLoc *StartLoc = nullptr) const; |
394 | FullSourceLoc getImmediateMacroCallerLoc() const; |
395 | std::pair<FullSourceLoc, StringRef> getModuleImportLoc() const; |
396 | unsigned getFileOffset() const; |
397 | |
398 | unsigned getExpansionLineNumber(bool *Invalid = nullptr) const; |
399 | unsigned getExpansionColumnNumber(bool *Invalid = nullptr) const; |
400 | |
401 | unsigned getSpellingLineNumber(bool *Invalid = nullptr) const; |
402 | unsigned getSpellingColumnNumber(bool *Invalid = nullptr) const; |
403 | |
404 | const char *getCharacterData(bool *Invalid = nullptr) const; |
405 | |
406 | unsigned getLineNumber(bool *Invalid = nullptr) const; |
407 | unsigned getColumnNumber(bool *Invalid = nullptr) const; |
408 | |
409 | const FileEntry *getFileEntry() const; |
410 | |
411 | /// Return a StringRef to the source buffer data for the |
412 | /// specified FileID. |
413 | StringRef getBufferData(bool *Invalid = nullptr) const; |
414 | |
415 | /// Decompose the specified location into a raw FileID + Offset pair. |
416 | /// |
417 | /// The first element is the FileID, the second is the offset from the |
418 | /// start of the buffer of the location. |
419 | std::pair<FileID, unsigned> getDecomposedLoc() const; |
420 | |
421 | bool isInSystemHeader() const; |
422 | |
423 | /// Determines the order of 2 source locations in the translation unit. |
424 | /// |
425 | /// \returns true if this source location comes before 'Loc', false otherwise. |
426 | bool isBeforeInTranslationUnitThan(SourceLocation Loc) const; |
427 | |
428 | /// Determines the order of 2 source locations in the translation unit. |
429 | /// |
430 | /// \returns true if this source location comes before 'Loc', false otherwise. |
431 | bool isBeforeInTranslationUnitThan(FullSourceLoc Loc) const { |
432 | assert(Loc.isValid())(static_cast <bool> (Loc.isValid()) ? void (0) : __assert_fail ("Loc.isValid()", "clang/include/clang/Basic/SourceLocation.h" , 432, __extension__ __PRETTY_FUNCTION__)); |
433 | assert(SrcMgr == Loc.SrcMgr && "Loc comes from another SourceManager!")(static_cast <bool> (SrcMgr == Loc.SrcMgr && "Loc comes from another SourceManager!" ) ? void (0) : __assert_fail ("SrcMgr == Loc.SrcMgr && \"Loc comes from another SourceManager!\"" , "clang/include/clang/Basic/SourceLocation.h", 433, __extension__ __PRETTY_FUNCTION__)); |
434 | return isBeforeInTranslationUnitThan((SourceLocation)Loc); |
435 | } |
436 | |
437 | /// Comparison function class, useful for sorting FullSourceLocs. |
438 | struct BeforeThanCompare { |
439 | bool operator()(const FullSourceLoc& lhs, const FullSourceLoc& rhs) const { |
440 | return lhs.isBeforeInTranslationUnitThan(rhs); |
441 | } |
442 | }; |
443 | |
444 | /// Prints information about this FullSourceLoc to stderr. |
445 | /// |
446 | /// This is useful for debugging. |
447 | void dump() const; |
448 | |
449 | friend bool |
450 | operator==(const FullSourceLoc &LHS, const FullSourceLoc &RHS) { |
451 | return LHS.getRawEncoding() == RHS.getRawEncoding() && |
452 | LHS.SrcMgr == RHS.SrcMgr; |
453 | } |
454 | |
455 | friend bool |
456 | operator!=(const FullSourceLoc &LHS, const FullSourceLoc &RHS) { |
457 | return !(LHS == RHS); |
458 | } |
459 | }; |
460 | |
461 | } // namespace clang |
462 | |
463 | namespace llvm { |
464 | |
465 | /// Define DenseMapInfo so that FileID's can be used as keys in DenseMap and |
466 | /// DenseSets. |
467 | template <> |
468 | struct DenseMapInfo<clang::FileID, void> { |
469 | static clang::FileID getEmptyKey() { |
470 | return {}; |
471 | } |
472 | |
473 | static clang::FileID getTombstoneKey() { |
474 | return clang::FileID::getSentinel(); |
475 | } |
476 | |
477 | static unsigned getHashValue(clang::FileID S) { |
478 | return S.getHashValue(); |
479 | } |
480 | |
481 | static bool isEqual(clang::FileID LHS, clang::FileID RHS) { |
482 | return LHS == RHS; |
483 | } |
484 | }; |
485 | |
486 | /// Define DenseMapInfo so that SourceLocation's can be used as keys in |
487 | /// DenseMap and DenseSet. This trait class is eqivalent to |
488 | /// DenseMapInfo<unsigned> which uses SourceLocation::ID is used as a key. |
489 | template <> struct DenseMapInfo<clang::SourceLocation, void> { |
490 | static clang::SourceLocation getEmptyKey() { |
491 | constexpr clang::SourceLocation::UIntTy Zero = 0; |
492 | return clang::SourceLocation::getFromRawEncoding(~Zero); |
493 | } |
494 | |
495 | static clang::SourceLocation getTombstoneKey() { |
496 | constexpr clang::SourceLocation::UIntTy Zero = 0; |
497 | return clang::SourceLocation::getFromRawEncoding(~Zero - 1); |
498 | } |
499 | |
500 | static unsigned getHashValue(clang::SourceLocation Loc) { |
501 | return Loc.getHashValue(); |
502 | } |
503 | |
504 | static bool isEqual(clang::SourceLocation LHS, clang::SourceLocation RHS) { |
505 | return LHS == RHS; |
506 | } |
507 | }; |
508 | |
509 | // Allow calling FoldingSetNodeID::Add with SourceLocation object as parameter |
510 | template <> struct FoldingSetTrait<clang::SourceLocation> { |
511 | static void Profile(const clang::SourceLocation &X, FoldingSetNodeID &ID); |
512 | }; |
513 | |
514 | } // namespace llvm |
515 | |
516 | #endif // LLVM_CLANG_BASIC_SOURCELOCATION_H |