VerifyDiagnosticConsumer.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. #pragma once
  2. #ifdef __GNUC__
  3. #pragma GCC diagnostic push
  4. #pragma GCC diagnostic ignored "-Wunused-parameter"
  5. #endif
  6. //===- VerifyDiagnosticConsumer.h - Verifying Diagnostic Client -*- C++ -*-===//
  7. //
  8. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  9. // See https://llvm.org/LICENSE.txt for license information.
  10. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #ifndef LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
  14. #define LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
  15. #include "clang/Basic/Diagnostic.h"
  16. #include "clang/Basic/FileManager.h"
  17. #include "clang/Basic/LLVM.h"
  18. #include "clang/Basic/SourceLocation.h"
  19. #include "clang/Lex/Preprocessor.h"
  20. #include "llvm/ADT/DenseMap.h"
  21. #include "llvm/ADT/PointerIntPair.h"
  22. #include "llvm/ADT/StringRef.h"
  23. #include <cassert>
  24. #include <limits>
  25. #include <memory>
  26. #include <string>
  27. #include <vector>
  28. namespace clang {
  29. class FileEntry;
  30. class LangOptions;
  31. class SourceManager;
  32. class TextDiagnosticBuffer;
  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. public:
  198. /// Constant representing n or more matches.
  199. static const unsigned MaxCount = std::numeric_limits<unsigned>::max();
  200. SourceLocation DirectiveLoc;
  201. SourceLocation DiagnosticLoc;
  202. const std::string Text;
  203. unsigned Min, Max;
  204. bool MatchAnyLine;
  205. bool MatchAnyFileAndLine; // `MatchAnyFileAndLine` implies `MatchAnyLine`.
  206. Directive(const Directive &) = delete;
  207. Directive &operator=(const Directive &) = delete;
  208. virtual ~Directive() = default;
  209. // Returns true if directive text is valid.
  210. // Otherwise returns false and populates E.
  211. virtual bool isValid(std::string &Error) = 0;
  212. // Returns true on match.
  213. virtual bool match(StringRef S) = 0;
  214. protected:
  215. Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
  216. bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
  217. unsigned Min, unsigned Max)
  218. : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc), Text(Text),
  219. Min(Min), Max(Max), MatchAnyLine(MatchAnyLine || MatchAnyFileAndLine),
  220. MatchAnyFileAndLine(MatchAnyFileAndLine) {
  221. assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!");
  222. assert((!DiagnosticLoc.isInvalid() || MatchAnyLine) &&
  223. "DiagnosticLoc is invalid!");
  224. }
  225. };
  226. using DirectiveList = std::vector<std::unique_ptr<Directive>>;
  227. /// ExpectedData - owns directive objects and deletes on destructor.
  228. struct ExpectedData {
  229. DirectiveList Errors;
  230. DirectiveList Warnings;
  231. DirectiveList Remarks;
  232. DirectiveList Notes;
  233. void Reset() {
  234. Errors.clear();
  235. Warnings.clear();
  236. Remarks.clear();
  237. Notes.clear();
  238. }
  239. };
  240. enum DirectiveStatus {
  241. HasNoDirectives,
  242. HasNoDirectivesReported,
  243. HasExpectedNoDiagnostics,
  244. HasOtherExpectedDirectives
  245. };
  246. class MarkerTracker;
  247. private:
  248. DiagnosticsEngine &Diags;
  249. DiagnosticConsumer *PrimaryClient;
  250. std::unique_ptr<DiagnosticConsumer> PrimaryClientOwner;
  251. std::unique_ptr<TextDiagnosticBuffer> Buffer;
  252. std::unique_ptr<MarkerTracker> Markers;
  253. const Preprocessor *CurrentPreprocessor = nullptr;
  254. const LangOptions *LangOpts = nullptr;
  255. SourceManager *SrcManager = nullptr;
  256. unsigned ActiveSourceFiles = 0;
  257. DirectiveStatus Status;
  258. ExpectedData ED;
  259. void CheckDiagnostics();
  260. void setSourceManager(SourceManager &SM) {
  261. assert((!SrcManager || SrcManager == &SM) && "SourceManager changed!");
  262. SrcManager = &SM;
  263. }
  264. // These facilities are used for validation in debug builds.
  265. class UnparsedFileStatus {
  266. llvm::PointerIntPair<const FileEntry *, 1, bool> Data;
  267. public:
  268. UnparsedFileStatus(const FileEntry *File, bool FoundDirectives)
  269. : Data(File, FoundDirectives) {}
  270. const FileEntry *getFile() const { return Data.getPointer(); }
  271. bool foundDirectives() const { return Data.getInt(); }
  272. };
  273. using ParsedFilesMap = llvm::DenseMap<FileID, const FileEntry *>;
  274. using UnparsedFilesMap = llvm::DenseMap<FileID, UnparsedFileStatus>;
  275. ParsedFilesMap ParsedFiles;
  276. UnparsedFilesMap UnparsedFiles;
  277. public:
  278. /// Create a new verifying diagnostic client, which will issue errors to
  279. /// the currently-attached diagnostic client when a diagnostic does not match
  280. /// what is expected (as indicated in the source file).
  281. VerifyDiagnosticConsumer(DiagnosticsEngine &Diags);
  282. ~VerifyDiagnosticConsumer() override;
  283. void BeginSourceFile(const LangOptions &LangOpts,
  284. const Preprocessor *PP) override;
  285. void EndSourceFile() override;
  286. enum ParsedStatus {
  287. /// File has been processed via HandleComment.
  288. IsParsed,
  289. /// File has diagnostics and may have directives.
  290. IsUnparsed,
  291. /// File has diagnostics but guaranteed no directives.
  292. IsUnparsedNoDirectives
  293. };
  294. /// Update lists of parsed and unparsed files.
  295. void UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS);
  296. bool HandleComment(Preprocessor &PP, SourceRange Comment) override;
  297. void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
  298. const Diagnostic &Info) override;
  299. };
  300. } // namespace clang
  301. #endif // LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
  302. #ifdef __GNUC__
  303. #pragma GCC diagnostic pop
  304. #endif