blob: 613e8fb8c579e8aeeb56c820953786dfa225d15c [file] [log] [blame]
Daniel Jasperd07c8402013-07-29 08:19:24 +00001//===--- ClangTidyDiagnosticConsumer.h - clang-tidy -------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_DIAGNOSTIC_CONSUMER_H
11#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_DIAGNOSTIC_CONSUMER_H
12
13#include "clang/Basic/Diagnostic.h"
14#include "clang/Basic/SourceManager.h"
15#include "clang/Tooling/Refactoring.h"
Alexander Kornienko73f7b022014-01-09 02:21:52 +000016#include "llvm/ADT/SmallString.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000017
18namespace clang {
19
20class CompilerInstance;
21namespace ast_matchers {
22class MatchFinder;
23}
24namespace tooling {
25class CompilationDatabase;
26}
27
28namespace tidy {
29
Manuel Klimek814f9bd2013-11-14 15:49:44 +000030/// \brief A message from a clang-tidy check.
31///
32/// Note that this is independent of a \c SourceManager.
33struct ClangTidyMessage {
34 ClangTidyMessage(StringRef Message = "");
35 ClangTidyMessage(StringRef Message, const SourceManager &Sources,
36 SourceLocation Loc);
37 std::string Message;
38 std::string FilePath;
39 unsigned FileOffset;
40};
41
42/// \brief A detected error complete with information to display diagnostic and
43/// automatic fix.
44///
45/// This is used as an intermediate format to transport Diagnostics without a
46/// dependency on a SourceManager.
47///
48/// FIXME: Make Diagnostics flexible enough to support this directly.
49struct ClangTidyError {
50 ClangTidyError(const ClangTidyMessage &Message);
51
52 ClangTidyMessage Message;
53 tooling::Replacements Fix;
54 SmallVector<ClangTidyMessage, 1> Notes;
55};
56
57/// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticEngine
58/// provided by this context.
59///
60/// A \c ClangTidyCheck always has access to the active context to report
61/// warnings like:
62/// \code
63/// Context->Diag(Loc, "Single-argument constructors must be explicit")
64/// << FixItHint::CreateInsertion(Loc, "explicit ");
65/// \endcode
66class ClangTidyContext {
67public:
Alexander Kornienko175fefb2014-01-03 09:31:57 +000068 ClangTidyContext(SmallVectorImpl<ClangTidyError> *Errors)
69 : Errors(Errors), DiagEngine(0) {}
Manuel Klimek814f9bd2013-11-14 15:49:44 +000070
71 /// \brief Report any errors detected using this method.
72 ///
73 /// This is still under heavy development and will likely change towards using
74 /// tablegen'd diagnostic IDs.
75 /// FIXME: Figure out a way to manage ID spaces.
76 DiagnosticBuilder Diag(SourceLocation Loc, StringRef Message);
77
78 /// \brief Sets the \c DiagnosticsEngine so that Diagnostics can be generated
79 /// correctly.
80 ///
81 /// This is called from the \c ClangTidyCheck base class.
82 void setDiagnosticsEngine(DiagnosticsEngine *Engine);
83
84 /// \brief Sets the \c SourceManager of the used \c DiagnosticsEngine.
85 ///
86 /// This is called from the \c ClangTidyCheck base class.
87 void setSourceManager(SourceManager *SourceMgr);
88
89private:
90 friend class ClangTidyDiagnosticConsumer; // Calls storeError().
91
92 /// \brief Store a \c ClangTidyError.
93 void storeError(const ClangTidyError &Error);
94
95 SmallVectorImpl<ClangTidyError> *Errors;
96 DiagnosticsEngine *DiagEngine;
97};
98
Daniel Jasperd07c8402013-07-29 08:19:24 +000099/// \brief A diagnostic consumer that turns each \c Diagnostic into a
100/// \c SourceManager-independent \c ClangTidyError.
101//
102// FIXME: If we move away from unit-tests, this can be moved to a private
103// implementation file.
104class ClangTidyDiagnosticConsumer : public DiagnosticConsumer {
105public:
Alexander Kornienko73f7b022014-01-09 02:21:52 +0000106 ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx) : Context(Ctx) {
107 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
108 Diags.reset(new DiagnosticsEngine(
109 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this,
110 /*ShouldOwnClient=*/false));
111 Context.setDiagnosticsEngine(Diags.get());
112 }
Daniel Jasperd07c8402013-07-29 08:19:24 +0000113
114 // FIXME: The concept of converting between FixItHints and Replacements is
115 // more generic and should be pulled out into a more useful Diagnostics
116 // library.
117 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
Alexander Kornienko73f7b022014-01-09 02:21:52 +0000118 const Diagnostic &Info) LLVM_OVERRIDE {
119 // FIXME: Ensure that we don't get notes from user code related to errors
120 // from non-user code.
121 if (Diags->getSourceManager().isInSystemHeader(Info.getLocation()))
122 return;
123 if (DiagLevel != DiagnosticsEngine::Note) {
124 Errors.push_back(ClangTidyError(getMessage(Info)));
125 } else {
126 assert(!Errors.empty() &&
127 "A diagnostic note can only be appended to a message.");
128 Errors.back().Notes.push_back(getMessage(Info));
129 }
130 addFixes(Info, Errors.back());
131 }
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000132
Alexander Kornienkofc589872014-01-03 15:34:40 +0000133 // Flushes the internal diagnostics buffer to the ClangTidyContext.
Alexander Kornienko73f7b022014-01-09 02:21:52 +0000134 virtual void finish() LLVM_OVERRIDE {
135 for (unsigned i = 0, e = Errors.size(); i != e; ++i) {
136 Context.storeError(Errors[i]);
137 }
138 Errors.clear();
139 }
Daniel Jasperd07c8402013-07-29 08:19:24 +0000140
141private:
Alexander Kornienko73f7b022014-01-09 02:21:52 +0000142 void addFixes(const Diagnostic &Info, ClangTidyError &Error) {
143 if (!Info.hasSourceManager())
144 return;
145 SourceManager &SourceMgr = Info.getSourceManager();
146 tooling::Replacements Replacements;
147 for (unsigned i = 0, e = Info.getNumFixItHints(); i != e; ++i) {
148 Error.Fix.insert(tooling::Replacement(
149 SourceMgr, Info.getFixItHint(i).RemoveRange.getBegin(), 0,
150 Info.getFixItHint(i).CodeToInsert));
151 }
152 }
153
154 ClangTidyMessage getMessage(const Diagnostic &Info) const {
155 SmallString<100> Buf;
156 Info.FormatDiagnostic(Buf);
157 if (!Info.hasSourceManager()) {
158 return ClangTidyMessage(Buf.str());
159 }
160 return ClangTidyMessage(Buf.str(), Info.getSourceManager(),
161 Info.getLocation());
162 }
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000163
Daniel Jasperd07c8402013-07-29 08:19:24 +0000164 ClangTidyContext &Context;
165 OwningPtr<DiagnosticsEngine> Diags;
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000166 SmallVector<ClangTidyError, 8> Errors;
Daniel Jasperd07c8402013-07-29 08:19:24 +0000167};
168
169} // end namespace tidy
170} // end namespace clang
171
172#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_DIAGNOSTIC_CONSUMER_H