| 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 |