clang: lib/Frontend/DiagnosticRenderer.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

19#include "llvm/ADT/ArrayRef.h"

20#include "llvm/ADT/DenseMap.h"

21#include "llvm/ADT/SmallString.h"

22#include "llvm/ADT/SmallVector.h"

23#include "llvm/ADT/StringRef.h"

24#include "llvm/Support/raw_ostream.h"

25#include

26#include

27#include

28#include

29

30using namespace clang;

31

34 : LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {}

35

37

38namespace {

39

42

43public:

45 : MergedFixits(MergedFixits) {}

46

47 void insert(SourceLocation loc, StringRef text) override {

49 }

50

51 void replace(CharSourceRange range, StringRef text) override {

53 }

54};

55

56}

57

62 for (const auto &Hint : FixItHints)

63 if (Hint.CodeToInsert.empty()) {

64 if (Hint.InsertFromRange.isValid())

66 Hint.InsertFromRange, false,

67 Hint.BeforePreviousInsertions);

68 else

69 commit.remove(Hint.RemoveRange);

70 } else {

71 if (Hint.RemoveRange.isTokenRange() ||

72 Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd())

73 commit.replace(Hint.RemoveRange, Hint.CodeToInsert);

74 else

75 commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert,

76 false, Hint.BeforePreviousInsertions);

77 }

78

80 if (Editor.commit(commit)) {

81 FixitReceiver Rec(MergedFixits);

83 }

84}

85

88 StringRef Message,

93

95

97

99 else {

100

102

104 if (!FixItHints.empty()) {

106 FixItHints = MergedFixits;

107 }

108

109 for (const auto &Hint : FixItHints)

110 if (Hint.RemoveRange.isValid())

111 MutableRanges.push_back(Hint.RemoveRange);

112

114

115

116 Loc = Loc.getFileLoc();

117

119

120

121

122 emitIncludeStack(Loc, PLoc, Level);

123

124

126 emitCaret(Loc, Level, MutableRanges, FixItHints);

127

128

129

131 emitMacroExpansions(UnexpandedLoc, Level, MutableRanges, FixItHints);

132 }

133 }

134

137

139}

140

143 Diag.getRanges(), Diag.getFixIts(),

145}

146

147void DiagnosticRenderer::emitBasicNote(StringRef Message) {

150}

151

152

153

154

155

156

157

158

159

160

161

162

168

169

171 return;

172

174

176 return;

177

178 if (IncludeLoc.isValid())

179 emitIncludeStackRecursively(IncludeLoc);

180 else {

181 emitModuleBuildStack(Loc.getManager());

182 emitImportStack(Loc);

183 }

184}

185

186

187

188void DiagnosticRenderer::emitIncludeStackRecursively(FullSourceLoc Loc) {

190 emitModuleBuildStack(Loc.getManager());

191 return;

192 }

193

196 return;

197

198

199

200

201 std::pair<FullSourceLoc, StringRef> Imported = Loc.getModuleImportLoc();

202 if (!Imported.second.empty()) {

203

204 emitImportStackRecursively(Imported.first, Imported.second);

205 return;

206 }

207

208

209 emitIncludeStackRecursively(

211

212

214}

215

216

219 emitModuleBuildStack(Loc.getManager());

220 return;

221 }

222

223 std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc();

224 emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second);

225}

226

227

228

229void DiagnosticRenderer::emitImportStackRecursively(FullSourceLoc Loc,

230 StringRef ModuleName) {

231 if (ModuleName.empty()) {

232 return;

233 }

234

236

237

238 std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc();

239 emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second);

240

241

243}

244

245

246

247void DiagnosticRenderer::emitModuleBuildStack(const SourceManager &SM) {

249 for (const auto &I : Stack) {

252 I.first);

253 }

254}

255

256

257

263 bool &IsTokenRange) {

264 assert(SM->getFileID(Loc) == MacroFileID);

265 if (MacroFileID == CaretFileID)

266 return Loc;

268 return {};

269

271

272 if (SM->isMacroArgExpansion(Loc)) {

273

274

275 if (std::binary_search(CommonArgExpansions.begin(),

276 CommonArgExpansions.end(), MacroFileID))

277 MacroRange =

279 MacroArgRange = SM->getImmediateExpansionRange(Loc);

280 } else {

281 MacroRange = SM->getImmediateExpansionRange(Loc);

282 MacroArgRange =

284 }

285

287 IsBegin ? MacroRange.getBegin() : MacroRange.getEnd();

288 if (MacroLocation.isValid()) {

289 MacroFileID = SM->getFileID(MacroLocation);

290 bool TokenRange = IsBegin ? IsTokenRange : MacroRange.isTokenRange();

291 MacroLocation =

293 CommonArgExpansions, IsBegin, SM, TokenRange);

294 if (MacroLocation.isValid()) {

295 IsTokenRange = TokenRange;

296 return MacroLocation;

297 }

298 }

