Clang Project

clang_source_code/include/clang/Frontend/VerifyDiagnosticConsumer.h
1//===- VerifyDiagnosticConsumer.h - Verifying Diagnostic Client -*- C++ -*-===//
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
9#ifndef LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
10#define LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
11
12#include "clang/Basic/Diagnostic.h"
13#include "clang/Basic/LLVM.h"
14#include "clang/Basic/SourceLocation.h"
15#include "clang/Lex/Preprocessor.h"
16#include "llvm/ADT/DenseMap.h"
17#include "llvm/ADT/PointerIntPair.h"
18#include "llvm/ADT/StringRef.h"
19#include <cassert>
20#include <limits>
21#include <memory>
22#include <string>
23#include <vector>
24
25namespace clang {
26
27class FileEntry;
28class LangOptions;
29class SourceManager;
30class TextDiagnosticBuffer;
31
32/// VerifyDiagnosticConsumer - Create a diagnostic client which will use
33/// markers in the input source to check that all the emitted diagnostics match
34/// those expected.
35///
36/// USING THE DIAGNOSTIC CHECKER:
37///
38/// Indicating that a line expects an error or a warning is simple. Put a
39/// comment on the line that has the diagnostic, use:
40///
41/// \code
42///   expected-{error,warning,remark,note}
43/// \endcode
44///
45/// to tag if it's an expected error, remark or warning, and place the expected
46/// text between {{ and }} markers. The full text doesn't have to be included,
47/// only enough to ensure that the correct diagnostic was emitted.
48///
49/// Here's an example:
50///
51/// \code
52///   int A = B; // expected-error {{use of undeclared identifier 'B'}}
53/// \endcode
54///
55/// You can place as many diagnostics on one line as you wish. To make the code
56/// more readable, you can use slash-newline to separate out the diagnostics.
57///
58/// Alternatively, it is possible to specify the line on which the diagnostic
59/// should appear by appending "@<line>" to "expected-<type>", for example:
60///
61/// \code
62///   #warning some text
63///   // expected-warning@10 {{some text}}
64/// \endcode
65///
66/// The line number may be absolute (as above), or relative to the current
67/// line by prefixing the number with either '+' or '-'.
68///
69/// If the diagnostic is generated in a separate file, for example in a shared
70/// header file, it may be beneficial to be able to declare the file in which
71/// the diagnostic will appear, rather than placing the expected-* directive in
72/// the actual file itself.  This can be done using the following syntax:
73///
74/// \code
75///   // expected-error@path/include.h:15 {{error message}}
76/// \endcode
77///
78/// The path can be absolute or relative and the same search paths will be used
79/// as for #include directives.  The line number in an external file may be
80/// substituted with '*' meaning that any line number will match (useful where
81/// the included file is, for example, a system header where the actual line
82/// number may change and is not critical).
83///
84/// The simple syntax above allows each specification to match exactly one
85/// error.  You can use the extended syntax to customize this. The extended
86/// syntax is "expected-<type> <n> {{diag text}}", where \<type> is one of
87/// "error", "warning" or "note", and \<n> is a positive integer. This allows
88/// the diagnostic to appear as many times as specified. Example:
89///
90/// \code
91///   void f(); // expected-note 2 {{previous declaration is here}}
92/// \endcode
93///
94/// Where the diagnostic is expected to occur a minimum number of times, this
95/// can be specified by appending a '+' to the number. Example:
96///
97/// \code
98///   void f(); // expected-note 0+ {{previous declaration is here}}
99///   void g(); // expected-note 1+ {{previous declaration is here}}
100/// \endcode
101///
102/// In the first example, the diagnostic becomes optional, i.e. it will be
103/// swallowed if it occurs, but will not generate an error if it does not
104/// occur.  In the second example, the diagnostic must occur at least once.
105/// As a short-hand, "one or more" can be specified simply by '+'. Example:
106///
107/// \code
108///   void g(); // expected-note + {{previous declaration is here}}
109/// \endcode
110///
111/// A range can also be specified by "<n>-<m>".  Example:
112///
113/// \code
114///   void f(); // expected-note 0-1 {{previous declaration is here}}
115/// \endcode
116///
117/// In this example, the diagnostic may appear only once, if at all.
118///
119/// Regex matching mode may be selected by appending '-re' to type and
120/// including regexes wrapped in double curly braces in the directive, such as:
121///
122/// \code
123///   expected-error-re {{format specifies type 'wchar_t **' (aka '{{.+}}')}}
124/// \endcode
125///
126/// Examples matching error: "variable has incomplete type 'struct s'"
127///
128/// \code
129///   // expected-error {{variable has incomplete type 'struct s'}}
130///   // expected-error {{variable has incomplete type}}
131///
132///   // expected-error-re {{variable has type 'struct {{.}}'}}
133///   // expected-error-re {{variable has type 'struct {{.*}}'}}
134///   // expected-error-re {{variable has type 'struct {{(.*)}}'}}
135///   // expected-error-re {{variable has type 'struct{{[[:space:]](.*)}}'}}
136/// \endcode
137///
138/// VerifyDiagnosticConsumer expects at least one expected-* directive to
139/// be found inside the source code.  If no diagnostics are expected the
140/// following directive can be used to indicate this:
141///
142/// \code
143///   // expected-no-diagnostics
144/// \endcode
145///
146class VerifyDiagnosticConsumerpublic DiagnosticConsumer,
147                                public CommentHandler {
148public:
149  /// Directive - Abstract class representing a parsed verify directive.
150  ///
151  class Directive {
152  public:
153    static std::unique_ptr<Directivecreate(bool RegexKind,
154                                             SourceLocation DirectiveLoc,
155                                             SourceLocation DiagnosticLoc,
156                                             bool MatchAnyLineStringRef Text,
157                                             unsigned Minunsigned Max);
158
159  public:
160    /// Constant representing n or more matches.
161    static const unsigned MaxCount = std::numeric_limits<unsigned>::max();
162
163    SourceLocation DirectiveLoc;
164    SourceLocation DiagnosticLoc;
165    const std::string Text;
166    unsigned MinMax;
167    bool MatchAnyLine;
168
169    Directive(const Directive &) = delete;
170    Directive &operator=(const Directive &) = delete;
171    virtual ~Directive() = default;
172
173    // Returns true if directive text is valid.
174    // Otherwise returns false and populates E.
175    virtual bool isValid(std::string &Error) = 0;
176
177    // Returns true on match.
178    virtual bool match(StringRef S) = 0;
179
180  protected:
181    Directive(SourceLocation DirectiveLocSourceLocation DiagnosticLoc,
182              bool MatchAnyLineStringRef Textunsigned Minunsigned Max)
183        : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc),
184          Text(Text), Min(Min), Max(Max), MatchAnyLine(MatchAnyLine) {
185       (0) . __assert_fail ("!DirectiveLoc.isInvalid() && \"DirectiveLoc is invalid!\"", "/home/seafit/code_projects/clang_source/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h", 185, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!");
186       (0) . __assert_fail ("(!DiagnosticLoc.isInvalid() || MatchAnyLine) && \"DiagnosticLoc is invalid!\"", "/home/seafit/code_projects/clang_source/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h", 187, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert((!DiagnosticLoc.isInvalid() || MatchAnyLine) &&
187 (0) . __assert_fail ("(!DiagnosticLoc.isInvalid() || MatchAnyLine) && \"DiagnosticLoc is invalid!\"", "/home/seafit/code_projects/clang_source/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h", 187, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">             "DiagnosticLoc is invalid!");
188    }
189  };
190
191  using DirectiveList = std::vector<std::unique_ptr<Directive>>;
192
193  /// ExpectedData - owns directive objects and deletes on destructor.
194  struct ExpectedData {
195    DirectiveList Errors;
196    DirectiveList Warnings;
197    DirectiveList Remarks;
198    DirectiveList Notes;
199
200    void Reset() {
201      Errors.clear();
202      Warnings.clear();
203      Remarks.clear();
204      Notes.clear();
205    }
206  };
207
208  enum DirectiveStatus {
209    HasNoDirectives,
210    HasNoDirectivesReported,
211    HasExpectedNoDiagnostics,
212    HasOtherExpectedDirectives
213  };
214
215private:
216  DiagnosticsEngine &Diags;
217  DiagnosticConsumer *PrimaryClient;
218  std::unique_ptr<DiagnosticConsumerPrimaryClientOwner;
219  std::unique_ptr<TextDiagnosticBufferBuffer;
220  const Preprocessor *CurrentPreprocessor = nullptr;
221  const LangOptions *LangOpts = nullptr;
222  SourceManager *SrcManager = nullptr;
223  unsigned ActiveSourceFiles = 0;
224  DirectiveStatus Status;
225  ExpectedData ED;
226
227  void CheckDiagnostics();
228
229  void setSourceManager(SourceManager &SM) {
230     (0) . __assert_fail ("(!SrcManager || SrcManager == &SM) && \"SourceManager changed!\"", "/home/seafit/code_projects/clang_source/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h", 230, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert((!SrcManager || SrcManager == &SM) && "SourceManager changed!");
231    SrcManager = &SM;
232  }
233
234  // These facilities are used for validation in debug builds.
235  class UnparsedFileStatus {
236    llvm::PointerIntPair<const FileEntry *, 1boolData;
237
238  public:
239    UnparsedFileStatus(const FileEntry *Filebool FoundDirectives)
240        : Data(File, FoundDirectives) {}
241
242    const FileEntry *getFile() const { return Data.getPointer(); }
243    bool foundDirectives() const { return Data.getInt(); }
244  };
245
246  using ParsedFilesMap = llvm::DenseMap<FileID, const FileEntry *>;
247  using UnparsedFilesMap = llvm::DenseMap<FileID, UnparsedFileStatus>;
248
249  ParsedFilesMap ParsedFiles;
250  UnparsedFilesMap UnparsedFiles;
251
252public:
253  /// Create a new verifying diagnostic client, which will issue errors to
254  /// the currently-attached diagnostic client when a diagnostic does not match
255  /// what is expected (as indicated in the source file).
256  VerifyDiagnosticConsumer(DiagnosticsEngine &Diags);
257  ~VerifyDiagnosticConsumer() override;
258
259  void BeginSourceFile(const LangOptions &LangOpts,
260                       const Preprocessor *PP) override;
261
262  void EndSourceFile() override;
263
264  enum ParsedStatus {
265    /// File has been processed via HandleComment.
266    IsParsed,
267
268    /// File has diagnostics and may have directives.
269    IsUnparsed,
270
271    /// File has diagnostics but guaranteed no directives.
272    IsUnparsedNoDirectives
273  };
274
275  /// Update lists of parsed and unparsed files.
276  void UpdateParsedFileStatus(SourceManager &SMFileID FIDParsedStatus PS);
277
278  bool HandleComment(Preprocessor &PPSourceRange Comment) override;
279
280  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
281                        const Diagnostic &Info) override;
282};
283
284// namespace clang
285
286#endif // LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
287
clang::VerifyDiagnosticConsumer::Directive
clang::VerifyDiagnosticConsumer::Directive::create
clang::VerifyDiagnosticConsumer::Directive::MaxCount
clang::VerifyDiagnosticConsumer::Directive::DirectiveLoc
clang::VerifyDiagnosticConsumer::Directive::DiagnosticLoc
clang::VerifyDiagnosticConsumer::Directive::Text
clang::VerifyDiagnosticConsumer::Directive::Min
clang::VerifyDiagnosticConsumer::Directive::Max
clang::VerifyDiagnosticConsumer::Directive::MatchAnyLine
clang::VerifyDiagnosticConsumer::Directive::isValid
clang::VerifyDiagnosticConsumer::Directive::match
clang::VerifyDiagnosticConsumer::ExpectedData
clang::VerifyDiagnosticConsumer::ExpectedData::Errors
clang::VerifyDiagnosticConsumer::ExpectedData::Warnings
clang::VerifyDiagnosticConsumer::ExpectedData::Remarks
clang::VerifyDiagnosticConsumer::ExpectedData::Notes
clang::VerifyDiagnosticConsumer::ExpectedData::Reset
clang::VerifyDiagnosticConsumer::DirectiveStatus
clang::VerifyDiagnosticConsumer::Diags
clang::VerifyDiagnosticConsumer::PrimaryClient
clang::VerifyDiagnosticConsumer::PrimaryClientOwner
clang::VerifyDiagnosticConsumer::Buffer
clang::VerifyDiagnosticConsumer::CurrentPreprocessor
clang::VerifyDiagnosticConsumer::LangOpts
clang::VerifyDiagnosticConsumer::SrcManager
clang::VerifyDiagnosticConsumer::ActiveSourceFiles
clang::VerifyDiagnosticConsumer::Status
clang::VerifyDiagnosticConsumer::ED
clang::VerifyDiagnosticConsumer::CheckDiagnostics
clang::VerifyDiagnosticConsumer::setSourceManager
clang::VerifyDiagnosticConsumer::UnparsedFileStatus
clang::VerifyDiagnosticConsumer::UnparsedFileStatus::Data
clang::VerifyDiagnosticConsumer::UnparsedFileStatus::getFile
clang::VerifyDiagnosticConsumer::UnparsedFileStatus::foundDirectives
clang::VerifyDiagnosticConsumer::ParsedFiles
clang::VerifyDiagnosticConsumer::UnparsedFiles
clang::VerifyDiagnosticConsumer::BeginSourceFile
clang::VerifyDiagnosticConsumer::EndSourceFile
clang::VerifyDiagnosticConsumer::ParsedStatus
clang::VerifyDiagnosticConsumer::UpdateParsedFileStatus
clang::VerifyDiagnosticConsumer::HandleComment
clang::VerifyDiagnosticConsumer::HandleDiagnostic