FileCheck.cpp revision 9fc6678bea11f32acba4e9b6624ae9a0d3a9287b
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 42class Pattern { 43 /// Str - The string to match. 44 std::string Str; 45public: 46 47 Pattern(StringRef S) : Str(S.str()) { 48 // Remove duplicate spaces in the check strings if requested. 49 if (!NoCanonicalizeWhiteSpace) 50 CanonicalizeCheckString(); 51 } 52 53 /// Match - Match the pattern string against the input buffer Buffer. This 54 /// returns the position that is matched or npos if there is no match. If 55 /// there is a match, the size of the matched string is returned in MatchLen. 56 size_t Match(StringRef Buffer, size_t &MatchLen) const { 57 MatchLen = Str.size(); 58 return Buffer.find(Str); 59 } 60 61private: 62 /// CanonicalizeCheckString - Replace all sequences of horizontal whitespace 63 /// in the check strings with a single space. 64 void CanonicalizeCheckString() { 65 for (unsigned C = 0; C != Str.size(); ++C) { 66 // If C is not a horizontal whitespace, skip it. 67 if (Str[C] != ' ' && Str[C] != '\t') 68 continue; 69 70 // Replace the character with space, then remove any other space 71 // characters after it. 72 Str[C] = ' '; 73 74 while (C+1 != Str.size() && 75 (Str[C+1] == ' ' || Str[C+1] == '\t')) 76 Str.erase(Str.begin()+C+1); 77 } 78 } 79}; 80 81 82/// CheckString - This is a check that we found in the input file. 83struct CheckString { 84 /// Pat - The pattern to match. 85 Pattern Pat; 86 87 /// Loc - The location in the match file that the check string was specified. 88 SMLoc Loc; 89 90 /// IsCheckNext - This is true if this is a CHECK-NEXT: directive (as opposed 91 /// to a CHECK: directive. 92 bool IsCheckNext; 93 94 /// NotStrings - These are all of the strings that are disallowed from 95 /// occurring between this match string and the previous one (or start of 96 /// file). 97 std::vector<std::pair<SMLoc, std::string> > NotStrings; 98 99 CheckString(const Pattern &P, SMLoc L, bool isCheckNext) 100 : Pat(P), Loc(L), IsCheckNext(isCheckNext) {} 101}; 102 103 104/// ReadCheckFile - Read the check file, which specifies the sequence of 105/// expected strings. The strings are added to the CheckStrings vector. 106static bool ReadCheckFile(SourceMgr &SM, 107 std::vector<CheckString> &CheckStrings) { 108 // Open the check file, and tell SourceMgr about it. 109 std::string ErrorStr; 110 MemoryBuffer *F = 111 MemoryBuffer::getFileOrSTDIN(CheckFilename.c_str(), &ErrorStr); 112 if (F == 0) { 113 errs() << "Could not open check file '" << CheckFilename << "': " 114 << ErrorStr << '\n'; 115 return true; 116 } 117 SM.AddNewSourceBuffer(F, SMLoc()); 118 119 // Find all instances of CheckPrefix followed by : in the file. 120 StringRef Buffer = F->getBuffer(); 121 122 std::vector<std::pair<SMLoc, std::string> > NotMatches; 123 124 while (1) { 125 // See if Prefix occurs in the memory buffer. 126 Buffer = Buffer.substr(Buffer.find(CheckPrefix)); 127 128 // If we didn't find a match, we're done. 129 if (Buffer.empty()) 130 break; 131 132 const char *CheckPrefixStart = Buffer.data(); 133 134 // When we find a check prefix, keep track of whether we find CHECK: or 135 // CHECK-NEXT: 136 bool IsCheckNext = false, IsCheckNot = false; 137 138 // Verify that the : is present after the prefix. 139 if (Buffer[CheckPrefix.size()] == ':') { 140 Buffer = Buffer.substr(CheckPrefix.size()+1); 141 } else if (Buffer.size() > CheckPrefix.size()+6 && 142 memcmp(Buffer.data()+CheckPrefix.size(), "-NEXT:", 6) == 0) { 143 Buffer = Buffer.substr(CheckPrefix.size()+7); 144 IsCheckNext = true; 145 } else if (Buffer.size() > CheckPrefix.size()+5 && 146 memcmp(Buffer.data()+CheckPrefix.size(), "-NOT:", 5) == 0) { 147 Buffer = Buffer.substr(CheckPrefix.size()+6); 148 IsCheckNot = true; 149 } else { 150 Buffer = Buffer.substr(1); 151 continue; 152 } 153 154 // Okay, we found the prefix, yay. Remember the rest of the line, but 155 // ignore leading and trailing whitespace. 156 Buffer = Buffer.substr(Buffer.find_first_not_of(" \t")); 157 158 // Scan ahead to the end of line. 159 size_t EOL = Buffer.find_first_of("\n\r"); 160 if (EOL == StringRef::npos) EOL = Buffer.size(); 161 162 // Ignore trailing whitespace. 163 while (EOL && (Buffer[EOL-1] == ' ' || Buffer[EOL-1] == '\t')) 164 --EOL; 165 166 // Check that there is something on the line. 167 if (EOL == 0) { 168 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), 169 "found empty check string with prefix '"+CheckPrefix+":'", 170 "error"); 171 return true; 172 } 173 174 StringRef PatternStr = Buffer.substr(0, EOL); 175 176 // Handle CHECK-NOT. 177 if (IsCheckNot) { 178 NotMatches.push_back(std::make_pair(SMLoc::getFromPointer(Buffer.data()), 179 PatternStr.str())); 180 Buffer = Buffer.substr(EOL); 181 continue; 182 } 183 184 // Verify that CHECK-NEXT lines have at least one CHECK line before them. 185 if (IsCheckNext && CheckStrings.empty()) { 186 SM.PrintMessage(SMLoc::getFromPointer(CheckPrefixStart), 187 "found '"+CheckPrefix+"-NEXT:' without previous '"+ 188 CheckPrefix+ ": line", "error"); 189 return true; 190 } 191 192 Pattern P(PatternStr); 193 194 // Okay, add the string we captured to the output vector and move on. 195 CheckStrings.push_back(CheckString(P, 196 SMLoc::getFromPointer(Buffer.data()), 197 IsCheckNext)); 198 std::swap(NotMatches, CheckStrings.back().NotStrings); 199 200 Buffer = Buffer.substr(EOL); 201 } 202 203 if (CheckStrings.empty()) { 204 errs() << "error: no check strings found with prefix '" << CheckPrefix 205 << ":'\n"; 206 return true; 207 } 208 209 if (!NotMatches.empty()) { 210 errs() << "error: '" << CheckPrefix 211 << "-NOT:' not supported after last check line.\n"; 212 return true; 213 } 214 215 return false; 216} 217 218/// CanonicalizeInputFile - Remove duplicate horizontal space from the specified 219/// memory buffer, free it, and return a new one. 220static MemoryBuffer *CanonicalizeInputFile(MemoryBuffer *MB) { 221 SmallVector<char, 16> NewFile; 222 NewFile.reserve(MB->getBufferSize()); 223 224 for (const char *Ptr = MB->getBufferStart(), *End = MB->getBufferEnd(); 225 Ptr != End; ++Ptr) { 226 // If C is not a horizontal whitespace, skip it. 227 if (*Ptr != ' ' && *Ptr != '\t') { 228 NewFile.push_back(*Ptr); 229 continue; 230 } 231 232 // Otherwise, add one space and advance over neighboring space. 233 NewFile.push_back(' '); 234 while (Ptr+1 != End && 235 (Ptr[1] == ' ' || Ptr[1] == '\t')) 236 ++Ptr; 237 } 238 239 // Free the old buffer and return a new one. 240 MemoryBuffer *MB2 = 241 MemoryBuffer::getMemBufferCopy(NewFile.data(), 242 NewFile.data() + NewFile.size(), 243 MB->getBufferIdentifier()); 244 245 delete MB; 246 return MB2; 247} 248 249 250static void PrintCheckFailed(const SourceMgr &SM, const CheckString &CheckStr, 251 StringRef Buffer) { 252 // Otherwise, we have an error, emit an error message. 253 SM.PrintMessage(CheckStr.Loc, "expected string not found in input", 254 "error"); 255 256 // Print the "scanning from here" line. If the current position is at the 257 // end of a line, advance to the start of the next line. 258 Buffer = Buffer.substr(Buffer.find_first_not_of(" \t\n\r")); 259 260 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), "scanning from here", 261 "note"); 262} 263 264/// CountNumNewlinesBetween - Count the number of newlines in the specified 265/// range. 266static unsigned CountNumNewlinesBetween(StringRef Range) { 267 unsigned NumNewLines = 0; 268 while (1) { 269 // Scan for newline. 270 Range = Range.substr(Range.find_first_of("\n\r")); 271 if (Range.empty()) return NumNewLines; 272 273 ++NumNewLines; 274 275 // Handle \n\r and \r\n as a single newline. 276 if (Range.size() > 1 && 277 (Range[1] == '\n' || Range[1] == '\r') && 278 (Range[0] != Range[1])) 279 Range = Range.substr(1); 280 Range = Range.substr(1); 281 } 282} 283 284int main(int argc, char **argv) { 285 sys::PrintStackTraceOnErrorSignal(); 286 PrettyStackTraceProgram X(argc, argv); 287 cl::ParseCommandLineOptions(argc, argv); 288 289 SourceMgr SM; 290 291 // Read the expected strings from the check file. 292 std::vector<CheckString> CheckStrings; 293 if (ReadCheckFile(SM, CheckStrings)) 294 return 2; 295 296 // Open the file to check and add it to SourceMgr. 297 std::string ErrorStr; 298 MemoryBuffer *F = 299 MemoryBuffer::getFileOrSTDIN(InputFilename.c_str(), &ErrorStr); 300 if (F == 0) { 301 errs() << "Could not open input file '" << InputFilename << "': " 302 << ErrorStr << '\n'; 303 return true; 304 } 305 306 // Remove duplicate spaces in the input file if requested. 307 if (!NoCanonicalizeWhiteSpace) 308 F = CanonicalizeInputFile(F); 309 310 SM.AddNewSourceBuffer(F, SMLoc()); 311 312 // Check that we have all of the expected strings, in order, in the input 313 // file. 314 StringRef Buffer = F->getBuffer(); 315 316 const char *LastMatch = Buffer.data(); 317 318 for (unsigned StrNo = 0, e = CheckStrings.size(); StrNo != e; ++StrNo) { 319 const CheckString &CheckStr = CheckStrings[StrNo]; 320 321 StringRef SearchFrom = Buffer; 322 323 // Find StrNo in the file. 324 size_t MatchLen = 0; 325 Buffer = Buffer.substr(CheckStr.Pat.Match(Buffer, MatchLen)); 326 327 // If we didn't find a match, reject the input. 328 if (Buffer.empty()) { 329 PrintCheckFailed(SM, CheckStr, SearchFrom); 330 return 1; 331 } 332 333 StringRef SkippedRegion(LastMatch, Buffer.data()-LastMatch); 334 335 // If this check is a "CHECK-NEXT", verify that the previous match was on 336 // the previous line (i.e. that there is one newline between them). 337 if (CheckStr.IsCheckNext) { 338 // Count the number of newlines between the previous match and this one. 339 assert(LastMatch != F->getBufferStart() && 340 "CHECK-NEXT can't be the first check in a file"); 341 342 unsigned NumNewLines = CountNumNewlinesBetween(SkippedRegion); 343 if (NumNewLines == 0) { 344 SM.PrintMessage(CheckStr.Loc, 345 CheckPrefix+"-NEXT: is on the same line as previous match", 346 "error"); 347 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), 348 "'next' match was here", "note"); 349 SM.PrintMessage(SMLoc::getFromPointer(LastMatch), 350 "previous match was here", "note"); 351 return 1; 352 } 353 354 if (NumNewLines != 1) { 355 SM.PrintMessage(CheckStr.Loc, 356 CheckPrefix+ 357 "-NEXT: is not on the line after the previous match", 358 "error"); 359 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), 360 "'next' match was here", "note"); 361 SM.PrintMessage(SMLoc::getFromPointer(LastMatch), 362 "previous match was here", "note"); 363 return 1; 364 } 365 } 366 367 // If this match had "not strings", verify that they don't exist in the 368 // skipped region. 369 for (unsigned i = 0, e = CheckStr.NotStrings.size(); i != e; ++i) { 370 size_t Pos = SkippedRegion.find(CheckStr.NotStrings[i].second); 371 if (Pos == StringRef::npos) continue; 372 373 SM.PrintMessage(SMLoc::getFromPointer(LastMatch+Pos), 374 CheckPrefix+"-NOT: string occurred!", "error"); 375 SM.PrintMessage(CheckStr.NotStrings[i].first, 376 CheckPrefix+"-NOT: pattern specified here", "note"); 377 return 1; 378 } 379 380 381 // Otherwise, everything is good. Step over the matched text and remember 382 // the position after the match as the end of the last match. 383 Buffer = Buffer.substr(MatchLen); 384 LastMatch = Buffer.data(); 385 } 386 387 return 0; 388} 389