299

300

301

302 if (!IsBegin)

304

306 IsBegin ? MacroArgRange.getBegin() : MacroArgRange.getEnd();

307 MacroFileID = SM->getFileID(MacroArgLocation);

309 CommonArgExpansions, IsBegin, SM, IsTokenRange);

310}

311

312

313

318 if (SM->isMacroArgExpansion(Loc)) {

319 IDs.push_back(SM->getFileID(Loc));

320 Loc = SM->getImmediateSpellingLoc(Loc);

321 } else {

322 auto ExpRange = SM->getImmediateExpansionRange(Loc);

323 Loc = IsBegin ? ExpRange.getBegin() : ExpRange.getEnd();

324 }

325 }

326}

327

328

329

337 llvm::sort(BeginArgExpansions);

338 llvm::sort(EndArgExpansions);

339 std::set_intersection(BeginArgExpansions.begin(), BeginArgExpansions.end(),

340 EndArgExpansions.begin(), EndArgExpansions.end(),

341 std::back_inserter(CommonArgExpansions));

342}

343

344

345

346

347

348

349

350

351

352

353

354static void

358

360

361 for (const auto &Range : Ranges) {

363 continue;

364

366 bool IsTokenRange = Range.isTokenRange();

367

369 FileID EndFileID = SM->getFileID(End);

370

371

372

373

374 llvm::SmallDenseMap<FileID, SourceLocation> BeginLocsMap;

375 while (Begin.isMacroID() && BeginFileID != EndFileID) {

376 BeginLocsMap[BeginFileID] = Begin;

377 Begin = SM->getImmediateExpansionRange(Begin).getBegin();

378 BeginFileID = SM->getFileID(Begin);

379 }

380

381

382 if (BeginFileID != EndFileID) {

383 while (End.isMacroID() && !BeginLocsMap.count(EndFileID)) {

384 auto Exp = SM->getImmediateExpansionRange(End);

385 IsTokenRange = Exp.isTokenRange();

386 End = Exp.getEnd();

387 EndFileID = SM->getFileID(End);

388 }

389 if (End.isMacroID()) {

390 Begin = BeginLocsMap[EndFileID];

391 BeginFileID = EndFileID;

392 }

393 }

394

395

396

397

398

399 if (Begin.isInvalid() || End.isInvalid() || BeginFileID != EndFileID)

400 continue;

401

402

406 CommonArgExpansions, true, SM,

407 IsTokenRange);

409 CommonArgExpansions, false, SM,

410 IsTokenRange);

412

413

415 End = SM->getSpellingLoc(End);

416

418 IsTokenRange));

419 }

420}

421

429}

430

431

432

433void DiagnosticRenderer::emitSingleMacroExpansion(

436

437

439

440

443

445 llvm::raw_svector_ostream Message(MessageStorage);

448 if (MacroName.empty())

449 Message << "expanded from here";

450 else

451 Message << "expanded from macro '" << MacroName << "'";

452

454 SpellingRanges, {});

455}

456

457

458

459

464 if (SM.isMacroArgExpansion(Loc, &MacroLoc)) {

465 if (ArgumentLoc == MacroLoc) return true;

466 }

467

468 return false;

469}

470

471

472

477 while (BegLoc != EndLoc) {

479 return false;

481 }

482

484}

485

486

487

490 assert(Loc.isMacroID() && "Must be a macro expansion!");

491

494

495

496 unsigned ValidCount =

497 llvm::count_if(Ranges, [](const auto &R) { return R.isValid(); });

498

499 if (ValidCount > SpellingRanges.size())

500 return false;

501

502

504

505

506

507 if (Loc.isMacroArgExpansion(&ArgumentLoc))

508 return false;

509

510 for (const auto &Range : SpellingRanges)

512 return false;

513

514 return true;

515}

516

517

518

519

520

521

522

523

524

525

526

527

