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