1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
//===--- tools/extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp ----------=== //
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file This file implements ClangTidyDiagnosticConsumer, ClangTidyMessage,
/// ClangTidyContext and ClangTidyError classes.
///
/// This tool uses the Clang Tooling infrastructure, see
/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
/// for details on setting it up with LLVM source tree.
///
//===----------------------------------------------------------------------===//
#include "ClangTidyDiagnosticConsumer.h"
#include "llvm/ADT/SmallString.h"
namespace clang {
namespace tidy {
ClangTidyMessage::ClangTidyMessage(StringRef Message) : Message(Message) {}
ClangTidyMessage::ClangTidyMessage(StringRef Message,
const SourceManager &Sources,
SourceLocation Loc)
: Message(Message) {
FilePath = Sources.getFilename(Loc);
FileOffset = Sources.getFileOffset(Loc);
}
ClangTidyError::ClangTidyError(StringRef CheckName,
const ClangTidyMessage &Message)
: CheckName(CheckName), Message(Message) {}
DiagnosticBuilder ClangTidyContext::diag(
StringRef CheckName, SourceLocation Loc, StringRef Description,
DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
assert(Loc.isValid());
bool Invalid;
const char *CharacterData =
DiagEngine->getSourceManager().getCharacterData(Loc, &Invalid);
if (!Invalid) {
const char *P = CharacterData;
while (*P != '\0' && *P != '\r' && *P != '\n')
++P;
StringRef RestOfLine(CharacterData, P - CharacterData + 1);
// FIXME: Handle /\bNOLINT\b(\([^)]*\))?/ as cpplint.py does.
if (RestOfLine.find("NOLINT") != StringRef::npos)
Level = DiagnosticIDs::Ignored;
}
unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
Level, (Description + " [" + CheckName + "]").str());
if (CheckNamesByDiagnosticID.count(ID) == 0)
CheckNamesByDiagnosticID.insert(std::make_pair(ID, CheckName.str()));
return DiagEngine->Report(Loc, ID);
}
void ClangTidyContext::setDiagnosticsEngine(DiagnosticsEngine *Engine) {
DiagEngine = Engine;
}
void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
DiagEngine->setSourceManager(SourceMgr);
}
/// \brief Store a \c ClangTidyError.
void ClangTidyContext::storeError(const ClangTidyError &Error) {
Errors->push_back(Error);
}
StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
llvm::DenseMap<unsigned, std::string>::const_iterator I =
CheckNamesByDiagnosticID.find(DiagnosticID);
if (I != CheckNamesByDiagnosticID.end())
return I->second;
return "";
}
ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx)
: Context(Ctx), LastErrorRelatesToUserCode(false) {
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
Diags.reset(new DiagnosticsEngine(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this,
/*ShouldOwnClient=*/false));
Context.setDiagnosticsEngine(Diags.get());
}
void ClangTidyDiagnosticConsumer::finalizeLastError() {
if (!LastErrorRelatesToUserCode && !Errors.empty())
Errors.pop_back();
LastErrorRelatesToUserCode = false;
}
void ClangTidyDiagnosticConsumer::HandleDiagnostic(
DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
// FIXME: Deduplicate diagnostics.
if (DiagLevel == DiagnosticsEngine::Note) {
assert(!Errors.empty() &&
"A diagnostic note can only be appended to a message.");
Errors.back().Notes.push_back(getMessage(Info));
} else {
finalizeLastError();
Errors.push_back(
ClangTidyError(Context.getCheckName(Info.getID()), getMessage(Info)));
}
addFixes(Info, Errors.back());
// Let argument parsing-related warnings through.
if (!Diags->hasSourceManager() ||
!Diags->getSourceManager().isInSystemHeader(Info.getLocation())) {
LastErrorRelatesToUserCode = true;
}
}
// Flushes the internal diagnostics buffer to the ClangTidyContext.
void ClangTidyDiagnosticConsumer::finish() {
finalizeLastError();
for (unsigned i = 0, e = Errors.size(); i != e; ++i)
Context.storeError(Errors[i]);
Errors.clear();
}
void ClangTidyDiagnosticConsumer::addFixes(const Diagnostic &Info,
ClangTidyError &Error) {
if (!Info.hasSourceManager())
return;
SourceManager &SourceMgr = Info.getSourceManager();
tooling::Replacements Replacements;
for (unsigned i = 0, e = Info.getNumFixItHints(); i != e; ++i) {
Error.Fix.insert(tooling::Replacement(
SourceMgr, Info.getFixItHint(i).RemoveRange.getBegin(), 0,
Info.getFixItHint(i).CodeToInsert));
}
}
ClangTidyMessage
ClangTidyDiagnosticConsumer::getMessage(const Diagnostic &Info) const {
SmallString<100> Buf;
Info.FormatDiagnostic(Buf);
if (!Info.hasSourceManager()) {
return ClangTidyMessage(Buf.str());
}
return ClangTidyMessage(Buf.str(), Info.getSourceManager(),
Info.getLocation());
}
} // namespace tidy
} // namespace clang
|