xref: /freebsd/contrib/llvm-project/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
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. See clang/docs/InternalsManual.rst for details about how to
36 /// write tests to verify diagnostics.
37 ///
38 class VerifyDiagnosticConsumer: public DiagnosticConsumer,
39                                 public CommentHandler {
40 public:
41   /// Directive - Abstract class representing a parsed verify directive.
42   ///
43   class Directive {
44   public:
45     static std::unique_ptr<Directive>
46     create(bool RegexKind, SourceLocation DirectiveLoc,
47            SourceLocation DiagnosticLoc, StringRef Spelling,
48            bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
49            unsigned Min, unsigned Max);
50 
51   public:
52     /// Constant representing n or more matches.
53     static const unsigned MaxCount = std::numeric_limits<unsigned>::max();
54 
55     SourceLocation DirectiveLoc;
56     SourceLocation DiagnosticLoc;
57     const std::string Spelling;
58     const std::string Text;
59     unsigned Min, Max;
60     bool MatchAnyLine;
61     bool MatchAnyFileAndLine; // `MatchAnyFileAndLine` implies `MatchAnyLine`.
62 
63     Directive(const Directive &) = delete;
64     Directive &operator=(const Directive &) = delete;
65     virtual ~Directive() = default;
66 
67     // Returns true if directive text is valid.
68     // Otherwise returns false and populates E.
69     virtual bool isValid(std::string &Error) = 0;
70 
71     // Returns true on match.
72     virtual bool match(StringRef S) = 0;
73 
74   protected:
Directive(SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc,StringRef Spelling,bool MatchAnyFileAndLine,bool MatchAnyLine,StringRef Text,unsigned Min,unsigned Max)75     Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
76               StringRef Spelling, bool MatchAnyFileAndLine, bool MatchAnyLine,
77               StringRef Text, unsigned Min, unsigned Max)
78         : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc),
79           Spelling(Spelling), Text(Text), Min(Min), Max(Max),
80           MatchAnyLine(MatchAnyLine || MatchAnyFileAndLine),
81           MatchAnyFileAndLine(MatchAnyFileAndLine) {
82       assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!");
83       assert((!DiagnosticLoc.isInvalid() || MatchAnyLine) &&
84              "DiagnosticLoc is invalid!");
85     }
86   };
87 
88   using DirectiveList = std::vector<std::unique_ptr<Directive>>;
89 
90   /// ExpectedData - owns directive objects and deletes on destructor.
91   struct ExpectedData {
92     DirectiveList Errors;
93     DirectiveList Warnings;
94     DirectiveList Remarks;
95     DirectiveList Notes;
96 
ResetExpectedData97     void Reset() {
98       Errors.clear();
99       Warnings.clear();
100       Remarks.clear();
101       Notes.clear();
102     }
103   };
104 
105   enum DirectiveStatus {
106     HasNoDirectives,
107     HasNoDirectivesReported,
108     HasExpectedNoDiagnostics,
109     HasOtherExpectedDirectives
110   };
111 
112   struct ParsingState {
113     DirectiveStatus Status;
114     std::string FirstNoDiagnosticsDirective;
115   };
116 
117   class MarkerTracker;
118 
119 private:
120   DiagnosticsEngine &Diags;
121   DiagnosticConsumer *PrimaryClient;
122   std::unique_ptr<DiagnosticConsumer> PrimaryClientOwner;
123   std::unique_ptr<TextDiagnosticBuffer> Buffer;
124   std::unique_ptr<MarkerTracker> Markers;
125   const Preprocessor *CurrentPreprocessor = nullptr;
126   const LangOptions *LangOpts = nullptr;
127   SourceManager *SrcManager = nullptr;
128   unsigned ActiveSourceFiles = 0;
129   ParsingState State;
130   ExpectedData ED;
131 
132   void CheckDiagnostics();
133 
setSourceManager(SourceManager & SM)134   void setSourceManager(SourceManager &SM) {
135     assert((!SrcManager || SrcManager == &SM) && "SourceManager changed!");
136     SrcManager = &SM;
137   }
138 
139   // These facilities are used for validation in debug builds.
140   class UnparsedFileStatus {
141     OptionalFileEntryRef File;
142     bool FoundDirectives;
143 
144   public:
UnparsedFileStatus(OptionalFileEntryRef File,bool FoundDirectives)145     UnparsedFileStatus(OptionalFileEntryRef File, bool FoundDirectives)
146         : File(File), FoundDirectives(FoundDirectives) {}
147 
getFile()148     OptionalFileEntryRef getFile() const { return File; }
foundDirectives()149     bool foundDirectives() const { return FoundDirectives; }
150   };
151 
152   using ParsedFilesMap = llvm::DenseMap<FileID, const FileEntry *>;
153   using UnparsedFilesMap = llvm::DenseMap<FileID, UnparsedFileStatus>;
154 
155   ParsedFilesMap ParsedFiles;
156   UnparsedFilesMap UnparsedFiles;
157 
158 public:
159   /// Create a new verifying diagnostic client, which will issue errors to
160   /// the currently-attached diagnostic client when a diagnostic does not match
161   /// what is expected (as indicated in the source file).
162   VerifyDiagnosticConsumer(DiagnosticsEngine &Diags);
163   ~VerifyDiagnosticConsumer() override;
164 
165   void BeginSourceFile(const LangOptions &LangOpts,
166                        const Preprocessor *PP) override;
167 
168   void EndSourceFile() override;
169 
170   enum ParsedStatus {
171     /// File has been processed via HandleComment.
172     IsParsed,
173 
174     /// File has diagnostics and may have directives.
175     IsUnparsed,
176 
177     /// File has diagnostics but guaranteed no directives.
178     IsUnparsedNoDirectives
179   };
180 
181   /// Update lists of parsed and unparsed files.
182   void UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS);
183 
184   bool HandleComment(Preprocessor &PP, SourceRange Comment) override;
185 
186   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
187                         const Diagnostic &Info) override;
188 };
189 
190 } // namespace clang
191 
192 #endif // LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
193