Index: docs/CommandGuide/FileCheck.rst =================================================================== --- docs/CommandGuide/FileCheck.rst +++ docs/CommandGuide/FileCheck.rst @@ -378,6 +378,17 @@ ``CHECK-LABEL:`` directives cannot contain variable definitions or uses. +The "CHECK-LABEL:" directive +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some checks may be used several times. Then these checks may be described in +separate file. Then this file can be included by directive // CHECK-INCLUDE: filename. +To add directory where included files would be searching in, user should use –I option. + +.. code-block:: llvm + // RUN: FileCheck -I=$LLVM_SRC_ROOT/test -input-file %s %s + + FileCheck Pattern Matching Syntax ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Index: test/FileCheck/check-include.txt =================================================================== --- /dev/null +++ test/FileCheck/check-include.txt @@ -0,0 +1,22 @@ +// RUN: FileCheck -I=$LLVM_SRC_ROOT/test -input-file %s %s + +op1; +op2; +op33; + +first and +This is second line + +one two three + +four! +dog +line +op0xA1; +op0xc2; +op0x33; +reg0x0 +// CHECK-INCLUDE: FileCheck/FileCheckImprovement/included.txt +ADD r0xA, reg0x10 +// CHECK: {{ADD r0xA, reg0x10}} + Index: test/FileCheck/included.txt =================================================================== --- /dev/null +++ test/FileCheck/included.txt @@ -0,0 +1,12 @@ +// CHECK: op{{[0-9]}}; +// CHECK-NEXT: op{{[0-9]}}; +// CHECK-NOT: op1{{[0-9]}} +// CHECK: first +// CHECK-SAME: and +// CHECK: second +// CHECK-DAG: two +// CHECK-DAG: three +// CHECK-DAG: one +// CHECK: four +// CHECK-NOT: cat +// CHECK: line Index: utils/FileCheck/FileCheck.cpp =================================================================== --- utils/FileCheck/FileCheck.cpp +++ utils/FileCheck/FileCheck.cpp @@ -22,6 +22,7 @@ #include "llvm/ADT/StringSet.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Regex.h" #include "llvm/Support/Signals.h" @@ -33,6 +34,9 @@ #include #include #include + +#define MAX_INCLUDING_DEPTH 20 // maximum depth for including files. + using namespace llvm; static cl::opt @@ -73,6 +77,10 @@ "Allows leading and trailing whitespace if --strict-whitespace\n" "is not also passed.")); +static cl::list IncludedDirectories( + "I", + cl::desc("Add directories for searching included files in CHECK-INCLUDE")); + typedef cl::list::const_iterator prefix_iterator; //===----------------------------------------------------------------------===// @@ -88,6 +96,7 @@ CheckNot, CheckDAG, CheckLabel, + CheckInclude, /// MatchEOF - When set, this pattern only matches the end of file. This is /// used for trailing CHECK-NOTs. @@ -131,6 +140,8 @@ /// getLoc - Return the location in source code. SMLoc getLoc() const { return PatternLoc; } + StringRef getFixedString() const { return FixedStr; } + /// ParsePattern - Parse the given string into the Pattern. Prefix provides /// which prefix is being matched, SM provides the SourceMgr used for error /// reports, and LineNumber is the line number in the input file from which @@ -721,6 +732,9 @@ case Check::CheckLabel: return sizeof("-LABEL:") - 1; + case Check::CheckInclude: + return sizeof("-INCLUDE:") - 1; + case Check::CheckEOF: llvm_unreachable("Should not be using EOF size"); } @@ -754,6 +768,9 @@ if (Rest.startswith("LABEL:")) return Check::CheckLabel; + if (Rest.startswith("INCLUDE:")) + return Check::CheckInclude; + // You can't combine -NOT with another suffix. if (Rest.startswith("DAG-NOT:") || Rest.startswith("NOT-DAG:") || Rest.startswith("NEXT-NOT:") || Rest.startswith("NOT-NEXT:") || @@ -864,11 +881,13 @@ /// expected strings. The strings are added to the CheckStrings vector. /// Returns true in case of an error, false otherwise. static bool ReadCheckFile(SourceMgr &SM, - std::vector &CheckStrings) { + std::vector &CheckStrings, + std::string Filename = CheckFilename, + int IncludingLevel = 0) { ErrorOr> FileOrErr = - MemoryBuffer::getFileOrSTDIN(CheckFilename); + MemoryBuffer::getFileOrSTDIN(Filename); if (std::error_code EC = FileOrErr.getError()) { - errs() << "Could not open check file '" << CheckFilename + errs() << "Could not open check file '" << Filename << "': " << EC.message() << '\n'; return true; } @@ -951,38 +970,75 @@ if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber)) return true; - // Verify that CHECK-LABEL lines do not define or use variables - if ((CheckTy == Check::CheckLabel) && P.hasVariable()) { - SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart), - SourceMgr::DK_Error, - "found '" + UsedPrefix + "-LABEL:'" - " with variable definition or use"); - return true; - } + // if included another file + if (CheckTy == Check::CheckInclude) { + // Check for escaping infinite including + if (IncludingLevel > MAX_INCLUDING_DEPTH) { + SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), + SourceMgr::DK_Error, + "Maximum including level!"); + return true; + } + // Get included file name. + StringRef IncludedFileName = P.getFixedString(); + + std::vector IncludedCheckStrings; + // Try find file in included directories. + ErrorOr> FileOrErr = + MemoryBuffer::getFileOrSTDIN(StringRef(IncludedFileName)); + if (FileOrErr.getError()) { + for (const std::string &Directory : IncludedDirectories) { + SmallString<32> FullIncludedFileName; + FullIncludedFileName.append(Directory); + sys::path::append(FullIncludedFileName, IncludedFileName.data()); + FileOrErr = MemoryBuffer::getFileOrSTDIN(FullIncludedFileName); + if (!FileOrErr.getError()) { + IncludedFileName = FullIncludedFileName.str(); + break; + } + } + } + + // Read it and get check strings from it. + ReadCheckFile(SM, IncludedCheckStrings, IncludedFileName, + IncludingLevel + 1); + CheckStrings.insert(CheckStrings.end(), IncludedCheckStrings.begin(), + IncludedCheckStrings.end()); + } else { - Buffer = Buffer.substr(EOL); + // Verify that CHECK-LABEL lines do not define or use variables + if ((CheckTy == Check::CheckLabel) && P.hasVariable()) { + SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart), + SourceMgr::DK_Error, + "found '" + UsedPrefix + "-LABEL:'" + " with variable definition or use"); + return true; + } - // Verify that CHECK-NEXT lines have at least one CHECK line before them. - 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 + "-" + Type + "' without previous '" - + UsedPrefix + ": line"); - return true; - } + Buffer = Buffer.substr(EOL); - // Handle CHECK-DAG/-NOT. - if (CheckTy == Check::CheckDAG || CheckTy == Check::CheckNot) { - DagNotMatches.push_back(P); - continue; - } + // Verify that CHECK-NEXT lines have at least one CHECK line before them. + 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 + "-" + Type + "' without previous '" + + UsedPrefix + ": line"); + return true; + } - // Okay, add the string we captured to the output vector and move on. - CheckStrings.emplace_back(P, UsedPrefix, PatternLoc); - std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); - DagNotMatches = ImplicitNegativeChecks; + // Handle CHECK-DAG/-NOT. + if (CheckTy == Check::CheckDAG || CheckTy == Check::CheckNot) { + DagNotMatches.push_back(P); + continue; + } + + // Okay, add the string we captured to the output vector and move on. + CheckStrings.emplace_back(P, UsedPrefix, PatternLoc); + std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); + DagNotMatches = ImplicitNegativeChecks; + } } // Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first