123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- #pragma once
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-parameter"
- #endif
- //===- VerifyDiagnosticConsumer.h - Verifying Diagnostic Client -*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #ifndef LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
- #define LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
- #include "clang/Basic/Diagnostic.h"
- #include "clang/Basic/FileManager.h"
- #include "clang/Basic/LLVM.h"
- #include "clang/Basic/SourceLocation.h"
- #include "clang/Lex/Preprocessor.h"
- #include "llvm/ADT/DenseMap.h"
- #include "llvm/ADT/PointerIntPair.h"
- #include "llvm/ADT/StringRef.h"
- #include <cassert>
- #include <limits>
- #include <memory>
- #include <string>
- #include <vector>
- namespace clang {
- class FileEntry;
- class LangOptions;
- class SourceManager;
- class TextDiagnosticBuffer;
- /// VerifyDiagnosticConsumer - Create a diagnostic client which will use
- /// markers in the input source to check that all the emitted diagnostics match
- /// those expected.
- ///
- /// INVOKING THE DIAGNOSTIC CHECKER:
- ///
- /// VerifyDiagnosticConsumer is typically invoked via the "-verify" option to
- /// "clang -cc1". "-verify" is equivalent to "-verify=expected", so all
- /// diagnostics are typically specified with the prefix "expected". For
- /// example:
- ///
- /// \code
- /// int A = B; // expected-error {{use of undeclared identifier 'B'}}
- /// \endcode
- ///
- /// Custom prefixes can be specified as a comma-separated sequence. Each
- /// prefix must start with a letter and contain only alphanumeric characters,
- /// hyphens, and underscores. For example, given just "-verify=foo,bar",
- /// the above diagnostic would be ignored, but the following diagnostics would
- /// be recognized:
- ///
- /// \code
- /// int A = B; // foo-error {{use of undeclared identifier 'B'}}
- /// int C = D; // bar-error {{use of undeclared identifier 'D'}}
- /// \endcode
- ///
- /// Multiple occurrences accumulate prefixes. For example,
- /// "-verify -verify=foo,bar -verify=baz" is equivalent to
- /// "-verify=expected,foo,bar,baz".
- ///
- /// SPECIFYING DIAGNOSTICS:
- ///
- /// Indicating that a line expects an error or a warning is simple. Put a
- /// comment on the line that has the diagnostic, use:
- ///
- /// \code
- /// expected-{error,warning,remark,note}
- /// \endcode
- ///
- /// to tag if it's an expected error, remark or warning, and place the expected
- /// text between {{ and }} markers. The full text doesn't have to be included,
- /// only enough to ensure that the correct diagnostic was emitted.
- ///
- /// Here's an example:
- ///
- /// \code
- /// int A = B; // expected-error {{use of undeclared identifier 'B'}}
- /// \endcode
- ///
- /// You can place as many diagnostics on one line as you wish. To make the code
- /// more readable, you can use slash-newline to separate out the diagnostics.
- ///
- /// Alternatively, it is possible to specify the line on which the diagnostic
- /// should appear by appending "@<line>" to "expected-<type>", for example:
- ///
- /// \code
- /// #warning some text
- /// // expected-warning@10 {{some text}}
- /// \endcode
- ///
- /// The line number may be absolute (as above), or relative to the current
- /// line by prefixing the number with either '+' or '-'.
- ///
- /// If the diagnostic is generated in a separate file, for example in a shared
- /// header file, it may be beneficial to be able to declare the file in which
- /// the diagnostic will appear, rather than placing the expected-* directive in
- /// the actual file itself. This can be done using the following syntax:
- ///
- /// \code
- /// // expected-error@path/include.h:15 {{error message}}
- /// \endcode
- ///
- /// The path can be absolute or relative and the same search paths will be used
- /// as for #include directives. The line number in an external file may be
- /// substituted with '*' meaning that any line number will match (useful where
- /// the included file is, for example, a system header where the actual line
- /// number may change and is not critical).
- ///
- /// As an alternative to specifying a fixed line number, the location of a
- /// diagnostic can instead be indicated by a marker of the form "#<marker>".
- /// Markers are specified by including them in a comment, and then referenced
- /// by appending the marker to the diagnostic with "@#<marker>":
- ///
- /// \code
- /// #warning some text // #1
- /// // expected-warning@#1 {{some text}}
- /// \endcode
- ///
- /// The name of a marker used in a directive must be unique within the
- /// compilation.
- ///
- /// The simple syntax above allows each specification to match exactly one
- /// error. You can use the extended syntax to customize this. The extended
- /// syntax is "expected-<type> <n> {{diag text}}", where \<type> is one of
- /// "error", "warning" or "note", and \<n> is a positive integer. This allows
- /// the diagnostic to appear as many times as specified. Example:
- ///
- /// \code
- /// void f(); // expected-note 2 {{previous declaration is here}}
- /// \endcode
- ///
- /// Where the diagnostic is expected to occur a minimum number of times, this
- /// can be specified by appending a '+' to the number. Example:
- ///
- /// \code
- /// void f(); // expected-note 0+ {{previous declaration is here}}
- /// void g(); // expected-note 1+ {{previous declaration is here}}
- /// \endcode
- ///
- /// In the first example, the diagnostic becomes optional, i.e. it will be
- /// swallowed if it occurs, but will not generate an error if it does not
- /// occur. In the second example, the diagnostic must occur at least once.
- /// As a short-hand, "one or more" can be specified simply by '+'. Example:
- ///
- /// \code
- /// void g(); // expected-note + {{previous declaration is here}}
- /// \endcode
- ///
- /// A range can also be specified by "<n>-<m>". Example:
- ///
- /// \code
- /// void f(); // expected-note 0-1 {{previous declaration is here}}
- /// \endcode
- ///
- /// In this example, the diagnostic may appear only once, if at all.
- ///
- /// Regex matching mode may be selected by appending '-re' to type and
- /// including regexes wrapped in double curly braces in the directive, such as:
- ///
- /// \code
- /// expected-error-re {{format specifies type 'wchar_t **' (aka '{{.+}}')}}
- /// \endcode
- ///
- /// Examples matching error: "variable has incomplete type 'struct s'"
- ///
- /// \code
- /// // expected-error {{variable has incomplete type 'struct s'}}
- /// // expected-error {{variable has incomplete type}}
- ///
- /// // expected-error-re {{variable has type 'struct {{.}}'}}
- /// // expected-error-re {{variable has type 'struct {{.*}}'}}
- /// // expected-error-re {{variable has type 'struct {{(.*)}}'}}
- /// // expected-error-re {{variable has type 'struct{{[[:space:]](.*)}}'}}
- /// \endcode
- ///
- /// VerifyDiagnosticConsumer expects at least one expected-* directive to
- /// be found inside the source code. If no diagnostics are expected the
- /// following directive can be used to indicate this:
- ///
- /// \code
- /// // expected-no-diagnostics
- /// \endcode
- ///
- class VerifyDiagnosticConsumer: public DiagnosticConsumer,
- public CommentHandler {
- public:
- /// Directive - Abstract class representing a parsed verify directive.
- ///
- class Directive {
- public:
- static std::unique_ptr<Directive>
- create(bool RegexKind, SourceLocation DirectiveLoc,
- SourceLocation DiagnosticLoc, bool MatchAnyFileAndLine,
- bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max);
- public:
- /// Constant representing n or more matches.
- static const unsigned MaxCount = std::numeric_limits<unsigned>::max();
- SourceLocation DirectiveLoc;
- SourceLocation DiagnosticLoc;
- const std::string Text;
- unsigned Min, Max;
- bool MatchAnyLine;
- bool MatchAnyFileAndLine; // `MatchAnyFileAndLine` implies `MatchAnyLine`.
- Directive(const Directive &) = delete;
- Directive &operator=(const Directive &) = delete;
- virtual ~Directive() = default;
- // Returns true if directive text is valid.
- // Otherwise returns false and populates E.
- virtual bool isValid(std::string &Error) = 0;
- // Returns true on match.
- virtual bool match(StringRef S) = 0;
- protected:
- Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
- bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
- unsigned Min, unsigned Max)
- : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc), Text(Text),
- Min(Min), Max(Max), MatchAnyLine(MatchAnyLine || MatchAnyFileAndLine),
- MatchAnyFileAndLine(MatchAnyFileAndLine) {
- assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!");
- assert((!DiagnosticLoc.isInvalid() || MatchAnyLine) &&
- "DiagnosticLoc is invalid!");
- }
- };
- using DirectiveList = std::vector<std::unique_ptr<Directive>>;
- /// ExpectedData - owns directive objects and deletes on destructor.
- struct ExpectedData {
- DirectiveList Errors;
- DirectiveList Warnings;
- DirectiveList Remarks;
- DirectiveList Notes;
- void Reset() {
- Errors.clear();
- Warnings.clear();
- Remarks.clear();
- Notes.clear();
- }
- };
- enum DirectiveStatus {
- HasNoDirectives,
- HasNoDirectivesReported,
- HasExpectedNoDiagnostics,
- HasOtherExpectedDirectives
- };
- class MarkerTracker;
- private:
- DiagnosticsEngine &Diags;
- DiagnosticConsumer *PrimaryClient;
- std::unique_ptr<DiagnosticConsumer> PrimaryClientOwner;
- std::unique_ptr<TextDiagnosticBuffer> Buffer;
- std::unique_ptr<MarkerTracker> Markers;
- const Preprocessor *CurrentPreprocessor = nullptr;
- const LangOptions *LangOpts = nullptr;
- SourceManager *SrcManager = nullptr;
- unsigned ActiveSourceFiles = 0;
- DirectiveStatus Status;
- ExpectedData ED;
- void CheckDiagnostics();
- void setSourceManager(SourceManager &SM) {
- assert((!SrcManager || SrcManager == &SM) && "SourceManager changed!");
- SrcManager = &SM;
- }
- // These facilities are used for validation in debug builds.
- class UnparsedFileStatus {
- llvm::PointerIntPair<const FileEntry *, 1, bool> Data;
- public:
- UnparsedFileStatus(const FileEntry *File, bool FoundDirectives)
- : Data(File, FoundDirectives) {}
- const FileEntry *getFile() const { return Data.getPointer(); }
- bool foundDirectives() const { return Data.getInt(); }
- };
- using ParsedFilesMap = llvm::DenseMap<FileID, const FileEntry *>;
- using UnparsedFilesMap = llvm::DenseMap<FileID, UnparsedFileStatus>;
- ParsedFilesMap ParsedFiles;
- UnparsedFilesMap UnparsedFiles;
- public:
- /// Create a new verifying diagnostic client, which will issue errors to
- /// the currently-attached diagnostic client when a diagnostic does not match
- /// what is expected (as indicated in the source file).
- VerifyDiagnosticConsumer(DiagnosticsEngine &Diags);
- ~VerifyDiagnosticConsumer() override;
- void BeginSourceFile(const LangOptions &LangOpts,
- const Preprocessor *PP) override;
- void EndSourceFile() override;
- enum ParsedStatus {
- /// File has been processed via HandleComment.
- IsParsed,
- /// File has diagnostics and may have directives.
- IsUnparsed,
- /// File has diagnostics but guaranteed no directives.
- IsUnparsedNoDirectives
- };
- /// Update lists of parsed and unparsed files.
- void UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS);
- bool HandleComment(Preprocessor &PP, SourceRange Comment) override;
- void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
- const Diagnostic &Info) override;
- };
- } // namespace clang
- #endif // LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
|