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 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: Directive(SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc,bool MatchAnyFileAndLine,bool MatchAnyLine,StringRef Text,unsigned Min,unsigned Max)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 MatchAnyFileAndLine(MatchAnyFileAndLine) { 227 assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!"); 228 assert((!DiagnosticLoc.isInvalid() || MatchAnyLine) && 229 "DiagnosticLoc is invalid!"); 230 } 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 ResetExpectedData242 void Reset() { 243 Errors.clear(); 244 Warnings.clear(); 245 Remarks.clear(); 246 Notes.clear(); 247 } 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 setSourceManager(SourceManager & SM)274 void setSourceManager(SourceManager &SM) { 275 assert((!SrcManager || SrcManager == &SM) && "SourceManager changed!"); 276 SrcManager = &SM; 277 } 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: UnparsedFileStatus(const FileEntry * File,bool FoundDirectives)284 UnparsedFileStatus(const FileEntry *File, bool FoundDirectives) 285 : Data(File, FoundDirectives) {} 286 getFile()287 const FileEntry *getFile() const { return Data.getPointer(); } foundDirectives()288 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 332