23#include "llvm/ADT/STLExtras.h"
24#include "llvm/ADT/SmallVector.h"
25#include "llvm/Support/Debug.h"
28#define DEBUG_TYPE "format-formatter"
33class FormatTokenLexer;
115 if (LHS.
URL.empty() != RHS.
URL.empty())
116 return LHS.
URL.empty() < RHS.
URL.empty();
117 if (
int Res = LHS.
URL.compare_insensitive(RHS.
URL))
134 FileContents(
Env.getSourceManager().getBufferData(
Env.getFileID())) {
139 std::pair<tooling::Replacements, unsigned>
149 std::tie(References, FirstNonImportLine) =
150 parseModuleReferences(Keywords, AnnotatedLines);
152 if (References.empty())
157 InsertionPoint.
setEnd(References[References.size() - 1].Range.getEnd());
159 References = sortModuleReferences(References);
161 std::string ReferencesText;
162 for (
unsigned I = 0,
E = References.size(); I !=
E; ++I) {
164 appendReference(ReferencesText,
Reference);
167 ReferencesText +=
"\n";
171 (
Reference.IsExport != References[I + 1].IsExport ||
172 Reference.Category != References[I + 1].Category)) {
173 ReferencesText +=
"\n";
177 StringRef PreviousText = getSourceText(InsertionPoint);
178 if (ReferencesText == PreviousText)
188 unsigned PreviousSize = PreviousText.size();
189 while (ReferencesText.size() < PreviousSize)
190 ReferencesText +=
" ";
194 !(FirstNonImportLine->
First->
is(tok::comment) &&
196 ReferencesText +=
"\n";
199 LLVM_DEBUG(llvm::dbgs() <<
"Replacing imports:\n"
200 << PreviousText <<
"\nwith:\n"
201 << ReferencesText <<
"\n");
208 llvm::errs() <<
toString(std::move(Err)) <<
"\n";
221 StringRef FileContents;
223 void skipComments() { Current = skipComments(Current); }
225 FormatToken *skipComments(FormatToken *Tok) {
226 while (Tok && Tok->is(tok::comment))
232 Current = Current->Next;
234 if (!Current || Current == LineEnd->
Next) {
237 Current = &invalidToken;
241 StringRef getSourceText(SourceRange
Range) {
242 return getSourceText(
Range.getBegin(),
Range.getEnd());
245 StringRef getSourceText(SourceLocation
Begin, SourceLocation End) {
247 return FileContents.substr(
SM.getFileOffset(
Begin),
248 SM.getFileOffset(End) -
SM.getFileOffset(
Begin));
255 SmallVector<JsModuleReference, 16>
256 sortModuleReferences(
const SmallVector<JsModuleReference, 16> &References) {
261 const auto *Start = References.begin();
262 SmallVector<JsModuleReference, 16> ReferencesSorted;
263 while (Start != References.end()) {
264 while (Start != References.end() && Start->FormattingOff) {
266 ReferencesSorted.push_back(*Start);
269 SmallVector<JsModuleReference, 16> SortChunk;
270 while (Start != References.end() && !Start->FormattingOff) {
272 SortChunk.push_back(*Start);
275 stable_sort(SortChunk);
276 mergeModuleReferences(SortChunk);
277 llvm::append_range(ReferencesSorted, SortChunk);
279 return ReferencesSorted;
291 void mergeModuleReferences(SmallVector<JsModuleReference, 16> &References) {
292 if (References.empty())
294 JsModuleReference *PreviousReference = References.begin();
295 auto *
Reference = std::next(References.begin());
304 Reference->IsExport != PreviousReference->IsExport ||
305 Reference->IsTypeOnly != PreviousReference->IsTypeOnly ||
306 !PreviousReference->Prefix.empty() || !
Reference->Prefix.empty() ||
307 !PreviousReference->DefaultImport.empty() ||
309 PreviousReference->URL !=
Reference->URL) {
315 PreviousReference->Symbols.append(
Reference->Symbols);
316 PreviousReference->SymbolsMerged =
true;
323 void appendReference(std::string &Buffer, JsModuleReference &
Reference) {
331 SmallVector<JsImportedSymbol, 1> Symbols =
Reference.Symbols;
333 [&](
const JsImportedSymbol &LHS,
const JsImportedSymbol &RHS) {
334 return LHS.Symbol.compare_insensitive(RHS.Symbol) < 0;
338 StringRef ReferenceStmt = getSourceText(
Reference.Range);
339 Buffer += ReferenceStmt;
345 if (!Symbols.empty()) {
346 Buffer += getSourceText(Symbols.front().Range);
347 for (
const JsImportedSymbol &Symbol : drop_begin(Symbols)) {
349 Buffer += getSourceText(Symbol.Range);
359 std::pair<SmallVector<JsModuleReference, 16>, AnnotatedLine *>
360 parseModuleReferences(
const AdditionalKeywords &Keywords,
361 SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) {
362 SmallVector<JsModuleReference, 16> References;
363 SourceLocation Start;
364 AnnotatedLine *FirstNonImportLine =
nullptr;
365 bool AnyImportAffected =
false;
366 bool FormattingOff =
false;
367 for (
auto *
Line : AnnotatedLines) {
369 Current =
Line->First;
370 LineEnd =
Line->Last;
373 while (Current && Current->is(tok::comment)) {
374 StringRef CommentText = Current->TokenText.trim();
376 FormattingOff =
true;
378 FormattingOff =
false;
383 if (!References.empty()) {
384 References.back().Range.setEnd(Current->Tok.getEndLoc());
385 Start = Current->Tok.getEndLoc().getLocWithOffset(1);
389 Current = Current->Next;
392 if (Start.isInvalid() || References.empty()) {
396 Start =
Line->First->Tok.getLocation();
400 FirstNonImportLine =
Line;
408 if (!parseModuleReference(Keywords,
Reference)) {
409 if (!FirstNonImportLine)
410 FirstNonImportLine =
Line;
413 FirstNonImportLine =
nullptr;
414 AnyImportAffected = AnyImportAffected ||
Line->Affected;
417 llvm::dbgs() <<
"JsModuleReference: {"
418 <<
"formatting_off: " <<
Reference.FormattingOff
423 for (
const JsImportedSymbol &Symbol :
Reference.Symbols)
424 llvm::dbgs() <<
", " << Symbol.Symbol <<
" as " << Symbol.Alias;
425 llvm::dbgs() <<
", text: " << getSourceText(
Reference.Range);
426 llvm::dbgs() <<
"}\n";
429 Start = SourceLocation();
432 if (!AnyImportAffected)
434 return std::make_pair(References, FirstNonImportLine);
440 bool parseModuleReference(
const AdditionalKeywords &Keywords,
442 if (!Current || !Current->isOneOf(Keywords.kw_import, tok::kw_export))
444 Reference.IsExport = Current->is(tok::kw_export);
447 if (Current->isStringLiteral() && !
Reference.IsExport) {
451 Current->TokenText.substr(1, Current->TokenText.size() - 2);
455 if (!parseModuleBindings(Keywords,
Reference))
458 if (Current->is(Keywords.kw_from)) {
461 if (!Current->isStringLiteral())
465 Current->TokenText.substr(1, Current->TokenText.size() - 2);
469 }
else if (
Reference.URL.starts_with(
".")) {
478 bool parseModuleBindings(
const AdditionalKeywords &Keywords,
480 if (parseStarBinding(Keywords,
Reference))
482 return parseNamedBindings(Keywords,
Reference);
485 bool parseStarBinding(
const AdditionalKeywords &Keywords,
488 if (Current->is(Keywords.kw_type) && Current->Next &&
489 Current->Next->is(tok::star)) {
493 if (Current->isNot(tok::star))
496 if (Current->isNot(Keywords.kw_as))
499 if (Current->isNot(tok::identifier))
506 bool parseNamedBindings(
const AdditionalKeywords &Keywords,
508 if (Current->is(Keywords.kw_type) && Current->Next &&
509 Current->Next->isOneOf(tok::identifier, tok::l_brace)) {
515 if (!
Reference.IsExport && Current->is(tok::identifier)) {
516 Reference.DefaultImport = Current->TokenText;
518 if (Current->is(Keywords.kw_from))
521 if (Current->is(tok::equal)) {
524 while (Current->is(tok::identifier)) {
526 if (Current->is(tok::semi))
528 if (Current->isNot(tok::period))
533 if (Current->isNot(tok::comma))
537 if (Current->isNot(tok::l_brace))
541 Reference.SymbolsStart = Current->Tok.getEndLoc();
542 while (Current->isNot(tok::r_brace)) {
544 if (Current->is(tok::r_brace))
546 auto IsIdentifier = [](
const auto *Tok) {
547 return Tok->isOneOf(tok::identifier, tok::kw_default, tok::kw_template);
549 bool isTypeOnly = Current->is(Keywords.kw_type) && Current->Next &&
550 IsIdentifier(Current->Next);
551 if (!isTypeOnly && !IsIdentifier(Current))
554 JsImportedSymbol Symbol;
556 Symbol.Range.setBegin(
557 Current->getPreviousNonComment()->Next->WhitespaceRange.getBegin());
560 Symbol.Symbol = Current->TokenText;
563 if (Current->is(Keywords.kw_as)) {
565 if (!IsIdentifier(Current))
567 Symbol.Alias = Current->TokenText;
570 Symbol.Range.setEnd(Current->Tok.getLocation());
573 if (!Current->isOneOf(tok::r_brace, tok::comma))
576 Reference.SymbolsEnd = Current->Tok.getLocation();
579 if (Current->Previous->is(tok::comma))
580 Reference.SymbolsEnd = Current->Previous->Tok.getLocation();
Defines the Diagnostic-related interfaces.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
static std::string toString(const clang::SanitizerSet &Sanitizers)
Produce a string containing comma-separated names of sanitizers in Sanitizers set.
This file implements a sorter for JavaScript ES6 imports.
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
This file declares an abstract TokenAnalyzer, and associated helper classes.
This file implements a token annotator, i.e.
Defines the clang::TokenKind enum and support functions.
static CharSourceRange getCharRange(SourceRange R)
Encodes a location in the source.
A trivial tuple used to represent a source range.
void setEnd(SourceLocation e)
SourceLocation getEndLoc() const
void startToken()
Reset all flags to cleared.
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.