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: |
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 ; |
92 | DirectiveList Notes; |
93 | |
94 | 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 | |
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: |
137 | UnparsedFileStatus(OptionalFileEntryRef File, bool FoundDirectives) |
138 | : File(File), FoundDirectives(FoundDirectives) {} |
139 | |
140 | OptionalFileEntryRef getFile() const { return File; } |
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 ) 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 | |