diff options
author | Hans Wennborg <hans@hanshq.net> | 2013-12-11 23:40:50 +0000 |
---|---|---|
committer | Hans Wennborg <hans@hanshq.net> | 2013-12-11 23:40:50 +0000 |
commit | cda4b6dd007c4726f36f4cd24271b49cc19f1f5a (patch) | |
tree | e6ce6c3b704e3032a58fac25b81be60e467c3d60 /clang/lib/Frontend/VerifyDiagnosticConsumer.cpp | |
parent | 6030c84a2fc1b6f84494807d27bc96d8f522b5c0 (diff) | |
download | bcm5719-llvm-cda4b6dd007c4726f36f4cd24271b49cc19f1f5a.tar.gz bcm5719-llvm-cda4b6dd007c4726f36f4cd24271b49cc19f1f5a.zip |
Change semantics of regex expectations in the diagnostic verifier
Previously, a line like
// expected-error-re {{foo}}
treats the entirety of foo as a regex. This is inconvenient when matching type
names containing regex characters. For example, to match
"void *(class test8::A::*)(void)" inside such a regex, one would have to type
"void \*\(class test8::A::\*\)\(void\)".
This patch changes the semantics of expected-error-re to only treat the parts
of the directive wrapped in double curly braces as regexes. This avoids the
escaping problem and leads to nicer patterns for those cases; see e.g. the
change to test/Sema/format-strings-scanf.c.
(The balanced search for closing }} of a directive also makes us handle the
full directive in test\SemaCXX\constexpr-printing.cpp:41 and :53.)
Differential Revision: http://llvm-reviews.chandlerc.com/D2388
llvm-svn: 197092
Diffstat (limited to 'clang/lib/Frontend/VerifyDiagnosticConsumer.cpp')
-rw-r--r-- | clang/lib/Frontend/VerifyDiagnosticConsumer.cpp | 97 |
1 files changed, 91 insertions, 6 deletions
diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp index 77901a73e15..b53fde257de 100644 --- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp +++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp @@ -181,8 +181,8 @@ public: class RegexDirective : public Directive { public: RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, - StringRef Text, unsigned Min, unsigned Max) - : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max), Regex(Text) { } + StringRef Text, unsigned Min, unsigned Max, StringRef RegexStr) + : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max), Regex(RegexStr) { } virtual bool isValid(std::string &Error) { if (Regex.isValid(Error)) @@ -249,6 +249,30 @@ public: return false; } + // Return true if a CloseBrace that closes the OpenBrace at the current nest + // level is found. When true, P marks begin-position of CloseBrace. + bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) { + unsigned Depth = 1; + P = C; + while (P < End) { + StringRef S(P, End - P); + if (S.startswith(OpenBrace)) { + ++Depth; + P += OpenBrace.size(); + } else if (S.startswith(CloseBrace)) { + --Depth; + if (Depth == 0) { + PEnd = P + CloseBrace.size(); + return true; + } + P += CloseBrace.size(); + } else { + ++P; + } + } + return false; + } + // Advance 1-past previous next/search. // Behavior is undefined if previous next/search failed. bool Advance() { @@ -437,7 +461,7 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, const char* const ContentBegin = PH.C; // mark content begin // Search for token: }} - if (!PH.Search("}}")) { + if (!PH.SearchClosingBrace("{{", "}}")) { Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), diag::err_verify_missing_end) << KindStr; continue; @@ -459,6 +483,13 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, if (Text.empty()) Text.assign(ContentBegin, ContentEnd); + // Check that regex directives contain at least one regex. + if (RegexKind && Text.find("{{") == StringRef::npos) { + Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), + diag::err_verify_missing_regex) << Text; + return false; + } + // Construct new directive. Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text, Min, Max); @@ -820,10 +851,64 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() { ED.Notes.clear(); } +// Add the characters from FixedStr to RegexStr, escaping as needed. This +// avoids the need for backslash-escaping in common patterns. +static void AddFixedStringToRegEx(StringRef FixedStr, std::string &RegexStr) { + // FIXME: Expose FileCheck.cpp's Pattern::AddFixedStringToRegEx as a utility + // method in RegEx. + + for (unsigned i = 0, e = FixedStr.size(); i != e; ++i) { + switch (FixedStr[i]) { + // These are the special characters matched in "p_ere_exp". + case '(': + case ')': + case '^': + case '$': + case '|': + case '*': + case '+': + case '?': + case '.': + case '[': + case '\\': + case '{': + RegexStr += '\\'; + // FALL THROUGH. + default: + RegexStr += FixedStr[i]; + break; + } + } +} + Directive *Directive::create(bool RegexKind, SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, StringRef Text, unsigned Min, unsigned Max) { - if (RegexKind) - return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max); - return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max); + if (!RegexKind) + return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max); + + // Parse the directive into a regular expression. + std::string RegexStr; + StringRef S = Text; + while (!S.empty()) { + if (S.startswith("{{")) { + S = S.drop_front(2); + size_t RegexMatchLength = S.find("}}"); + assert(RegexMatchLength != StringRef::npos); + // Append the regex, enclosed in parentheses. + RegexStr += "("; + RegexStr.append(S.data(), RegexMatchLength); + RegexStr += ")"; + S = S.drop_front(RegexMatchLength + 2); + } else { + size_t VerbatimMatchLength = S.find("{{"); + if (VerbatimMatchLength == StringRef::npos) + VerbatimMatchLength = S.size(); + // Escape and append the fixed string. + AddFixedStringToRegEx(S.substr(0, VerbatimMatchLength), RegexStr); + S = S.drop_front(VerbatimMatchLength); + } + } + + return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max, RegexStr); } |