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