528void DiagnosticRenderer::emitMacroExpansions(FullSourceLoc Loc,

532 assert(Loc.isValid() && "must have a valid source location here");

535

536

538 unsigned IgnoredEnd = 0;

540

541

542 if (SM.isMacroArgExpansion(L))

543 LocationStack.push_back(SM.getImmediateExpansionRange(L).getBegin());

544 else

545 LocationStack.push_back(L);

546

548 IgnoredEnd = LocationStack.size();

549

550 L = SM.getImmediateMacroCallerLoc(L);

551

552

553

554

556 L = SM.getImmediateMacroCallerLoc(LocationStack.back());

557 assert(L.isValid() && "must have a valid source location here");

558 }

559

560 LocationStack.erase(LocationStack.begin(),

561 LocationStack.begin() + IgnoredEnd);

562

563 unsigned MacroDepth = LocationStack.size();

564 unsigned MacroLimit = DiagOpts->MacroBacktraceLimit;

565 if (MacroDepth <= MacroLimit || MacroLimit == 0) {

566 for (auto I = LocationStack.rbegin(), E = LocationStack.rend();

567 I != E; ++I)

568 emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges);

569 return;

570 }

571

572 unsigned MacroStartMessages = MacroLimit / 2;

573 unsigned MacroEndMessages = MacroLimit / 2 + MacroLimit % 2;

574

575 for (auto I = LocationStack.rbegin(),

576 E = LocationStack.rbegin() + MacroStartMessages;

577 I != E; ++I)

578 emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges);

579

581 llvm::raw_svector_ostream Message(MessageStorage);

582 Message << "(skipping " << (MacroDepth - MacroLimit)

583 << " expansions in backtrace; use -fmacro-backtrace-limit=0 to "

584 "see all)";

585 emitBasicNote(Message.str());

586

587 for (auto I = LocationStack.rend() - MacroEndMessages,

588 E = LocationStack.rend();

589 I != E; ++I)

590 emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges);

591}

592

594

597

599 llvm::raw_svector_ostream Message(MessageStorage);

600 Message << "in file included from " << PLoc.getFilename() << ':'

601 << PLoc.getLine() << ":";

603}

604

607 StringRef ModuleName) {

608

610 llvm::raw_svector_ostream Message(MessageStorage);

611 Message << "in module '" << ModuleName;

613 Message << "' imported from " << PLoc.getFilename() << ':'

615 Message << ":";

617}

618

621 StringRef ModuleName) {

622

624 llvm::raw_svector_ostream Message(MessageStorage);

626 Message << "while building module '" << ModuleName << "' imported from "

628 else

629 Message << "while building module '" << ModuleName << "':";

631}

Defines the Diagnostic-related interfaces.

static bool checkLocForMacroArgExpansion(SourceLocation Loc, const SourceManager &SM, SourceLocation ArgumentLoc)

Check that the macro argument location of Loc starts with ArgumentLoc.

static void getMacroArgExpansionFileIDs(SourceLocation Loc, SmallVectorImpl< FileID > &IDs, bool IsBegin, const SourceManager *SM)

Walk up the chain of macro expansions and collect the FileIDs identifying the expansions.

static SourceLocation retrieveMacroLocation(SourceLocation Loc, FileID MacroFileID, FileID CaretFileID, const SmallVectorImpl< FileID > &CommonArgExpansions, bool IsBegin, const SourceManager *SM, bool &IsTokenRange)

A recursive function to trace all possible backtrace locations to match the CaretLocFileID.

static void computeCommonMacroArgExpansionFileIDs(SourceLocation Begin, SourceLocation End, const SourceManager *SM, SmallVectorImpl< FileID > &CommonArgExpansions)

Collect the expansions of the begin and end locations and compute the set intersection.

static bool checkRangeForMacroArgExpansion(CharSourceRange Range, const SourceManager &SM, SourceLocation ArgumentLoc)

Check if all the locations in the range have the same macro argument expansion, and that the expansio...

static bool checkRangesForMacroArgExpansion(FullSourceLoc Loc, ArrayRef< CharSourceRange > Ranges)

A helper function to check if the current ranges are all inside the same macro argument expansion as ...

static void mapDiagnosticRanges(FullSourceLoc CaretLoc, ArrayRef< CharSourceRange > Ranges, SmallVectorImpl< CharSourceRange > &SpellingRanges)

