Coverage Report

Created: 2020-09-22 08:39

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