Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 14 | pmbaty | 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: |
||
| 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 | |||
| 242 | 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 | |||
| 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: |
||
| 284 | UnparsedFileStatus(const FileEntry *File, bool FoundDirectives) |
||
| 285 | : Data(File, FoundDirectives) {} |
||
| 286 | |||
| 287 | const FileEntry *getFile() const { return Data.getPointer(); } |
||
| 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 |