FileCheck.cpp revision 207e1bcf897d1c732f717b9773029651ecc59ab2
1//===- FileCheck.cpp - Check that File's Contents match what is expected --===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// FileCheck does a line-by line check of a file that validates whether it 11// contains the expected content. This is useful for regression tests etc. 12// 13// This program exits with an error status of 2 on error, exit status of 0 if 14// the file matched the expected contents, and exit status of 1 if it did not 15// contain the expected contents. 16// 17//===----------------------------------------------------------------------===// 18 19#include "llvm/Support/CommandLine.h" 20#include "llvm/Support/MemoryBuffer.h" 21#include "llvm/Support/PrettyStackTrace.h" 22#include "llvm/Support/SourceMgr.h" 23#include "llvm/Support/raw_ostream.h" 24#include "llvm/System/Signals.h" 25using namespace llvm; 26 27static cl::opt<std::string> 28CheckFilename(cl::Positional, cl::desc("<check-file>"), cl::Required); 29 30static cl::opt<std::string> 31InputFilename("input-file", cl::desc("File to check (defaults to stdin)"), 32 cl::init("-"), cl::value_desc("filename")); 33 34static cl::opt<std::string> 35CheckPrefix("check-prefix", cl::init("CHECK"), 36 cl::desc("Prefix to use from check file (defaults to 'CHECK')")); 37 38static cl::opt<bool> 39NoCanonicalizeWhiteSpace("strict-whitespace", 40 cl::desc("Do not treat all horizontal whitespace as equivalent")); 41 42/// CheckString - This is a check that we found in the input file. 43struct CheckString { 44 /// Str - The string to match. 45 std::string Str; 46 47 /// Loc - The location in the match file that the check string was specified. 48 SMLoc Loc; 49 50 CheckString(const std::string &S, SMLoc L) : Str(S), Loc(L) {} 51}; 52 53 54/// FindStringInBuffer - This is basically just a strstr wrapper that differs in 55/// two ways: first it handles 'nul' characters in memory buffers, second, it 56/// returns the end of the memory buffer on match failure. 57static const char *FindStringInBuffer(const char *Str, const char *CurPtr, 58 const MemoryBuffer &MB) { 59 // Check to see if we have a match. If so, just return it. 60 if (const char *Res = strstr(CurPtr, Str)) 61 return Res; 62 63 // If not, check to make sure we didn't just find an embedded nul in the 64 // memory buffer. 65 const char *Ptr = CurPtr + strlen(CurPtr); 66 67 // If we really reached the end of the file, return it. 68 if (Ptr == MB.getBufferEnd()) 69 return Ptr; 70 71 // Otherwise, just skip this section of the file, including the nul. 72 return FindStringInBuffer(Str, Ptr+1, MB); 73} 74 75/// ReadCheckFile - Read the check file, which specifies the sequence of 76/// expected strings. The strings are added to the CheckStrings vector. 77static bool ReadCheckFile(SourceMgr &SM, 78 std::vector<CheckString> &CheckStrings) { 79 // Open the check file, and tell SourceMgr about it. 80 std::string ErrorStr; 81 MemoryBuffer *F = 82 MemoryBuffer::getFileOrSTDIN(CheckFilename.c_str(), &ErrorStr); 83 if (F == 0) { 84 errs() << "Could not open check file '" << CheckFilename << "': " 85 << ErrorStr << '\n'; 86 return true; 87 } 88 SM.AddNewSourceBuffer(F, SMLoc()); 89 90 // Find all instances of CheckPrefix followed by : in the file. The 91 // MemoryBuffer is guaranteed to be nul terminated, but may have nul's 92 // embedded into it. We don't support check strings with embedded nuls. 93 std::string Prefix = CheckPrefix + ":"; 94 const char *CurPtr = F->getBufferStart(), *BufferEnd = F->getBufferEnd(); 95 96 while (1) { 97 // See if Prefix occurs in the memory buffer. 98 const char *Ptr = FindStringInBuffer(Prefix.c_str(), CurPtr, *F); 99 100 // If we didn't find a match, we're done. 101 if (Ptr == BufferEnd) 102 break; 103 104 // Okay, we found the prefix, yay. Remember the rest of the line, but 105 // ignore leading and trailing whitespace. 106 Ptr += Prefix.size(); 107 while (*Ptr == ' ' || *Ptr == '\t') 108 ++Ptr; 109 110 // Scan ahead to the end of line. 111 CurPtr = Ptr; 112 while (CurPtr != BufferEnd && *CurPtr != '\n' && *CurPtr != '\r') 113 ++CurPtr; 114 115 // Ignore trailing whitespace. 116 while (CurPtr[-1] == ' ' || CurPtr[-1] == '\t') 117 --CurPtr; 118 119 // Check that there is something on the line. 120 if (Ptr >= CurPtr) { 121 SM.PrintMessage(SMLoc::getFromPointer(CurPtr), 122 "found empty check string with prefix '"+Prefix+"'", 123 "error"); 124 return true; 125 } 126 127 // Okay, add the string we captured to the output vector and move on. 128 CheckStrings.push_back(CheckString(std::string(Ptr, CurPtr), 129 SMLoc::getFromPointer(Ptr))); 130 } 131 132 if (CheckStrings.empty()) { 133 errs() << "error: no check strings found with prefix '" << Prefix << "'\n"; 134 return true; 135 } 136 137 return false; 138} 139 140// CanonicalizeCheckStrings - Replace all sequences of horizontal whitespace in 141// the check strings with a single space. 142static void CanonicalizeCheckStrings(std::vector<CheckString> &CheckStrings) { 143 for (unsigned i = 0, e = CheckStrings.size(); i != e; ++i) { 144 std::string &Str = CheckStrings[i].Str; 145 146 for (unsigned C = 0; C != Str.size(); ++C) { 147 // If C is not a horizontal whitespace, skip it. 148 if (Str[C] != ' ' && Str[C] != '\t') 149 continue; 150 151 // Replace the character with space, then remove any other space 152 // characters after it. 153 Str[C] = ' '; 154 155 while (C+1 != Str.size() && 156 (Str[C+1] == ' ' || Str[C+1] == '\t')) 157 Str.erase(Str.begin()+C+1); 158 } 159 } 160} 161 162/// CanonicalizeInputFile - Remove duplicate horizontal space from the specified 163/// memory buffer, free it, and return a new one. 164static MemoryBuffer *CanonicalizeInputFile(MemoryBuffer *MB) { 165 SmallVector<char, 16> NewFile; 166 NewFile.reserve(MB->getBufferSize()); 167 168 for (const char *Ptr = MB->getBufferStart(), *End = MB->getBufferEnd(); 169 Ptr != End; ++Ptr) { 170 // If C is not a horizontal whitespace, skip it. 171 if (*Ptr != ' ' && *Ptr != '\t') { 172 NewFile.push_back(*Ptr); 173 continue; 174 } 175 176 // Otherwise, add one space and advance over neighboring space. 177 NewFile.push_back(' '); 178 while (Ptr+1 != End && 179 (Ptr[1] == ' ' || Ptr[1] == '\t')) 180 ++Ptr; 181 } 182 183 // Free the old buffer and return a new one. 184 MemoryBuffer *MB2 = 185 MemoryBuffer::getMemBufferCopy(NewFile.data(), 186 NewFile.data() + NewFile.size(), 187 MB->getBufferIdentifier()); 188 189 delete MB; 190 return MB2; 191} 192 193 194int main(int argc, char **argv) { 195 sys::PrintStackTraceOnErrorSignal(); 196 PrettyStackTraceProgram X(argc, argv); 197 cl::ParseCommandLineOptions(argc, argv); 198 199 SourceMgr SM; 200 201 // Read the expected strings from the check file. 202 std::vector<CheckString> CheckStrings; 203 if (ReadCheckFile(SM, CheckStrings)) 204 return 2; 205 206 // Remove duplicate spaces in the check strings if requested. 207 if (!NoCanonicalizeWhiteSpace) 208 CanonicalizeCheckStrings(CheckStrings); 209 210 // Open the file to check and add it to SourceMgr. 211 std::string ErrorStr; 212 MemoryBuffer *F = 213 MemoryBuffer::getFileOrSTDIN(InputFilename.c_str(), &ErrorStr); 214 if (F == 0) { 215 errs() << "Could not open input file '" << InputFilename << "': " 216 << ErrorStr << '\n'; 217 return true; 218 } 219 220 // Remove duplicate spaces in the input file if requested. 221 if (!NoCanonicalizeWhiteSpace) 222 F = CanonicalizeInputFile(F); 223 224 SM.AddNewSourceBuffer(F, SMLoc()); 225 226 // Check that we have all of the expected strings, in order, in the input 227 // file. 228 const char *CurPtr = F->getBufferStart(), *BufferEnd = F->getBufferEnd(); 229 230 for (unsigned StrNo = 0, e = CheckStrings.size(); StrNo != e; ++StrNo) { 231 const CheckString &CheckStr = CheckStrings[StrNo]; 232 233 // Find StrNo in the file. 234 const char *Ptr = FindStringInBuffer(CheckStr.Str.c_str(), CurPtr, *F); 235 236 // If we found a match, we're done, move on. 237 if (Ptr != BufferEnd) { 238 CurPtr = Ptr + CheckStr.Str.size(); 239 continue; 240 } 241 242 // Otherwise, we have an error, emit an error message. 243 SM.PrintMessage(CheckStr.Loc, "expected string not found in input", 244 "error"); 245 246 // Print the "scanning from here" line. If the current position is at the 247 // end of a line, advance to the start of the next line. 248 const char *Scan = CurPtr; 249 while (Scan != BufferEnd && 250 (*Scan == ' ' || *Scan == '\t')) 251 ++Scan; 252 if (*Scan == '\n' || *Scan == '\r') 253 CurPtr = Scan+1; 254 255 256 SM.PrintMessage(SMLoc::getFromPointer(CurPtr), "scanning from here", 257 "note"); 258 return 1; 259 } 260 261 return 0; 262} 263