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