static void mergeFixits(ArrayRef< FixItHint > FixItHints, const SourceManager &SM, const LangOptions &LangOpts, SmallVectorImpl< FixItHint > &MergedFixits)

Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.

static DiagnosticBuilder Diag(DiagnosticsEngine *Diags, const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, const char *TokRangeBegin, const char *TokRangeEnd, unsigned DiagID)

Produce a diagnostic highlighting some portion of a literal.

Defines the clang::SourceLocation class and associated facilities.

Defines the SourceManager interface.

Represents a character-granular source range.

bool isTokenRange() const

Return true if the end of this range specifies the start of the last token.

SourceLocation getEnd() const

SourceLocation getBegin() const

~DiagnosticNoteRenderer() override

virtual void emitNote(FullSourceLoc Loc, StringRef Message)=0

void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc, StringRef ModuleName) override

void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) override

void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc, StringRef ModuleName) override

Options for controlling the compiler diagnostics engine.

virtual void endDiagnostic(DiagOrStoredDiag D, DiagnosticsEngine::Level Level)

virtual void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc)=0

virtual ~DiagnosticRenderer()

const LangOptions & LangOpts

void emitStoredDiagnostic(StoredDiagnostic &Diag)

SourceLocation LastLoc

The location of the previous diagnostic if known.

DiagnosticRenderer(const LangOptions &LangOpts, DiagnosticOptions *DiagOpts)

DiagnosticsEngine::Level LastLevel

The level of the last diagnostic emitted.

virtual void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc, StringRef ModuleName)=0

virtual void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, StringRef Message, ArrayRef< CharSourceRange > Ranges, DiagOrStoredDiag Info)=0

SourceLocation LastIncludeLoc

The location of the last include whose stack was printed if known.

virtual void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc, StringRef ModuleName)=0

void emitDiagnostic(FullSourceLoc Loc, DiagnosticsEngine::Level Level, StringRef Message, ArrayRef< CharSourceRange > Ranges, ArrayRef< FixItHint > FixItHints, DiagOrStoredDiag D=(Diagnostic *) nullptr)

Emit a diagnostic.

virtual void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level, SmallVectorImpl< CharSourceRange > &Ranges, ArrayRef< FixItHint > Hints)=0

IntrusiveRefCntPtr< DiagnosticOptions > DiagOpts

virtual void beginDiagnostic(DiagOrStoredDiag D, DiagnosticsEngine::Level Level)

Level

The level of the diagnostic, after it has been through mapping.

An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...

static FixItHint CreateReplacement(CharSourceRange RemoveRange, StringRef Code)

Create a code modification hint that replaces the given source range with the given code string.

static FixItHint CreateInsertion(SourceLocation InsertionLoc, StringRef Code, bool BeforePreviousInsertions=false)

Create a code modification hint that inserts the given code string at a specific location.

A SourceLocation and its associated SourceManager.

const SourceManager & getManager() const

Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...

static StringRef getImmediateMacroNameForDiagnostics(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)

Retrieve the name of the immediate macro expansion.

Represents an unpacked "presumed" location which can be presented to the user.

const char * getFilename() const

Return the presumed filename of this location.

unsigned getLine() const

Return the presumed line number of this location.

bool isInvalid() const

Return true if this object is invalid or uninitialized.

SourceLocation getIncludeLoc() const

Return the presumed include location of this location.

Encodes a location in the source.

bool isValid() const

Return true if this is a valid SourceLocation object.

SourceLocation getLocWithOffset(IntTy Offset) const

Return a source location with the specified offset from this SourceLocation.

This class handles loading and caching of source files into memory.

A trivial tuple used to represent a source range.

SourceLocation getEnd() const

SourceLocation getBegin() const

Represents a diagnostic in a form that can be retained until its corresponding source manager is dest...

bool insertFromRange(SourceLocation loc, CharSourceRange range, bool afterToken=false, bool beforePreviousInsertions=false)

bool insert(SourceLocation loc, StringRef text, bool afterToken=false, bool beforePreviousInsertions=false)

bool remove(CharSourceRange range)

bool replace(CharSourceRange range, StringRef text)

void applyRewrites(EditsReceiver &receiver, bool adjustRemovals=true)

bool commit(const Commit &commit)

The JSON file list parser is used to communicate input to InstallAPI.

llvm::PointerUnion< const Diagnostic *, const StoredDiagnostic * > DiagOrStoredDiag