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