aboutsummaryrefslogtreecommitdiff
path: root/lib/Frontend/VerifyDiagnosticConsumer.cpp
diff options
context:
space:
mode:
authorJordan Rose <jordan_rose@apple.com>2012-07-11 19:58:23 +0000
committerJordan Rose <jordan_rose@apple.com>2012-07-11 19:58:23 +0000
commit78541c433049322b27b4f437973076ba29cff709 (patch)
tree277d3272b9b897d6897ff5d4457ebc1e65a82938 /lib/Frontend/VerifyDiagnosticConsumer.cpp
parent2db5abb43b7a7fce4771063f6a5de561780c1335 (diff)
Allow -verify directives to be filtered by preprocessing.
This is accomplished by making VerifyDiagnosticsConsumer a CommentHandler, which then only reads the -verify directives that are actually in live blocks of code. It also makes it simpler to handle -verify directives that appear in header files, though we still have to manually reparse some files depending on how they are generated. This requires some test changes. In particular, all PCH tests now have their -verify directives outside the "header" portion of the file, using the @line syntax added in r159978. Other tests have been modified mostly to make it clear what is being tested, and to prevent polluting the expected output with the directives themselves. Patch by Andy Gibbs! (with slight modifications) The new Frontend/verify-* tests exercise the functionality of this commit, as well as r159978, r159979, and r160053 (Andy's other -verify enhancements). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160068 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Frontend/VerifyDiagnosticConsumer.cpp')
-rw-r--r--lib/Frontend/VerifyDiagnosticConsumer.cpp118
1 files changed, 90 insertions, 28 deletions
diff --git a/lib/Frontend/VerifyDiagnosticConsumer.cpp b/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 236b700662..8aa65f1f1f 100644
--- a/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Basic/FileManager.h"
#include "clang/Frontend/VerifyDiagnosticConsumer.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/TextDiagnosticBuffer.h"
@@ -43,15 +44,15 @@ VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
const Preprocessor *PP) {
- // FIXME: Const hack, we screw up the preprocessor but in practice its ok
- // because it doesn't get reused. It would be better if we could make a copy
- // though.
- CurrentPreprocessor = const_cast<Preprocessor*>(PP);
+ CurrentPreprocessor = PP;
+ if (PP) const_cast<Preprocessor*>(PP)->addCommentHandler(this);
PrimaryClient->BeginSourceFile(LangOpts, PP);
}
void VerifyDiagnosticConsumer::EndSourceFile() {
+ if (CurrentPreprocessor)
+ const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this);
CheckDiagnostics();
PrimaryClient->EndSourceFile();
@@ -61,9 +62,9 @@ void VerifyDiagnosticConsumer::EndSourceFile() {
void VerifyDiagnosticConsumer::HandleDiagnostic(
DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
- if (FirstErrorFID.isInvalid() && Info.hasSourceManager()) {
+ if (Info.hasSourceManager()) {
const SourceManager &SM = Info.getSourceManager();
- FirstErrorFID = SM.getFileID(Info.getLocation());
+ FilesWithDiagnostics.insert(SM.getFileID(Info.getLocation()));
}
// Send the diagnostic to the buffer, we will check it once we reach the end
// of the source file (or are destructed).
@@ -122,8 +123,8 @@ private:
class ParseHelper
{
public:
- ParseHelper(const char *Begin, const char *End)
- : Begin(Begin), End(End), C(Begin), P(Begin), PEnd(NULL) { }
+ ParseHelper(StringRef S)
+ : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(NULL) { }
// Return true if string literal is next.
bool Next(StringRef S) {
@@ -190,11 +191,12 @@ private:
/// ParseDirective - Go through the comment and see if it indicates expected
/// diagnostics. If so, then put them in the appropriate directive list.
///
-static void ParseDirective(const char *CommentStart, unsigned CommentLen,
- ExpectedData &ED, SourceManager &SM,
+/// Returns true if any valid directives were found.
+static bool ParseDirective(StringRef S, ExpectedData &ED, SourceManager &SM,
SourceLocation Pos, DiagnosticsEngine &Diags) {
// A single comment may contain multiple directives.
- for (ParseHelper PH(CommentStart, CommentStart+CommentLen); !PH.Done();) {
+ bool FoundDirective = false;
+ for (ParseHelper PH(S); !PH.Done();) {
// Search for token: expected
if (!PH.Search("expected"))
break;
@@ -329,19 +331,77 @@ static void ParseDirective(const char *CommentStart, unsigned CommentLen,
Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text,
Min, Max);
std::string Error;
- if (D->isValid(Error))
+ if (D->isValid(Error)) {
DL->push_back(D);
- else {
+ FoundDirective = true;
+ } else {
Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
diag::err_verify_invalid_content)
<< KindStr << Error;
}
}
+
+ return FoundDirective;
+}
+
+/// HandleComment - Hook into the preprocessor and extract comments containing
+/// expected errors and warnings.
+bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
+ SourceRange Comment) {
+ SourceManager &SM = PP.getSourceManager();
+ SourceLocation CommentBegin = Comment.getBegin();
+
+ const char *CommentRaw = SM.getCharacterData(CommentBegin);
+ StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
+
+ if (C.empty())
+ return false;
+
+ // Fold any "\<EOL>" sequences
+ size_t loc = C.find('\\');
+ if (loc == StringRef::npos) {
+ if (ParseDirective(C, ED, SM, CommentBegin, PP.getDiagnostics()))
+ if (const FileEntry *E = SM.getFileEntryForID(SM.getFileID(CommentBegin)))
+ FilesWithDirectives.insert(E);
+ return false;
+ }
+
+ std::string C2;
+ C2.reserve(C.size());
+
+ for (size_t last = 0;; loc = C.find('\\', last)) {
+ if (loc == StringRef::npos || loc == C.size()) {
+ C2 += C.substr(last);
+ break;
+ }
+ C2 += C.substr(last, loc-last);
+ last = loc + 1;
+
+ if (C[last] == '\n' || C[last] == '\r') {
+ ++last;
+
+ // Escape \r\n or \n\r, but not \n\n.
+ if (last < C.size())
+ if (C[last] == '\n' || C[last] == '\r')
+ if (C[last] != C[last-1])
+ ++last;
+ } else {
+ // This was just a normal backslash.
+ C2 += '\\';
+ }
+ }
+
+ if (!C2.empty())
+ if (ParseDirective(C2, ED, SM, CommentBegin, PP.getDiagnostics()))
+ if (const FileEntry *E = SM.getFileEntryForID(SM.getFileID(CommentBegin)))
+ FilesWithDirectives.insert(E);
+ return false;
}
/// FindExpectedDiags - Lex the main source file to find all of the
// expected errors and warnings.
-static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED, FileID FID) {
+static void FindExpectedDiags(const Preprocessor &PP, ExpectedData &ED,
+ FileID FID) {
// Create a raw lexer to pull all the comments out of FID.
if (FID.isInvalid())
return;
@@ -364,8 +424,7 @@ static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED, FileID FID) {
if (Comment.empty()) continue;
// Find all expected errors/warnings/notes.
- ParseDirective(&Comment[0], Comment.size(), ED, SM, Tok.getLocation(),
- PP.getDiagnostics());
+ ParseDirective(Comment, ED, SM, Tok.getLocation(), PP.getDiagnostics());
};
}
@@ -497,18 +556,21 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() {
// markers. If not then any diagnostics are unexpected.
if (CurrentPreprocessor) {
SourceManager &SM = CurrentPreprocessor->getSourceManager();
- // Extract expected-error strings from main file.
- FindExpectedDiags(*CurrentPreprocessor, ED, SM.getMainFileID());
- // Only check for expectations in other diagnostic locations
- // if they are not the main file (via ID or FileEntry) - the main
- // file has already been looked at, and its expectations must not
- // be added twice.
- if (!FirstErrorFID.isInvalid() && FirstErrorFID != SM.getMainFileID()
- && (!SM.getFileEntryForID(FirstErrorFID)
- || (SM.getFileEntryForID(FirstErrorFID) !=
- SM.getFileEntryForID(SM.getMainFileID())))) {
- FindExpectedDiags(*CurrentPreprocessor, ED, FirstErrorFID);
- FirstErrorFID = FileID();
+ // Only check for expectations in other diagnostic locations not
+ // captured during normal parsing.
+ // FIXME: This check is currently necessary while synthesized files may
+ // not have their expected-* directives captured during parsing. These
+ // cases should be fixed and the following loop replaced with one which
+ // checks only during a debug build and asserts on a mismatch.
+ for (FilesWithDiagnosticsSet::iterator I = FilesWithDiagnostics.begin(),
+ End = FilesWithDiagnostics.end();
+ I != End; ++I) {
+ const FileEntry *E = SM.getFileEntryForID(*I);
+ if (!E || !FilesWithDirectives.count(E)) {
+ if (E)
+ FilesWithDirectives.insert(E);
+ FindExpectedDiags(*CurrentPreprocessor, ED, *I);
+ }
}
// Check that the expected diagnostics occurred.