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