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