14 #include "clang/Basic/SourceManager.h" 15 #include "clang/Lex/Lexer.h" 16 #include "llvm/Support/Capacity.h" 17 #include "llvm/Support/Path.h" 25 bool mentionsMainFile(
const Diag &D) {
31 for (
auto &N : D.Notes) {
40 bool locationInRange(SourceLocation L, CharSourceRange R,
41 const SourceManager &M) {
42 assert(R.isCharRange());
43 if (!R.isValid() || M.getFileID(R.getBegin()) != M.getFileID(R.getEnd()) ||
44 M.getFileID(R.getBegin()) != M.getFileID(L))
46 return L != R.getEnd() && M.isPointWithin(L, R.getBegin(), R.getEnd());
51 Range diagnosticRange(
const clang::Diagnostic &D,
const LangOptions &L) {
52 auto &M = D.getSourceManager();
53 auto Loc = M.getFileLoc(D.getLocation());
55 for (
const auto &CR : D.getRanges()) {
56 auto R = Lexer::makeFileCharRange(CR, M, L);
57 if (locationInRange(
Loc, R, M))
61 for (
const auto &F : D.getFixItHints()) {
62 auto R = Lexer::makeFileCharRange(F.RemoveRange, M, L);
63 if (locationInRange(
Loc, R, M))
67 auto R = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(
Loc), M, L);
69 R = CharSourceRange::getCharRange(
Loc);
73 TextEdit toTextEdit(
const FixItHint &FixIt,
const SourceManager &M,
74 const LangOptions &L) {
78 Result.newText = FixIt.CodeToInsert;
82 bool isInsideMainFile(
const SourceLocation
Loc,
const SourceManager &M) {
83 return Loc.isValid() && M.isInMainFile(Loc);
86 bool isInsideMainFile(
const clang::Diagnostic &D) {
87 if (!D.hasSourceManager())
90 return isInsideMainFile(D.getLocation(), D.getSourceManager());
93 bool isNote(DiagnosticsEngine::Level L) {
94 return L == DiagnosticsEngine::Note || L == DiagnosticsEngine::Remark;
97 llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
99 case DiagnosticsEngine::Ignored:
101 case DiagnosticsEngine::Note:
103 case DiagnosticsEngine::Remark:
105 case DiagnosticsEngine::Warning:
107 case DiagnosticsEngine::Error:
109 case DiagnosticsEngine::Fatal:
110 return "fatal error";
112 llvm_unreachable(
"unhandled DiagnosticsEngine::Level");
126 void printDiag(llvm::raw_string_ostream &OS,
const DiagBase &D) {
127 if (D.InsideMainFile) {
131 OS << llvm::sys::path::filename(D.File) <<
":";
137 auto Pos = D.Range.start;
141 if (D.InsideMainFile)
145 OS << diagLeveltoString(D.Severity) <<
": " << D.Message;
160 std::string mainMessage(
const Diag &D) {
162 llvm::raw_string_ostream OS(Result);
164 for (
auto &Note : D.Notes) {
175 std::string noteMessage(
const Diag &Main,
const DiagBase &Note) {
177 llvm::raw_string_ostream OS(Result);
188 OS <<
"[in " << D.
File <<
"] ";
194 const char *Sep =
"";
195 for (
const auto &Edit : F.
Edits) {
203 OS << static_cast<const DiagBase &>(D);
204 if (!D.
Notes.empty()) {
206 const char *Sep =
"";
213 if (!D.
Fixes.empty()) {
215 const char *Sep =
"";
237 OutFn(std::move(Main), D.
Fixes);
245 OutFn(std::move(Res), llvm::ArrayRef<Fix>());
251 case DiagnosticsEngine::Remark:
253 case DiagnosticsEngine::Note:
255 case DiagnosticsEngine::Warning:
257 case DiagnosticsEngine::Fatal:
258 case DiagnosticsEngine::Error:
260 case DiagnosticsEngine::Ignored:
263 llvm_unreachable(
"Unknown diagnostic level!");
269 const Preprocessor *) {
279 const clang::Diagnostic &
Info) {
280 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
282 if (!LangOpts || !Info.hasSourceManager()) {
287 bool InsideMainFile = isInsideMainFile(Info);
289 auto FillDiagBase = [&](
DiagBase &D) {
290 D.
Range = diagnosticRange(Info, *LangOpts);
292 Info.FormatDiagnostic(Message);
295 D.
File = Info.getSourceManager().getFilename(Info.getLocation());
300 auto AddFix = [&](
bool SyntheticMessage) ->
bool {
301 assert(!Info.getFixItHints().empty() &&
302 "diagnostic does not have attached fix-its");
306 llvm::SmallVector<TextEdit, 1> Edits;
307 for (
auto &FixIt : Info.getFixItHints()) {
308 if (!isInsideMainFile(FixIt.RemoveRange.getBegin(),
309 Info.getSourceManager()))
311 Edits.push_back(toTextEdit(FixIt, Info.getSourceManager(), *LangOpts));
316 if (SyntheticMessage && Info.getNumFixItHints() == 1) {
317 const auto &FixIt = Info.getFixItHint(0);
318 bool Invalid =
false;
319 StringRef Remove = Lexer::getSourceText(
320 FixIt.RemoveRange, Info.getSourceManager(), *LangOpts, &Invalid);
321 StringRef Insert = FixIt.CodeToInsert;
323 llvm::raw_svector_ostream M(Message);
324 if (!Remove.empty() && !Insert.empty())
325 M <<
"change '" << Remove <<
"' to '" << Insert <<
"'";
326 else if (!Remove.empty())
327 M <<
"remove '" << Remove <<
"'";
328 else if (!Insert.empty())
329 M <<
"insert '" << Insert <<
"'";
331 std::replace(Message.begin(), Message.end(),
'\n',
' ');
335 Info.FormatDiagnostic(Message);
336 LastDiag->Fixes.push_back(
Fix{Message.str(), std::move(Edits)});
340 if (!isNote(DiagLevel)) {
345 FillDiagBase(*LastDiag);
347 if (!Info.getFixItHints().empty())
352 assert(
false &&
"Adding a note without main diagnostic");
357 if (!Info.getFixItHints().empty()) {
367 LastDiag->Notes.push_back(std::move(N));
372 void StoreDiags::flushLastDiag() {
375 if (mentionsMainFile(*LastDiag))
376 Output.push_back(std::move(*LastDiag));
378 log(
"Dropped diagnostic outside main file: {0}: {1}", LastDiag->File,
SourceLocation Loc
'#' location in the include directive
static void log(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info)
std::vector< Diag > take()
Contains basic information about a diagnostic.
void toLSPDiags(const Diag &D, llvm::function_ref< void(clangd::Diagnostic, llvm::ArrayRef< Fix >)> OutFn)
Conversion to LSP diagnostics.
static const StringRef Message
Documents should not be synced at all.
void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override
A top-level diagnostic that may have Notes and Fixes.
std::string Message
Message for the fix-it.
std::vector< Fix > Fixes
Alternative fixes for this diagnostic, one should be chosen.
llvm::SmallVector< TextEdit, 1 > Edits
TextEdits from clang's fix-its. Must be non-empty.
int getSeverity(DiagnosticsEngine::Level L)
Convert from clang diagnostic level to LSP severity.
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) override
void log(const char *Fmt, Ts &&... Vals)
DiagnosticsEngine::Level Severity
Represents a single fix-it that editor can apply to fix the error.
int line
Line position in a document (zero-based).
int character
Character offset on a line in a document (zero-based).
std::vector< Note > Notes
Elaborate on the problem, usually pointing to a related piece of code.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::string message
The diagnostic's code.
CharSourceRange Range
SourceRange for the file name.
int severity
The diagnostic's severity.
void EndSourceFile() override
Represents a note for the diagnostic.
Range range
The range at which the message applies.
static cl::opt< bool > Fix("fix", cl::desc(R"(
Apply suggested fixes. Without -fix-errors
clang-tidy will bail out if any compilation
errors were found.
)"), cl::init(false), cl::cat(ClangTidyCategory))
raw_ostream & operator<<(raw_ostream &OS, const CodeCompletion &C)
Range halfOpenToRange(const SourceManager &SM, CharSourceRange R)