clang 20.0.0git
DiagnosticRenderer.cpp
Go to the documentation of this file.
1//===- DiagnosticRenderer.cpp - Diagnostic Pretty-Printing ----------------===//
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
12#include "clang/Basic/LLVM.h"
15#include "clang/Edit/Commit.h"
18#include "clang/Lex/Lexer.h"
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 <algorithm>
26#include <cassert>
27#include <iterator>
28#include <utility>
29
30using namespace clang;
31
33 DiagnosticOptions *DiagOpts)
34 : LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {}
35
37
38namespace {
39
40class FixitReceiver : public edit::EditsReceiver {
41 SmallVectorImpl<FixItHint> &MergedFixits;
42
43public:
44 FixitReceiver(SmallVectorImpl<FixItHint> &MergedFixits)
45 : MergedFixits(MergedFixits) {}
46
47 void insert(SourceLocation loc, StringRef text) override {
48 MergedFixits.push_back(FixItHint::CreateInsertion(loc, text));
49 }
50
51 void replace(CharSourceRange range, StringRef text) override {
52 MergedFixits.push_back(FixItHint::CreateReplacement(range, text));
53 }
54};
55
56} // namespace
57
58static void mergeFixits(ArrayRef<FixItHint> FixItHints,
59 const SourceManager &SM, const LangOptions &LangOpts,
60 SmallVectorImpl<FixItHint> &MergedFixits) {
61 edit::Commit commit(SM, LangOpts);
62 for (const auto &Hint : FixItHints)
63 if (Hint.CodeToInsert.empty()) {
64 if (Hint.InsertFromRange.isValid())
65 commit.insertFromRange(Hint.RemoveRange.getBegin(),
66 Hint.InsertFromRange, /*afterToken=*/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 /*afterToken=*/false, Hint.BeforePreviousInsertions);
77 }
78
79 edit::EditedSource Editor(SM, LangOpts);
80 if (Editor.commit(commit)) {
81 FixitReceiver Rec(MergedFixits);
82 Editor.applyRewrites(Rec);
83 }
84}
85
88 StringRef Message,
90 ArrayRef<FixItHint> FixItHints,
92 assert(Loc.hasManager() || Loc.isInvalid());
93
94 beginDiagnostic(D, Level);
95
96 if (!Loc.isValid())
97 // If we have no source location, just emit the diagnostic message.
98 emitDiagnosticMessage(Loc, PresumedLoc(), Level, Message, Ranges, D);
99 else {
100 // Get the ranges into a local array we can hack on.
101 SmallVector<CharSourceRange, 20> MutableRanges(Ranges);
102
103 SmallVector<FixItHint, 8> MergedFixits;
104 if (!FixItHints.empty()) {
105 mergeFixits(FixItHints, Loc.getManager(), LangOpts, MergedFixits);
106 FixItHints = MergedFixits;
107 }
108
109 for (const auto &Hint : FixItHints)
110 if (Hint.RemoveRange.isValid())
111 MutableRanges.push_back(Hint.RemoveRange);
112
113 FullSourceLoc UnexpandedLoc = Loc;
114
115 // Find the ultimate expansion location for the diagnostic.
116 Loc = Loc.getFileLoc();
117
118 PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts->ShowPresumedLoc);
119
120 // First, if this diagnostic is not in the main file, print out the
121 // "included from" lines.
122 emitIncludeStack(Loc, PLoc, Level);
123
124 // Next, emit the actual diagnostic message and caret.
125 emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, D);
126 emitCaret(Loc, Level, MutableRanges, FixItHints);
127
128 // If this location is within a macro, walk from UnexpandedLoc up to Loc
129 // and produce a macro backtrace.
130 if (UnexpandedLoc.isValid() && UnexpandedLoc.isMacroID()) {
131 emitMacroExpansions(UnexpandedLoc, Level, MutableRanges, FixItHints);
132 }
133 }
134
135 LastLoc = Loc;
136 LastLevel = Level;
137
138 endDiagnostic(D, Level);
139}
140
142 emitDiagnostic(Diag.getLocation(), Diag.getLevel(), Diag.getMessage(),
143 Diag.getRanges(), Diag.getFixIts(),
144 &Diag);
145}
146
147void DiagnosticRenderer::emitBasicNote(StringRef Message) {
149 Message, {}, DiagOrStoredDiag());
150}
151
152/// Prints an include stack when appropriate for a particular
153/// diagnostic level and location.
154///
155/// This routine handles all the logic of suppressing particular include
156/// stacks (such as those for notes) and duplicate include stacks when
157/// repeated warnings occur within the same file. It also handles the logic
158/// of customizing the formatting and display of the include stack.
159///
160/// \param Loc The diagnostic location.
161/// \param PLoc The presumed location of the diagnostic location.
162/// \param Level The diagnostic level of the message this stack pertains to.
163void DiagnosticRenderer::emitIncludeStack(FullSourceLoc Loc, PresumedLoc PLoc,
165 FullSourceLoc IncludeLoc =
166 PLoc.isInvalid() ? FullSourceLoc()
167 : FullSourceLoc(PLoc.getIncludeLoc(), Loc.getManager());
168
169 // Skip redundant include stacks altogether.
170 if (LastIncludeLoc == IncludeLoc)
171 return;
172
173 LastIncludeLoc = IncludeLoc;
174
175 if (!DiagOpts->ShowNoteIncludeStack && Level == DiagnosticsEngine::Note)
176 return;
177
178 if (IncludeLoc.isValid())
179 emitIncludeStackRecursively(IncludeLoc);
180 else {
181 emitModuleBuildStack(Loc.getManager());
182 emitImportStack(Loc);
183 }
184}
185
186/// Helper to recursively walk up the include stack and print each layer
187/// on the way back down.
188void DiagnosticRenderer::emitIncludeStackRecursively(FullSourceLoc Loc) {
189 if (Loc.isInvalid()) {
190 emitModuleBuildStack(Loc.getManager());
191 return;
192 }
193
194 PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts->ShowPresumedLoc);
195 if (PLoc.isInvalid())
196 return;
197
198 // If this source location was imported from a module, print the module
199 // import stack rather than the
200 // FIXME: We want submodule granularity here.
201 std::pair<FullSourceLoc, StringRef> Imported = Loc.getModuleImportLoc();
202 if (!Imported.second.empty()) {
203 // This location was imported by a module. Emit the module import stack.
204 emitImportStackRecursively(Imported.first, Imported.second);
205 return;
206 }
207
208 // Emit the other include frames first.
209 emitIncludeStackRecursively(
210 FullSourceLoc(PLoc.getIncludeLoc(), Loc.getManager()));
211
212 // Emit the inclusion text/note.