//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICSCLIENT_H
-#define LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICSCLIENT_H
+#ifndef LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
+#define LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H
#include "clang/Basic/Diagnostic.h"
-#include "llvm/ADT/OwningPtr.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/PointerIntPair.h"
+#include "llvm/ADT/STLExtras.h"
+#include <climits>
+#include <memory>
namespace clang {
class DiagnosticsEngine;
class TextDiagnosticBuffer;
+class FileEntry;
/// VerifyDiagnosticConsumer - Create a diagnostic client which will use
/// markers in the input source to check that all the emitted diagnostics match
/// Indicating that a line expects an error or a warning is simple. Put a
/// comment on the line that has the diagnostic, use:
///
-/// expected-{error,warning,note}
+/// \code
+/// expected-{error,warning,remark,note}
+/// \endcode
///
-/// to tag if it's an expected error 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.
+/// 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:
///
/// 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).
+///
/// 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
/// void f(); // expected-note 2 {{previous declaration is here}}
/// \endcode
///
-/// Regex matching mode may be selected by appending '-re' to type. Example:
+/// Where the diagnostic is expected to occur a minimum number of times, this
+/// can be specified by appending a '+' to the number. Example:
///
-/// expected-error-re
+/// \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 has type 'struct .'}}
-/// // expected-error-re {{variable has has type 'struct .*'}}
-/// // expected-error-re {{variable has has type 'struct (.*)'}}
-/// // expected-error-re {{variable has has type 'struct[[:space:]](.*)'}}
+/// // 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:
///
-class VerifyDiagnosticConsumer: public DiagnosticConsumer {
+/// \code
+/// // expected-no-diagnostics
+/// \endcode
+///
+class VerifyDiagnosticConsumer: public DiagnosticConsumer,
+ public CommentHandler {
public:
+ /// Directive - Abstract class representing a parsed verify directive.
+ ///
+ class Directive {
+ public:
+ static Directive *create(bool RegexKind, SourceLocation DirectiveLoc,
+ SourceLocation DiagnosticLoc, bool MatchAnyLine,
+ StringRef Text, unsigned Min, unsigned Max);
+ public:
+ /// Constant representing n or more matches.
+ static const unsigned MaxCount = UINT_MAX;
+
+ SourceLocation DirectiveLoc;
+ SourceLocation DiagnosticLoc;
+ const std::string Text;
+ unsigned Min, Max;
+ bool MatchAnyLine;
+
+ virtual ~Directive() { }
+
+ // 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 MatchAnyLine, StringRef Text, unsigned Min, unsigned Max)
+ : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc),
+ Text(Text), Min(Min), Max(Max), MatchAnyLine(MatchAnyLine) {
+ assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!");
+ assert(!DiagnosticLoc.isInvalid() && "DiagnosticLoc is invalid!");
+ }
+
+ private:
+ Directive(const Directive &) LLVM_DELETED_FUNCTION;
+ void operator=(const Directive &) LLVM_DELETED_FUNCTION;
+ };
+
+ typedef std::vector<Directive*> DirectiveList;
+
+ /// ExpectedData - owns directive objects and deletes on destructor.
+ ///
+ struct ExpectedData {
+ DirectiveList Errors;
+ DirectiveList Warnings;
+ DirectiveList Remarks;
+ DirectiveList Notes;
+
+ void Reset() {
+ llvm::DeleteContainerPointers(Errors);
+ llvm::DeleteContainerPointers(Warnings);
+ llvm::DeleteContainerPointers(Remarks);
+ llvm::DeleteContainerPointers(Notes);
+ }
+
+ ~ExpectedData() { Reset(); }
+ };
+
+ enum DirectiveStatus {
+ HasNoDirectives,
+ HasNoDirectivesReported,
+ HasExpectedNoDiagnostics,
+ HasOtherExpectedDirectives
+ };
+
+private:
DiagnosticsEngine &Diags;
DiagnosticConsumer *PrimaryClient;
bool OwnsPrimaryClient;
- OwningPtr<TextDiagnosticBuffer> Buffer;
- Preprocessor *CurrentPreprocessor;
+ std::unique_ptr<TextDiagnosticBuffer> Buffer;
+ const Preprocessor *CurrentPreprocessor;
+ const LangOptions *LangOpts;
+ SourceManager *SrcManager;
+ unsigned ActiveSourceFiles;
+ DirectiveStatus Status;
+ ExpectedData ED;
-private:
- FileID FirstErrorFID; // FileID of first diagnostic
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(); }
+ };
+ typedef llvm::DenseMap<FileID, const FileEntry *> ParsedFilesMap;
+ typedef llvm::DenseMap<FileID, UnparsedFileStatus> UnparsedFilesMap;
+ ParsedFilesMap ParsedFiles;
+ UnparsedFilesMap UnparsedFiles;
public:
/// Create a new verifying diagnostic client, which will issue errors to
VerifyDiagnosticConsumer(DiagnosticsEngine &Diags);
~VerifyDiagnosticConsumer();
- virtual void BeginSourceFile(const LangOptions &LangOpts,
- const Preprocessor *PP);
+ 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
+ };
+
+ /// \brief Update lists of parsed and unparsed files.
+ void UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS);
- virtual void EndSourceFile();
+ bool HandleComment(Preprocessor &PP, SourceRange Comment) override;
- virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
- const Diagnostic &Info);
-
- virtual DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const;
+ void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+ const Diagnostic &Info) override;
};
} // end namspace clang