X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=utils%2FFileCheck%2FFileCheck.cpp;h=f73a0fee76c8264eb76188c529d04ea892f9ba07;hb=0dbeff1e733db465a10bcd34479515b9a4bdeade;hp=a1f4be9cf72231394cac33254b4a54f417be4594;hpb=f4ccd110750a3f3fe6a107d5c74c649c2a0dc407;p=oota-llvm.git diff --git a/utils/FileCheck/FileCheck.cpp b/utils/FileCheck/FileCheck.cpp index a1f4be9cf72..f73a0fee76c 100644 --- a/utils/FileCheck/FileCheck.cpp +++ b/utils/FileCheck/FileCheck.cpp @@ -27,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; @@ -50,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; //===----------------------------------------------------------------------===// @@ -61,6 +73,7 @@ namespace Check { CheckNone = 0, CheckPlain, CheckNext, + CheckSame, CheckNot, CheckDAG, CheckLabel, @@ -386,15 +399,15 @@ size_t Pattern::Match(StringRef Buffer, size_t &MatchLen, TmpStr = RegExStr; unsigned InsertOffset = 0; - for (unsigned i = 0, e = VariableUses.size(); i != e; ++i) { + for (const auto &VariableUse : VariableUses) { std::string Value; - if (VariableUses[i].first[0] == '@') { - if (!EvaluateExpression(VariableUses[i].first, Value)) + if (VariableUse.first[0] == '@') { + if (!EvaluateExpression(VariableUse.first, Value)) return StringRef::npos; } else { StringMap::iterator it = - VariableTable.find(VariableUses[i].first); + VariableTable.find(VariableUse.first); // If the variable is undefined, return an error. if (it == VariableTable.end()) return StringRef::npos; @@ -404,7 +417,7 @@ size_t Pattern::Match(StringRef Buffer, size_t &MatchLen, } // Plop it into the regex at the adjusted offset. - TmpStr.insert(TmpStr.begin()+VariableUses[i].second+InsertOffset, + TmpStr.insert(TmpStr.begin() + VariableUse.second + InsertOffset, Value.begin(), Value.end()); InsertOffset += Value.size(); } @@ -423,11 +436,9 @@ size_t Pattern::Match(StringRef Buffer, size_t &MatchLen, StringRef FullMatch = MatchInfo[0]; // If this defines any variables, remember their values. - for (std::map::const_iterator I = VariableDefs.begin(), - E = VariableDefs.end(); - I != E; ++I) { - assert(I->second < MatchInfo.size() && "Internal paren error"); - VariableTable[I->first] = MatchInfo[I->second]; + for (const auto &VariableDef : VariableDefs) { + assert(VariableDef.second < MatchInfo.size() && "Internal paren error"); + VariableTable[VariableDef.first] = MatchInfo[VariableDef.second]; } MatchLen = FullMatch.size(); @@ -457,10 +468,10 @@ void Pattern::PrintFailureInfo(const SourceMgr &SM, StringRef Buffer, // If this was a regular expression using variables, print the current // variable values. if (!VariableUses.empty()) { - for (unsigned i = 0, e = VariableUses.size(); i != e; ++i) { + for (const auto &VariableUse : VariableUses) { SmallString<256> Msg; raw_svector_ostream OS(Msg); - StringRef Var = VariableUses[i].first; + StringRef Var = VariableUse.first; if (Var[0] == '@') { std::string Value; if (EvaluateExpression(Var, Value)) { @@ -608,6 +619,9 @@ struct CheckString { /// CheckNext - Verify there is a single line in the given buffer. bool CheckNext(const SourceMgr &SM, StringRef Buffer) const; + /// CheckSame - Verify there is no newline in the given buffer. + bool CheckSame(const SourceMgr &SM, StringRef Buffer) const; + /// CheckNot - Verify there's no "not strings" in the given buffer. bool CheckNot(const SourceMgr &SM, StringRef Buffer, const std::vector &NotStrings, @@ -624,8 +638,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()); @@ -650,12 +665,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) { @@ -674,6 +685,9 @@ static size_t CheckTypeSize(Check::CheckType Ty) { case Check::CheckNext: return sizeof("-NEXT:") - 1; + case Check::CheckSame: + return sizeof("-SAME:") - 1; + case Check::CheckNot: return sizeof("-NOT:") - 1; @@ -704,6 +718,9 @@ static Check::CheckType FindCheckType(StringRef Buffer, StringRef Prefix) { if (Rest.startswith("NEXT:")) return Check::CheckNext; + if (Rest.startswith("SAME:")) + return Check::CheckSame; + if (Rest.startswith("NOT:")) return Check::CheckNot; @@ -742,9 +759,7 @@ static StringRef FindFirstCandidateMatch(StringRef &Buffer, CheckTy = Check::CheckNone; CheckLoc = StringRef::npos; - for (prefix_iterator I = CheckPrefixes.begin(), E = CheckPrefixes.end(); - I != E; ++I) { - StringRef Prefix(*I); + for (StringRef Prefix : CheckPrefixes) { size_t PrefixLoc = Buffer.find(Prefix); if (PrefixLoc == StringRef::npos) @@ -820,24 +835,44 @@ static StringRef FindFirstMatchingPrefix(StringRef &Buffer, /// Returns true in case of an error, false otherwise. static bool ReadCheckFile(SourceMgr &SM, std::vector &CheckStrings) { - std::unique_ptr 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.release(), 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 = (Twine("-") + ImplicitCheckNot.ArgStr + "='").str(); + 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. @@ -890,10 +925,12 @@ static bool ReadCheckFile(SourceMgr &SM, Buffer = Buffer.substr(EOL); // Verify that CHECK-NEXT lines have at least one CHECK line before them. - if ((CheckTy == Check::CheckNext) && CheckStrings.empty()) { + if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckSame) && + CheckStrings.empty()) { + StringRef Type = CheckTy == Check::CheckNext ? "NEXT" : "SAME"; SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart), SourceMgr::DK_Error, - "found '" + UsedPrefix + "-NEXT:' without previous '" + "found '" + UsedPrefix + "-" + Type + "' without previous '" + UsedPrefix + ": line"); return true; } @@ -905,32 +942,31 @@ static bool ReadCheckFile(SourceMgr &SM, } // Okay, add the string we captured to the output vector and move on. - CheckStrings.push_back(CheckString(P, - UsedPrefix, - PatternLoc, - CheckTy)); + CheckStrings.emplace_back(P, UsedPrefix, PatternLoc, CheckTy); std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); + DagNotMatches = ImplicitNegativeChecks; } // Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first // prefix as a filler for the error message. if (!DagNotMatches.empty()) { - CheckStrings.push_back(CheckString(Pattern(Check::CheckEOF), - CheckPrefixes[0], - SMLoc::getFromPointer(Buffer.data()), - Check::CheckEOF)); + CheckStrings.emplace_back(Pattern(Check::CheckEOF), *CheckPrefixes.begin(), + SMLoc::getFromPointer(Buffer.data()), + Check::CheckEOF); std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); } if (CheckStrings.empty()) { errs() << "error: no check strings found with prefix" << (CheckPrefixes.size() > 1 ? "es " : " "); - for (size_t I = 0, N = CheckPrefixes.size(); I != N; ++I) { - StringRef Prefix(CheckPrefixes[I]); - errs() << '\'' << Prefix << ":'"; - if (I != N - 1) - errs() << ", "; + prefix_iterator I = CheckPrefixes.begin(); + prefix_iterator E = CheckPrefixes.end(); + if (I != E) { + errs() << "\'" << *I << ":'"; + ++I; } + for (; I != E; ++I) + errs() << ", \'" << *I << ":'"; errs() << '\n'; return true; @@ -939,7 +975,7 @@ static bool ReadCheckFile(SourceMgr &SM, return false; } -static void PrintCheckFailed(const SourceMgr &SM, const SMLoc &Loc, +static void PrintCheckFailed(const SourceMgr &SM, SMLoc Loc, const Pattern &Pat, StringRef Buffer, StringMap &VariableTable) { // Otherwise, we have an error, emit an error message. @@ -965,7 +1001,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. @@ -980,6 +1017,9 @@ static unsigned CountNumNewlinesBetween(StringRef Range) { (Range[0] != Range[1])) Range = Range.substr(1); Range = Range.substr(1); + + if (NumNewLines == 1) + FirstNewLine = Range.begin(); } } @@ -1007,7 +1047,6 @@ size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer, PrintCheckFailed(SM, *this, MatchBuffer, VariableTable); return StringRef::npos; } - MatchPos += LastPos; // Similar to the above, in "label-scan mode" we can't yet handle CHECK-NEXT // or CHECK-NOT @@ -1019,13 +1058,18 @@ size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer, if (CheckNext(SM, SkippedRegion)) return StringRef::npos; + // If this check is a "CHECK-SAME", verify that the previous match was on + // the same line (i.e. that there is no newline between them). + if (CheckSame(SM, SkippedRegion)) + return StringRef::npos; + // If this match had "not strings", verify that they don't exist in the // skipped region. if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable)) return StringRef::npos; } - return MatchPos; + return LastPos + MatchPos; } bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const { @@ -1039,7 +1083,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 + @@ -1058,6 +1103,36 @@ 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; + } + + return false; +} + +bool CheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const { + if (CheckTy != Check::CheckSame) + return false; + + // Count the number of newlines between the previous match and this one. + assert(Buffer.data() != + SM.getMemoryBuffer(SM.FindBufferContainingLoc( + SMLoc::getFromPointer(Buffer.data()))) + ->getBufferStart() && + "CHECK-SAME can't be the first check in a file"); + + const char *FirstNewLine = nullptr; + unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine); + + if (NumNewLines != 0) { + SM.PrintMessage(Loc, SourceMgr::DK_Error, + Prefix + + "-SAME: is not on the same line as the previous match"); + SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note, + "'next' match was here"); + SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note, + "previous match ended here"); return true; } @@ -1067,9 +1142,7 @@ bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const { bool CheckString::CheckNot(const SourceMgr &SM, StringRef Buffer, const std::vector &NotStrings, StringMap &VariableTable) const { - for (unsigned ChunkNo = 0, e = NotStrings.size(); - ChunkNo != e; ++ChunkNo) { - const Pattern *Pat = NotStrings[ChunkNo]; + for (const Pattern *Pat : NotStrings) { assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!"); size_t MatchLen = 0; @@ -1097,10 +1170,7 @@ size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, size_t LastPos = 0; size_t StartPos = LastPos; - for (unsigned ChunkNo = 0, e = DagNotStrings.size(); - ChunkNo != e; ++ChunkNo) { - const Pattern &Pat = DagNotStrings[ChunkNo]; - + for (const Pattern &Pat : DagNotStrings) { assert((Pat.getCheckTy() == Check::CheckDAG || Pat.getCheckTy() == Check::CheckNot) && "Invalid CHECK-DAG or CHECK-NOT!"); @@ -1174,11 +1244,12 @@ static bool ValidateCheckPrefix(StringRef CheckPrefix) { static bool ValidateCheckPrefixes() { StringSet<> PrefixSet; - for (prefix_iterator I = CheckPrefixes.begin(), E = CheckPrefixes.end(); - I != E; ++I) { - StringRef Prefix(*I); + for (StringRef Prefix : CheckPrefixes) { + // Reject empty prefixes. + if (Prefix == "") + return false; - if (!PrefixSet.insert(Prefix)) + if (!PrefixSet.insert(Prefix).second) return false; if (!ValidateCheckPrefix(Prefix)) @@ -1217,33 +1288,34 @@ int main(int argc, char **argv) { return 2; // Open the file to check and add it to SourceMgr. - std::unique_ptr 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.release(), 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();