X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=utils%2FFileCheck%2FFileCheck.cpp;h=9606e64d6f8937482b60a18650e74a6f885ac5b6;hb=12af22e8cc217827cf4f118b0f5e4ebbda9925ae;hp=c9eb8a650c809120be67d68dc8b0d8c3d1953a10;hpb=ee4f5eae1c416eddbecbb2d742e2bb8dc0032bf6;p=oota-llvm.git diff --git a/utils/FileCheck/FileCheck.cpp b/utils/FileCheck/FileCheck.cpp index c9eb8a650c8..9606e64d6f8 100644 --- a/utils/FileCheck/FileCheck.cpp +++ b/utils/FileCheck/FileCheck.cpp @@ -16,7 +16,6 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" @@ -28,11 +27,11 @@ #include "llvm/Support/Signals.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Support/system_error.h" #include #include #include #include +#include #include using namespace llvm; @@ -51,6 +50,18 @@ static cl::opt NoCanonicalizeWhiteSpace("strict-whitespace", cl::desc("Do not treat all horizontal whitespace as equivalent")); +static cl::list ImplicitCheckNot( + "implicit-check-not", + cl::desc("Add an implicit negative check with this pattern to every\n" + "positive check. This can be used to ensure that no instances of\n" + "this pattern occur which are not matched by a positive pattern"), + cl::value_desc("pattern")); + +static cl::opt AllowEmptyInput( + "allow-empty", cl::init(false), + cl::desc("Allow the input file to be empty. This is useful when making\n" + "checks that some error message does not occur, for example.")); + typedef cl::list::const_iterator prefix_iterator; //===----------------------------------------------------------------------===// @@ -136,7 +147,6 @@ public: Check::CheckType getCheckTy() const { return CheckTy; } private: - static void AddFixedStringToRegEx(StringRef FixedStr, std::string &TheStr); bool AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM); void AddBackrefToRegEx(unsigned BackrefNum); @@ -155,7 +165,7 @@ private: /// (right after the opening sequence). /// \return offset of the closing sequence within Str, or npos if it was not /// found. - size_t FindRegexVarEnd(StringRef Str); + size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM); }; @@ -228,7 +238,7 @@ bool Pattern::ParsePattern(StringRef PatternStr, if (PatternStr.startswith("[[")) { // Find the closing bracket pair ending the match. End is going to be an // offset relative to the beginning of the match string. - size_t End = FindRegexVarEnd(PatternStr.substr(2)); + size_t End = FindRegexVarEnd(PatternStr.substr(2), SM); if (End == StringRef::npos) { SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()), @@ -314,40 +324,13 @@ bool Pattern::ParsePattern(StringRef PatternStr, // Find the end, which is the start of the next regex. size_t FixedMatchEnd = PatternStr.find("{{"); FixedMatchEnd = std::min(FixedMatchEnd, PatternStr.find("[[")); - AddFixedStringToRegEx(PatternStr.substr(0, FixedMatchEnd), RegExStr); + RegExStr += Regex::escape(PatternStr.substr(0, FixedMatchEnd)); PatternStr = PatternStr.substr(FixedMatchEnd); } return false; } -void Pattern::AddFixedStringToRegEx(StringRef FixedStr, std::string &TheStr) { - // Add the characters from FixedStr to the regex, escaping as needed. This - // avoids "leaning toothpicks" in common patterns. - 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 '{': - TheStr += '\\'; - // FALL THROUGH. - default: - TheStr += FixedStr[i]; - break; - } - } -} - bool Pattern::AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM) { Regex R(RS); @@ -428,8 +411,8 @@ size_t Pattern::Match(StringRef Buffer, size_t &MatchLen, if (it == VariableTable.end()) return StringRef::npos; - // Look up the value and escape it so that we can plop it into the regex. - AddFixedStringToRegEx(it->second, Value); + // Look up the value and escape it so that we can put it into the regex. + Value += Regex::escape(it->second); } // Plop it into the regex at the adjusted offset. @@ -560,7 +543,7 @@ void Pattern::PrintFailureInfo(const SourceMgr &SM, StringRef Buffer, } } -size_t Pattern::FindRegexVarEnd(StringRef Str) { +size_t Pattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) { // Offset keeps track of the current offset within the input Str size_t Offset = 0; // [...] Nesting depth @@ -581,7 +564,12 @@ size_t Pattern::FindRegexVarEnd(StringRef Str) { BracketDepth++; break; case ']': - assert(BracketDepth > 0 && "Invalid regex"); + if (BracketDepth == 0) { + SM.PrintMessage(SMLoc::getFromPointer(Str.data()), + SourceMgr::DK_Error, + "missing closing \"]\" for regex variable"); + exit(1); + } BracketDepth--; break; } @@ -648,8 +636,9 @@ struct CheckString { /// /// \param PreserveHorizontal Don't squash consecutive horizontal whitespace /// characters to a single space. -static MemoryBuffer *CanonicalizeInputFile(MemoryBuffer *MB, - bool PreserveHorizontal) { +static std::unique_ptr +CanonicalizeInputFile(std::unique_ptr MB, + bool PreserveHorizontal) { SmallString<128> NewFile; NewFile.reserve(MB->getBufferSize()); @@ -674,12 +663,8 @@ static MemoryBuffer *CanonicalizeInputFile(MemoryBuffer *MB, ++Ptr; } - // Free the old buffer and return a new one. - MemoryBuffer *MB2 = - MemoryBuffer::getMemBufferCopy(NewFile.str(), MB->getBufferIdentifier()); - - delete MB; - return MB2; + return std::unique_ptr( + MemoryBuffer::getMemBufferCopy(NewFile.str(), MB->getBufferIdentifier())); } static bool IsPartOfWord(char c) { @@ -785,30 +770,32 @@ static StringRef FindFirstCandidateMatch(StringRef &Buffer, // We only want to find the first match to avoid skipping some. if (PrefixLoc > FirstLoc) continue; + // If one matching check-prefix is a prefix of another, choose the + // longer one. + if (PrefixLoc == FirstLoc && Prefix.size() < FirstPrefix.size()) + continue; StringRef Rest = Buffer.drop_front(PrefixLoc); // Make sure we have actually found the prefix, and not a word containing // it. This should also prevent matching the wrong prefix when one is a // substring of another. if (PrefixLoc != 0 && IsPartOfWord(Buffer[PrefixLoc - 1])) - continue; - - Check::CheckType Ty = FindCheckType(Rest, Prefix); - if (Ty == Check::CheckNone) - continue; + FirstTy = Check::CheckNone; + else + FirstTy = FindCheckType(Rest, Prefix); FirstLoc = PrefixLoc; - FirstTy = Ty; FirstPrefix = Prefix; } - if (FirstPrefix.empty()) { + // If the first prefix is invalid, we should continue the search after it. + if (FirstTy == Check::CheckNone) { CheckLoc = SearchLoc; - } else { - CheckTy = FirstTy; - CheckLoc = FirstLoc; + return ""; } + CheckTy = FirstTy; + CheckLoc = FirstLoc; return FirstPrefix; } @@ -842,24 +829,44 @@ static StringRef FindFirstMatchingPrefix(StringRef &Buffer, /// Returns true in case of an error, false otherwise. static bool ReadCheckFile(SourceMgr &SM, std::vector &CheckStrings) { - OwningPtr File; - if (error_code ec = - MemoryBuffer::getFileOrSTDIN(CheckFilename, File)) { - errs() << "Could not open check file '" << CheckFilename << "': " - << ec.message() << '\n'; + ErrorOr> FileOrErr = + MemoryBuffer::getFileOrSTDIN(CheckFilename); + if (std::error_code EC = FileOrErr.getError()) { + errs() << "Could not open check file '" << CheckFilename + << "': " << EC.message() << '\n'; return true; } // If we want to canonicalize whitespace, strip excess whitespace from the // buffer containing the CHECK lines. Remove DOS style line endings. - MemoryBuffer *F = - CanonicalizeInputFile(File.take(), NoCanonicalizeWhiteSpace); - - SM.AddNewSourceBuffer(F, SMLoc()); + std::unique_ptr F = CanonicalizeInputFile( + std::move(FileOrErr.get()), NoCanonicalizeWhiteSpace); // Find all instances of CheckPrefix followed by : in the file. StringRef Buffer = F->getBuffer(); - std::vector DagNotMatches; + + SM.AddNewSourceBuffer(std::move(F), SMLoc()); + + std::vector ImplicitNegativeChecks; + for (const auto &PatternString : ImplicitCheckNot) { + // Create a buffer with fake command line content in order to display the + // command line option responsible for the specific implicit CHECK-NOT. + std::string Prefix = std::string("-") + ImplicitCheckNot.ArgStr + "='"; + std::string Suffix = "'"; + std::unique_ptr CmdLine = MemoryBuffer::getMemBufferCopy( + Prefix + PatternString + Suffix, "command line"); + + StringRef PatternInBuffer = + CmdLine->getBuffer().substr(Prefix.size(), PatternString.size()); + SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); + + ImplicitNegativeChecks.push_back(Pattern(Check::CheckNot)); + ImplicitNegativeChecks.back().ParsePattern(PatternInBuffer, + "IMPLICIT-CHECK", SM, 0); + } + + + std::vector DagNotMatches = ImplicitNegativeChecks; // LineNumber keeps track of the line on which CheckPrefix instances are // found. @@ -932,6 +939,7 @@ static bool ReadCheckFile(SourceMgr &SM, PatternLoc, CheckTy)); std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); + DagNotMatches = ImplicitNegativeChecks; } // Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first @@ -987,7 +995,8 @@ static void PrintCheckFailed(const SourceMgr &SM, const CheckString &CheckStr, /// CountNumNewlinesBetween - Count the number of newlines in the specified /// range. -static unsigned CountNumNewlinesBetween(StringRef Range) { +static unsigned CountNumNewlinesBetween(StringRef Range, + const char *&FirstNewLine) { unsigned NumNewLines = 0; while (1) { // Scan for newline. @@ -1002,6 +1011,9 @@ static unsigned CountNumNewlinesBetween(StringRef Range) { (Range[0] != Range[1])) Range = Range.substr(1); Range = Range.substr(1); + + if (NumNewLines == 1) + FirstNewLine = Range.begin(); } } @@ -1061,7 +1073,8 @@ bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const { SMLoc::getFromPointer(Buffer.data())))->getBufferStart() && "CHECK-NEXT can't be the first check in a file"); - unsigned NumNewLines = CountNumNewlinesBetween(Buffer); + const char *FirstNewLine = nullptr; + unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine); if (NumNewLines == 0) { SM.PrintMessage(Loc, SourceMgr::DK_Error, Prefix + @@ -1080,6 +1093,8 @@ bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const { SourceMgr::DK_Note, "'next' match was here"); SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note, "previous match ended here"); + SM.PrintMessage(SMLoc::getFromPointer(FirstNewLine), SourceMgr::DK_Note, + "non-matching line after previous match is here"); return true; } @@ -1200,6 +1215,10 @@ static bool ValidateCheckPrefixes() { I != E; ++I) { StringRef Prefix(*I); + // Reject empty prefixes. + if (Prefix == "") + return false; + if (!PrefixSet.insert(Prefix)) return false; @@ -1239,33 +1258,34 @@ int main(int argc, char **argv) { return 2; // Open the file to check and add it to SourceMgr. - OwningPtr File; - if (error_code ec = - MemoryBuffer::getFileOrSTDIN(InputFilename, File)) { - errs() << "Could not open input file '" << InputFilename << "': " - << ec.message() << '\n'; + ErrorOr> FileOrErr = + MemoryBuffer::getFileOrSTDIN(InputFilename); + if (std::error_code EC = FileOrErr.getError()) { + errs() << "Could not open input file '" << InputFilename + << "': " << EC.message() << '\n'; return 2; } + std::unique_ptr &File = FileOrErr.get(); - if (File->getBufferSize() == 0) { + if (File->getBufferSize() == 0 && !AllowEmptyInput) { errs() << "FileCheck error: '" << InputFilename << "' is empty.\n"; return 2; } // Remove duplicate spaces in the input file if requested. // Remove DOS style line endings. - MemoryBuffer *F = - CanonicalizeInputFile(File.take(), NoCanonicalizeWhiteSpace); - - SM.AddNewSourceBuffer(F, SMLoc()); - - /// VariableTable - This holds all the current filecheck variables. - StringMap VariableTable; + std::unique_ptr F = + CanonicalizeInputFile(std::move(File), NoCanonicalizeWhiteSpace); // Check that we have all of the expected strings, in order, in the input // file. StringRef Buffer = F->getBuffer(); + SM.AddNewSourceBuffer(std::move(F), SMLoc()); + + /// VariableTable - This holds all the current filecheck variables. + StringMap VariableTable; + bool hasError = false; unsigned i = 0, j = 0, e = CheckStrings.size();