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 /// If the diagnostic is generated in a separate file, for example in a shared 65 /// header file, it may be beneficial to be able to declare the file in which 66 /// the diagnostic will appear, rather than placing the expected-* directive in 67 /// the actual file itself. This can be done using the following syntax: 68 /// 69 /// \code 70 /// // expected-error@path/include.h:15 {{error message}} 71 /// \endcode 72 /// 73 /// The path can be absolute or relative and the same search paths will be used 74 /// as for #include directives. 75 /// 76 /// The simple syntax above allows each specification to match exactly one 77 /// error. You can use the extended syntax to customize this. The extended 78 /// syntax is "expected-<type> <n> {{diag text}}", where \<type> is one of 79 /// "error", "warning" or "note", and \<n> is a positive integer. This allows 80 /// the diagnostic to appear as many times as specified. Example: 81 /// 82 /// \code 83 /// void f(); // expected-note 2 {{previous declaration is here}} 84 /// \endcode 85 /// 86 /// Where the diagnostic is expected to occur a minimum number of times, this 87 /// can be specified by appending a '+' to the number. Example: 88 /// 89 /// \code 90 /// void f(); // expected-note 0+ {{previous declaration is here}} 91 /// void g(); // expected-note 1+ {{previous declaration is here}} 92 /// \endcode 93 /// 94 /// In the first example, the diagnostic becomes optional, i.e. it will be 95 /// swallowed if it occurs, but will not generate an error if it does not 96 /// occur. In the second example, the diagnostic must occur at least once. 97 /// As a short-hand, "one or more" can be specified simply by '+'. Example: 98 /// 99 /// \code 100 /// void g(); // expected-note + {{previous declaration is here}} 101 /// \endcode 102 /// 103 /// A range can also be specified by "<n>-<m>". Example: 104 /// 105 /// \code 106 /// void f(); // expected-note 0-1 {{previous declaration is here}} 107 /// \endcode 108 /// 109 /// In this example, the diagnostic may appear only once, if at all. 110 /// 111 /// Regex matching mode may be selected by appending '-re' to type, such as: 112 /// 113 /// \code 114 /// expected-error-re 115 /// \endcode 116 /// 117 /// Examples matching error: "variable has incomplete type 'struct s'" 118 /// 119 /// \code 120 /// // expected-error {{variable has incomplete type 'struct s'}} 121 /// // expected-error {{variable has incomplete type}} 122 /// 123 /// // expected-error-re {{variable has has type 'struct .'}} 124 /// // expected-error-re {{variable has has type 'struct .*'}} 125 /// // expected-error-re {{variable has has type 'struct (.*)'}} 126 /// // expected-error-re {{variable has has type 'struct[[:space:]](.*)'}} 127 /// \endcode 128 /// 129 /// VerifyDiagnosticConsumer expects at least one expected-* directive to 130 /// be found inside the source code. If no diagnostics are expected the 131 /// following directive can be used to indicate this: 132 /// 133 /// \code 134 /// // expected-no-diagnostics 135 /// \endcode 136 /// 137 class VerifyDiagnosticConsumer: public DiagnosticConsumer, 138 public CommentHandler { 139 public: 140 /// Directive - Abstract class representing a parsed verify directive. 141 /// 142 class Directive { 143 public: 144 static Directive *create(bool RegexKind, SourceLocation DirectiveLoc, 145 SourceLocation DiagnosticLoc, 146 StringRef Text, unsigned Min, unsigned Max); 147 public: 148 /// Constant representing n or more matches. 149 static const unsigned MaxCount = UINT_MAX; 150 151 SourceLocation DirectiveLoc; 152 SourceLocation DiagnosticLoc; 153 const std::string Text; 154 unsigned Min, Max; 155 156 virtual ~Directive() { } 157 158 // Returns true if directive text is valid. 159 // Otherwise returns false and populates E. 160 virtual bool isValid(std::string &Error) = 0; 161 162 // Returns true on match. 163 virtual bool match(StringRef S) = 0; 164 165 protected: 166 Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, 167 StringRef Text, unsigned Min, unsigned Max) 168 : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc), 169 Text(Text), Min(Min), Max(Max) { 170 assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!"); 171 assert(!DiagnosticLoc.isInvalid() && "DiagnosticLoc is invalid!"); 172 } 173 174 private: 175 Directive(const Directive &) LLVM_DELETED_FUNCTION; 176 void operator=(const Directive &) LLVM_DELETED_FUNCTION; 177 }; 178 179 typedef std::vector<Directive*> DirectiveList; 180 181 /// ExpectedData - owns directive objects and deletes on destructor. 182 /// 183 struct ExpectedData { 184 DirectiveList Errors; 185 DirectiveList Warnings; 186 DirectiveList Notes; 187 188 ~ExpectedData() { 189 llvm::DeleteContainerPointers(Errors); 190 llvm::DeleteContainerPointers(Warnings); 191 llvm::DeleteContainerPointers(Notes); 192 } 193 }; 194 195 enum DirectiveStatus { 196 HasNoDirectives, 197 HasNoDirectivesReported, 198 HasExpectedNoDiagnostics, 199 HasOtherExpectedDirectives 200 }; 201 202 private: 203 DiagnosticsEngine &Diags; 204 DiagnosticConsumer *PrimaryClient; 205 bool OwnsPrimaryClient; 206 OwningPtr<TextDiagnosticBuffer> Buffer; 207 const Preprocessor *CurrentPreprocessor; 208 const LangOptions *LangOpts; 209 SourceManager *SrcManager; 210 unsigned ActiveSourceFiles; 211 DirectiveStatus Status; 212 ExpectedData ED; 213 214 void CheckDiagnostics(); 215 void setSourceManager(SourceManager &SM) { 216 assert((!SrcManager || SrcManager == &SM) && "SourceManager changed!"); 217 SrcManager = &SM; 218 } 219 220 #ifndef NDEBUG 221 class UnparsedFileStatus { 222 llvm::PointerIntPair<const FileEntry *, 1, bool> Data; 223 224 public: 225 UnparsedFileStatus(const FileEntry *File, bool FoundDirectives) 226 : Data(File, FoundDirectives) {} 227 228 const FileEntry *getFile() const { return Data.getPointer(); } 229 bool foundDirectives() const { return Data.getInt(); } 230 }; 231 232 typedef llvm::DenseMap<FileID, const FileEntry *> ParsedFilesMap; 233 typedef llvm::DenseMap<FileID, UnparsedFileStatus> UnparsedFilesMap; 234 235 ParsedFilesMap ParsedFiles; 236 UnparsedFilesMap UnparsedFiles; 237 #endif 238 239 public: 240 /// Create a new verifying diagnostic client, which will issue errors to 241 /// the currently-attached diagnostic client when a diagnostic does not match 242 /// what is expected (as indicated in the source file). 243 VerifyDiagnosticConsumer(DiagnosticsEngine &Diags); 244 ~VerifyDiagnosticConsumer(); 245 246 virtual void BeginSourceFile(const LangOptions &LangOpts, 247 const Preprocessor *PP); 248 249 virtual void EndSourceFile(); 250 251 enum ParsedStatus { 252 /// File has been processed via HandleComment. 253 IsParsed, 254 255 /// File has diagnostics and may have directives. 256 IsUnparsed, 257 258 /// File has diagnostics but guaranteed no directives. 259 IsUnparsedNoDirectives 260 }; 261 262 /// \brief Update lists of parsed and unparsed files. 263 void UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS); 264 265 virtual bool HandleComment(Preprocessor &PP, SourceRange Comment); 266 267 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 268 const Diagnostic &Info); 269 }; 270 271 } // end namspace clang 272 273 #endif 274