1 //===- VerifyDiagnosticConsumer.h - Verifying Diagnostic Client -*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #ifndef LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICSCLIENT_H 11 #define LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICSCLIENT_H 12 13 #include "clang/Basic/Diagnostic.h" 14 #include "clang/Lex/Preprocessor.h" 15 #include "llvm/ADT/DenseMap.h" 16 #include "llvm/ADT/OwningPtr.h" 17 #include "llvm/ADT/PointerIntPair.h" 18 #include "llvm/ADT/STLExtras.h" 19 #include <climits> 20 21 namespace clang { 22 23 class DiagnosticsEngine; 24 class TextDiagnosticBuffer; 25 class FileEntry; 26 27 /// VerifyDiagnosticConsumer - Create a diagnostic client which will use 28 /// markers in the input source to check that all the emitted diagnostics match 29 /// those expected. 30 /// 31 /// USING THE DIAGNOSTIC CHECKER: 32 /// 33 /// Indicating that a line expects an error or a warning is simple. Put a 34 /// comment on the line that has the diagnostic, use: 35 /// 36 /// \code 37 /// expected-{error,warning,note} 38 /// \endcode 39 /// 40 /// to tag if it's an expected error or warning, and place the expected text 41 /// between {{ and }} markers. The full text doesn't have to be included, only 42 /// enough to ensure that the correct diagnostic was emitted. 43 /// 44 /// Here's an example: 45 /// 46 /// \code 47 /// int A = B; // expected-error {{use of undeclared identifier 'B'}} 48 /// \endcode 49 /// 50 /// You can place as many diagnostics on one line as you wish. To make the code 51 /// more readable, you can use slash-newline to separate out the diagnostics. 52 /// 53 /// Alternatively, it is possible to specify the line on which the diagnostic 54 /// should appear by appending "@<line>" to "expected-<type>", for example: 55 /// 56 /// \code 57 /// #warning some text 58 /// // expected-warning@10 {{some text}} 59 /// \endcode 60 /// 61 /// The line number may be absolute (as above), or relative to the current 62 /// line by prefixing the number with either '+' or '-'. 63 /// 64 /// The simple syntax above allows each specification to match exactly one 65 /// error. You can use the extended syntax to customize this. The extended 66 /// syntax is "expected-<type> <n> {{diag text}}", where \<type> is one of 67 /// "error", "warning" or "note", and \<n> is a positive integer. This allows 68 /// the diagnostic to appear as many times as specified. Example: 69 /// 70 /// \code 71 /// void f(); // expected-note 2 {{previous declaration is here}} 72 /// \endcode 73 /// 74 /// Where the diagnostic is expected to occur a minimum number of times, this 75 /// can be specified by appending a '+' to the number. Example: 76 /// 77 /// \code 78 /// void f(); // expected-note 0+ {{previous declaration is here}} 79 /// void g(); // expected-note 1+ {{previous declaration is here}} 80 /// \endcode 81 /// 82 /// In the first example, the diagnostic becomes optional, i.e. it will be 83 /// swallowed if it occurs, but will not generate an error if it does not 84 /// occur. In the second example, the diagnostic must occur at least once. 85 /// As a short-hand, "one or more" can be specified simply by '+'. Example: 86 /// 87 /// \code 88 /// void g(); // expected-note + {{previous declaration is here}} 89 /// \endcode 90 /// 91 /// A range can also be specified by "<n>-<m>". Example: 92 /// 93 /// \code 94 /// void f(); // expected-note 0-1 {{previous declaration is here}} 95 /// \endcode 96 /// 97 /// In this example, the diagnostic may appear only once, if at all. 98 /// 99 /// Regex matching mode may be selected by appending '-re' to type, such as: 100 /// 101 /// \code 102 /// expected-error-re 103 /// \endcode 104 /// 105 /// Examples matching error: "variable has incomplete type 'struct s'" 106 /// 107 /// \code 108 /// // expected-error {{variable has incomplete type 'struct s'}} 109 /// // expected-error {{variable has incomplete type}} 110 /// 111 /// // expected-error-re {{variable has has type 'struct .'}} 112 /// // expected-error-re {{variable has has type 'struct .*'}} 113 /// // expected-error-re {{variable has has type 'struct (.*)'}} 114 /// // expected-error-re {{variable has has type 'struct[[:space:]](.*)'}} 115 /// \endcode 116 /// 117 /// VerifyDiagnosticConsumer expects at least one expected-* directive to 118 /// be found inside the source code. If no diagnostics are expected the 119 /// following directive can be used to indicate this: 120 /// 121 /// \code 122 /// // expected-no-diagnostics 123 /// \endcode 124 /// 125 class VerifyDiagnosticConsumer: public DiagnosticConsumer, 126 public CommentHandler { 127 public: 128 /// Directive - Abstract class representing a parsed verify directive. 129 /// 130 class Directive { 131 public: 132 static Directive *create(bool RegexKind, SourceLocation DirectiveLoc, 133 SourceLocation DiagnosticLoc, 134 StringRef Text, unsigned Min, unsigned Max); 135 public: 136 /// Constant representing n or more matches. 137 static const unsigned MaxCount = UINT_MAX; 138 139 SourceLocation DirectiveLoc; 140 SourceLocation DiagnosticLoc; 141 const std::string Text; 142 unsigned Min, Max; 143 144 virtual ~Directive() { } 145 146 // Returns true if directive text is valid. 147 // Otherwise returns false and populates E. 148 virtual bool isValid(std::string &Error) = 0; 149 150 // Returns true on match. 151 virtual bool match(StringRef S) = 0; 152 153 protected: 154 Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, 155 StringRef Text, unsigned Min, unsigned Max) 156 : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc), 157 Text(Text), Min(Min), Max(Max) { 158 assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!"); 159 assert(!DiagnosticLoc.isInvalid() && "DiagnosticLoc is invalid!"); 160 } 161 162 private: 163 Directive(const Directive &) LLVM_DELETED_FUNCTION; 164 void operator=(const Directive &) LLVM_DELETED_FUNCTION; 165 }; 166 167 typedef std::vector<Directive*> DirectiveList; 168 169 /// ExpectedData - owns directive objects and deletes on destructor. 170 /// 171 struct ExpectedData { 172 DirectiveList Errors; 173 DirectiveList Warnings; 174 DirectiveList Notes; 175 176 ~ExpectedData() { 177 llvm::DeleteContainerPointers(Errors); 178 llvm::DeleteContainerPointers(Warnings); 179 llvm::DeleteContainerPointers(Notes); 180 } 181 }; 182 183 enum DirectiveStatus { 184 HasNoDirectives, 185 HasNoDirectivesReported, 186 HasExpectedNoDiagnostics, 187 HasOtherExpectedDirectives 188 }; 189 190 private: 191 DiagnosticsEngine &Diags; 192 DiagnosticConsumer *PrimaryClient; 193 bool OwnsPrimaryClient; 194 OwningPtr<TextDiagnosticBuffer> Buffer; 195 const Preprocessor *CurrentPreprocessor; 196 const LangOptions *LangOpts; 197 SourceManager *SrcManager; 198 unsigned ActiveSourceFiles; 199 DirectiveStatus Status; 200 ExpectedData ED; 201 202 void CheckDiagnostics(); 203 void setSourceManager(SourceManager &SM) { 204 assert((!SrcManager || SrcManager == &SM) && "SourceManager changed!"); 205 SrcManager = &SM; 206 } 207 208 #ifndef NDEBUG 209 class UnparsedFileStatus { 210 llvm::PointerIntPair<const FileEntry *, 1, bool> Data; 211 212 public: 213 UnparsedFileStatus(const FileEntry *File, bool FoundDirectives) 214 : Data(File, FoundDirectives) {} 215 216 const FileEntry *getFile() const { return Data.getPointer(); } 217 bool foundDirectives() const { return Data.getInt(); } 218 }; 219 220 typedef llvm::DenseMap<FileID, const FileEntry *> ParsedFilesMap; 221 typedef llvm::DenseMap<FileID, UnparsedFileStatus> UnparsedFilesMap; 222 223 ParsedFilesMap ParsedFiles; 224 UnparsedFilesMap UnparsedFiles; 225 #endif 226 227 public: 228 /// Create a new verifying diagnostic client, which will issue errors to 229 /// the currently-attached diagnostic client when a diagnostic does not match 230 /// what is expected (as indicated in the source file). 231 VerifyDiagnosticConsumer(DiagnosticsEngine &Diags); 232 ~VerifyDiagnosticConsumer(); 233 234 virtual void BeginSourceFile(const LangOptions &LangOpts, 235 const Preprocessor *PP); 236 237 virtual void EndSourceFile(); 238 239 enum ParsedStatus { 240 /// File has been processed via HandleComment. 241 IsParsed, 242 243 /// File has diagnostics and may have directives. 244 IsUnparsed, 245 246 /// File has diagnostics but guaranteed no directives. 247 IsUnparsedNoDirectives 248 }; 249 250 /// \brief Update lists of parsed and unparsed files. 251 void UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS); 252 253 virtual bool HandleComment(Preprocessor &PP, SourceRange Comment); 254 255 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 256 const Diagnostic &Info); 257 258 virtual DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const; 259 }; 260 261 } // end namspace clang 262 263 #endif 264