llvm-project

Форк
0
879 строк · 35.4 Кб
1
//===- FileCheck.cpp - Check that File's Contents match what is expected --===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// FileCheck does a line-by line check of a file that validates whether it
10
// contains the expected content.  This is useful for regression tests etc.
11
//
12
// This program exits with an exit status of 2 on error, exit status of 0 if
13
// the file matched the expected contents, and exit status of 1 if it did not
14
// contain the expected contents.
15
//
16
//===----------------------------------------------------------------------===//
17

18
#include "llvm/FileCheck/FileCheck.h"
19
#include "llvm/Support/CommandLine.h"
20
#include "llvm/Support/InitLLVM.h"
21
#include "llvm/Support/MemoryBuffer.h"
22
#include "llvm/Support/Process.h"
23
#include "llvm/Support/SourceMgr.h"
24
#include "llvm/Support/WithColor.h"
25
#include "llvm/Support/raw_ostream.h"
26
#include <cmath>
27
#include <map>
28
using namespace llvm;
29

30
static cl::extrahelp FileCheckOptsEnv(
31
    "\nOptions are parsed from the environment variable FILECHECK_OPTS and\n"
32
    "from the command line.\n");
33

34
static cl::opt<std::string>
35
    CheckFilename(cl::Positional, cl::desc("<check-file>"), cl::Optional);
36

37
static cl::opt<std::string>
38
    InputFilename("input-file", cl::desc("File to check (defaults to stdin)"),
39
                  cl::init("-"), cl::value_desc("filename"));
40

41
static cl::list<std::string> CheckPrefixes(
42
    "check-prefix",
43
    cl::desc("Prefix to use from check file (defaults to 'CHECK')"));
44
static cl::alias CheckPrefixesAlias(
45
    "check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated,
46
    cl::NotHidden,
47
    cl::desc(
48
        "Alias for -check-prefix permitting multiple comma separated values"));
49

50
static cl::list<std::string> CommentPrefixes(
51
    "comment-prefixes", cl::CommaSeparated, cl::Hidden,
52
    cl::desc("Comma-separated list of comment prefixes to use from check file\n"
53
             "(defaults to 'COM,RUN'). Please avoid using this feature in\n"
54
             "LLVM's LIT-based test suites, which should be easier to\n"
55
             "maintain if they all follow a consistent comment style. This\n"
56
             "feature is meant for non-LIT test suites using FileCheck."));
57

58
static cl::opt<bool> NoCanonicalizeWhiteSpace(
59
    "strict-whitespace",
60
    cl::desc("Do not treat all horizontal whitespace as equivalent"));
61

62
static cl::opt<bool> IgnoreCase(
63
    "ignore-case",
64
    cl::desc("Use case-insensitive matching"));
65

66
static cl::list<std::string> ImplicitCheckNot(
67
    "implicit-check-not",
68
    cl::desc("Add an implicit negative check with this pattern to every\n"
69
             "positive check. This can be used to ensure that no instances of\n"
70
             "this pattern occur which are not matched by a positive pattern"),
71
    cl::value_desc("pattern"));
72

73
static cl::list<std::string>
74
    GlobalDefines("D", cl::AlwaysPrefix,
75
                  cl::desc("Define a variable to be used in capture patterns."),
76
                  cl::value_desc("VAR=VALUE"));
77

78
static cl::opt<bool> AllowEmptyInput(
79
    "allow-empty", cl::init(false),
80
    cl::desc("Allow the input file to be empty. This is useful when making\n"
81
             "checks that some error message does not occur, for example."));
82

83
static cl::opt<bool> AllowUnusedPrefixes(
84
    "allow-unused-prefixes",
85
    cl::desc("Allow prefixes to be specified but not appear in the test."));
86

87
static cl::opt<bool> MatchFullLines(
88
    "match-full-lines", cl::init(false),
89
    cl::desc("Require all positive matches to cover an entire input line.\n"
90
             "Allows leading and trailing whitespace if --strict-whitespace\n"
91
             "is not also passed."));
92

93
static cl::opt<bool> EnableVarScope(
94
    "enable-var-scope", cl::init(false),
95
    cl::desc("Enables scope for regex variables. Variables with names that\n"
96
             "do not start with '$' will be reset at the beginning of\n"
97
             "each CHECK-LABEL block."));
98

99
static cl::opt<bool> AllowDeprecatedDagOverlap(
100
    "allow-deprecated-dag-overlap", cl::init(false),
101
    cl::desc("Enable overlapping among matches in a group of consecutive\n"
102
             "CHECK-DAG directives.  This option is deprecated and is only\n"
103
             "provided for convenience as old tests are migrated to the new\n"
104
             "non-overlapping CHECK-DAG implementation.\n"));
105

106
static cl::opt<bool> Verbose(
107
    "v",
108
    cl::desc("Print directive pattern matches, or add them to the input dump\n"
109
             "if enabled.\n"));
110

111
static cl::opt<bool> VerboseVerbose(
112
    "vv",
113
    cl::desc("Print information helpful in diagnosing internal FileCheck\n"
114
             "issues, or add it to the input dump if enabled.  Implies\n"
115
             "-v.\n"));
116

117
// The order of DumpInputValue members affects their precedence, as documented
118
// for -dump-input below.
119
enum DumpInputValue {
120
  DumpInputNever,
121
  DumpInputFail,
122
  DumpInputAlways,
123
  DumpInputHelp
124
};
125

126
static cl::list<DumpInputValue> DumpInputs(
127
    "dump-input",
128
    cl::desc("Dump input to stderr, adding annotations representing\n"
129
             "currently enabled diagnostics.  When there are multiple\n"
130
             "occurrences of this option, the <value> that appears earliest\n"
131
             "in the list below has precedence.  The default is 'fail'.\n"),
132
    cl::value_desc("mode"),
133
    cl::values(clEnumValN(DumpInputHelp, "help", "Explain input dump and quit"),
134
               clEnumValN(DumpInputAlways, "always", "Always dump input"),
135
               clEnumValN(DumpInputFail, "fail", "Dump input on failure"),
136
               clEnumValN(DumpInputNever, "never", "Never dump input")));
137

138
// The order of DumpInputFilterValue members affects their precedence, as
139
// documented for -dump-input-filter below.
140
enum DumpInputFilterValue {
141
  DumpInputFilterError,
142
  DumpInputFilterAnnotation,
143
  DumpInputFilterAnnotationFull,
144
  DumpInputFilterAll
145
};
146

147
static cl::list<DumpInputFilterValue> DumpInputFilters(
148
    "dump-input-filter",
149
    cl::desc("In the dump requested by -dump-input, print only input lines of\n"
150
             "kind <value> plus any context specified by -dump-input-context.\n"
151
             "When there are multiple occurrences of this option, the <value>\n"
152
             "that appears earliest in the list below has precedence.  The\n"
153
             "default is 'error' when -dump-input=fail, and it's 'all' when\n"
154
             "-dump-input=always.\n"),
155
    cl::values(clEnumValN(DumpInputFilterAll, "all", "All input lines"),
156
               clEnumValN(DumpInputFilterAnnotationFull, "annotation-full",
157
                          "Input lines with annotations"),
158
               clEnumValN(DumpInputFilterAnnotation, "annotation",
159
                          "Input lines with starting points of annotations"),
160
               clEnumValN(DumpInputFilterError, "error",
161
                          "Input lines with starting points of error "
162
                          "annotations")));
163

164
static cl::list<unsigned> DumpInputContexts(
165
    "dump-input-context", cl::value_desc("N"),
166
    cl::desc("In the dump requested by -dump-input, print <N> input lines\n"
167
             "before and <N> input lines after any lines specified by\n"
168
             "-dump-input-filter.  When there are multiple occurrences of\n"
169
             "this option, the largest specified <N> has precedence.  The\n"
170
             "default is 5.\n"));
171

172
typedef cl::list<std::string>::const_iterator prefix_iterator;
173

174

175

176

177

178

179

180
static void DumpCommandLine(int argc, char **argv) {
181
  errs() << "FileCheck command line: ";
182
  for (int I = 0; I < argc; I++)
183
    errs() << " " << argv[I];
184
  errs() << "\n";
185
}
186

187
struct MarkerStyle {
188
  /// The starting char (before tildes) for marking the line.
189
  char Lead;
190
  /// What color to use for this annotation.
191
  raw_ostream::Colors Color;
192
  /// A note to follow the marker, or empty string if none.
193
  std::string Note;
194
  /// Does this marker indicate inclusion by -dump-input-filter=error?
195
  bool FiltersAsError;
196
  MarkerStyle() {}
197
  MarkerStyle(char Lead, raw_ostream::Colors Color,
198
              const std::string &Note = "", bool FiltersAsError = false)
199
      : Lead(Lead), Color(Color), Note(Note), FiltersAsError(FiltersAsError) {
200
    assert((!FiltersAsError || !Note.empty()) &&
201
           "expected error diagnostic to have note");
202
  }
203
};
204

205
static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) {
206
  switch (MatchTy) {
207
  case FileCheckDiag::MatchFoundAndExpected:
208
    return MarkerStyle('^', raw_ostream::GREEN);
209
  case FileCheckDiag::MatchFoundButExcluded:
210
    return MarkerStyle('!', raw_ostream::RED, "error: no match expected",
211
                       /*FiltersAsError=*/true);
212
  case FileCheckDiag::MatchFoundButWrongLine:
213
    return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line",
214
                       /*FiltersAsError=*/true);
215
  case FileCheckDiag::MatchFoundButDiscarded:
216
    return MarkerStyle('!', raw_ostream::CYAN,
217
                       "discard: overlaps earlier match");
218
  case FileCheckDiag::MatchFoundErrorNote:
219
    // Note should always be overridden within the FileCheckDiag.
220
    return MarkerStyle('!', raw_ostream::RED,
221
                       "error: unknown error after match",
222
                       /*FiltersAsError=*/true);
223
  case FileCheckDiag::MatchNoneAndExcluded:
224
    return MarkerStyle('X', raw_ostream::GREEN);
225
  case FileCheckDiag::MatchNoneButExpected:
226
    return MarkerStyle('X', raw_ostream::RED, "error: no match found",
227
                       /*FiltersAsError=*/true);
228
  case FileCheckDiag::MatchNoneForInvalidPattern:
229
    return MarkerStyle('X', raw_ostream::RED,
230
                       "error: match failed for invalid pattern",
231
                       /*FiltersAsError=*/true);
232
  case FileCheckDiag::MatchFuzzy:
233
    return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match",
234
                       /*FiltersAsError=*/true);
235
  }
236
  llvm_unreachable_internal("unexpected match type");
237
}
238

239
static void DumpInputAnnotationHelp(raw_ostream &OS) {
240
  OS << "The following description was requested by -dump-input=help to\n"
241
     << "explain the input dump printed by FileCheck.\n"
242
     << "\n"
243
     << "Related command-line options:\n"
244
     << "\n"
245
     << "  - -dump-input=<value> enables or disables the input dump\n"
246
     << "  - -dump-input-filter=<value> filters the input lines\n"
247
     << "  - -dump-input-context=<N> adjusts the context of filtered lines\n"
248
     << "  - -v and -vv add more annotations\n"
249
     << "  - -color forces colors to be enabled both in the dump and below\n"
250
     << "  - -help documents the above options in more detail\n"
251
     << "\n"
252
     << "These options can also be set via FILECHECK_OPTS.  For example, for\n"
253
     << "maximum debugging output on failures:\n"
254
     << "\n"
255
     << "  $ FILECHECK_OPTS='-dump-input-filter=all -vv -color' ninja check\n"
256
     << "\n"
257
     << "Input dump annotation format:\n"
258
     << "\n";
259

260
  // Labels for input lines.
261
  OS << "  - ";
262
  WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:";
263
  OS << "     labels line number L of the input file\n"
264
     << "           An extra space is added after each input line to represent"
265
     << " the\n"
266
     << "           newline character\n";
267

268
  // Labels for annotation lines.
269
  OS << "  - ";
270
  WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L";
271
  OS << "    labels the only match result for either (1) a pattern of type T"
272
     << " from\n"
273
     << "           line L of the check file if L is an integer or (2) the"
274
     << " I-th implicit\n"
275
     << "           pattern if L is \"imp\" followed by an integer "
276
     << "I (index origin one)\n";
277
  OS << "  - ";
278
  WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N";
279
  OS << "  labels the Nth match result for such a pattern\n";
280

281
  // Markers on annotation lines.
282
  OS << "  - ";
283
  WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~";
284
  OS << "    marks good match (reported if -v)\n"
285
     << "  - ";
286
  WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~";
287
  OS << "    marks bad match, such as:\n"
288
     << "           - CHECK-NEXT on same line as previous match (error)\n"
289
     << "           - CHECK-NOT found (error)\n"
290
     << "           - CHECK-DAG overlapping match (discarded, reported if "
291
     << "-vv)\n"
292
     << "  - ";
293
  WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~";
294
  OS << "    marks search range when no match is found, such as:\n"
295
     << "           - CHECK-NEXT not found (error)\n"
296
     << "           - CHECK-NOT not found (success, reported if -vv)\n"
297
     << "           - CHECK-DAG not found after discarded matches (error)\n"
298
     << "  - ";
299
  WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?";
300
  OS << "      marks fuzzy match when no match is found\n";
301

302
  // Elided lines.
303
  OS << "  - ";
304
  WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "...";
305
  OS << "    indicates elided input lines and annotations, as specified by\n"
306
     << "           -dump-input-filter and -dump-input-context\n";
307

308
  // Colors.
309
  OS << "  - colors ";
310
  WithColor(OS, raw_ostream::GREEN, true) << "success";
311
  OS << ", ";
312
  WithColor(OS, raw_ostream::RED, true) << "error";
313
  OS << ", ";
314
  WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match";
315
  OS << ", ";
316
  WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match";
317
  OS << ", ";
318
  WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input";
319
  OS << "\n";
320
}
321

322
/// An annotation for a single input line.
323
struct InputAnnotation {
324
  /// The index of the match result across all checks
325
  unsigned DiagIndex;
326
  /// The label for this annotation.
327
  std::string Label;
328
  /// Is this the initial fragment of a diagnostic that has been broken across
329
  /// multiple lines?
330
  bool IsFirstLine;
331
  /// What input line (one-origin indexing) this annotation marks.  This might
332
  /// be different from the starting line of the original diagnostic if
333
  /// !IsFirstLine.
334
  unsigned InputLine;
335
  /// The column range (one-origin indexing, open end) in which to mark the
336
  /// input line.  If InputEndCol is UINT_MAX, treat it as the last column
337
  /// before the newline.
338
  unsigned InputStartCol, InputEndCol;
339
  /// The marker to use.
340
  MarkerStyle Marker;
341
  /// Whether this annotation represents a good match for an expected pattern.
342
  bool FoundAndExpectedMatch;
343
};
344

345
/// Get an abbreviation for the check type.
346
static std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) {
347
  switch (Ty) {
348
  case Check::CheckPlain:
349
    if (Ty.getCount() > 1)
350
      return "count";
351
    return "check";
352
  case Check::CheckNext:
353
    return "next";
354
  case Check::CheckSame:
355
    return "same";
356
  case Check::CheckNot:
357
    return "not";
358
  case Check::CheckDAG:
359
    return "dag";
360
  case Check::CheckLabel:
361
    return "label";
362
  case Check::CheckEmpty:
363
    return "empty";
364
  case Check::CheckComment:
365
    return "com";
366
  case Check::CheckEOF:
367
    return "eof";
368
  case Check::CheckBadNot:
369
    return "bad-not";
370
  case Check::CheckBadCount:
371
    return "bad-count";
372
  case Check::CheckMisspelled:
373
    return "misspelled";
374
  case Check::CheckNone:
375
    llvm_unreachable("invalid FileCheckType");
376
  }
377
  llvm_unreachable("unknown FileCheckType");
378
}
379

380
static void
381
BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
382
                      const std::pair<unsigned, unsigned> &ImpPatBufferIDRange,
383
                      const std::vector<FileCheckDiag> &Diags,
384
                      std::vector<InputAnnotation> &Annotations,
385
                      unsigned &LabelWidth) {
386
  struct CompareSMLoc {
387
    bool operator()(const SMLoc &LHS, const SMLoc &RHS) const {
388
      return LHS.getPointer() < RHS.getPointer();
389
    }
390
  };
391
  // How many diagnostics does each pattern have?
392
  std::map<SMLoc, unsigned, CompareSMLoc> DiagCountPerPattern;
393
  for (const FileCheckDiag &Diag : Diags)
394
    ++DiagCountPerPattern[Diag.CheckLoc];
395
  // How many diagnostics have we seen so far per pattern?
396
  std::map<SMLoc, unsigned, CompareSMLoc> DiagIndexPerPattern;
397
  // How many total diagnostics have we seen so far?
398
  unsigned DiagIndex = 0;
399
  // What's the widest label?
400
  LabelWidth = 0;
401
  for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd;
402
       ++DiagItr) {
403
    InputAnnotation A;
404
    A.DiagIndex = DiagIndex++;
405

406
    // Build label, which uniquely identifies this check result.
407
    unsigned CheckBufferID = SM.FindBufferContainingLoc(DiagItr->CheckLoc);
408
    auto CheckLineAndCol =
409
        SM.getLineAndColumn(DiagItr->CheckLoc, CheckBufferID);
410
    llvm::raw_string_ostream Label(A.Label);
411
    Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":";
412
    if (CheckBufferID == CheckFileBufferID)
413
      Label << CheckLineAndCol.first;
414
    else if (ImpPatBufferIDRange.first <= CheckBufferID &&
415
             CheckBufferID < ImpPatBufferIDRange.second)
416
      Label << "imp" << (CheckBufferID - ImpPatBufferIDRange.first + 1);
417
    else
418
      llvm_unreachable("expected diagnostic's check location to be either in "
419
                       "the check file or for an implicit pattern");
420
    if (DiagCountPerPattern[DiagItr->CheckLoc] > 1)
421
      Label << "'" << DiagIndexPerPattern[DiagItr->CheckLoc]++;
422
    LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size());
423

424
    A.Marker = GetMarker(DiagItr->MatchTy);
425
    if (!DiagItr->Note.empty()) {
426
      A.Marker.Note = DiagItr->Note;
427
      // It's less confusing if notes that don't actually have ranges don't have
428
      // markers.  For example, a marker for 'with "VAR" equal to "5"' would
429
      // seem to indicate where "VAR" matches, but the location we actually have
430
      // for the marker simply points to the start of the match/search range for
431
      // the full pattern of which the substitution is potentially just one
432
      // component.
433
      if (DiagItr->InputStartLine == DiagItr->InputEndLine &&
434
          DiagItr->InputStartCol == DiagItr->InputEndCol)
435
        A.Marker.Lead = ' ';
436
    }
437
    if (DiagItr->MatchTy == FileCheckDiag::MatchFoundErrorNote) {
438
      assert(!DiagItr->Note.empty() &&
439
             "expected custom note for MatchFoundErrorNote");
440
      A.Marker.Note = "error: " + A.Marker.Note;
441
    }
442
    A.FoundAndExpectedMatch =
443
        DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected;
444

445
    // Compute the mark location, and break annotation into multiple
446
    // annotations if it spans multiple lines.
447
    A.IsFirstLine = true;
448
    A.InputLine = DiagItr->InputStartLine;
449
    A.InputStartCol = DiagItr->InputStartCol;
450
    if (DiagItr->InputStartLine == DiagItr->InputEndLine) {
451
      // Sometimes ranges are empty in order to indicate a specific point, but
452
      // that would mean nothing would be marked, so adjust the range to
453
      // include the following character.
454
      A.InputEndCol =
455
          std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol);
456
      Annotations.push_back(A);
457
    } else {
458
      assert(DiagItr->InputStartLine < DiagItr->InputEndLine &&
459
             "expected input range not to be inverted");
460
      A.InputEndCol = UINT_MAX;
461
      Annotations.push_back(A);
462
      for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine;
463
           L <= E; ++L) {
464
        // If a range ends before the first column on a line, then it has no
465
        // characters on that line, so there's nothing to render.
466
        if (DiagItr->InputEndCol == 1 && L == E)
467
          break;
468
        InputAnnotation B;
469
        B.DiagIndex = A.DiagIndex;
470
        B.Label = A.Label;
471
        B.IsFirstLine = false;
472
        B.InputLine = L;
473
        B.Marker = A.Marker;
474
        B.Marker.Lead = '~';
475
        B.Marker.Note = "";
476
        B.InputStartCol = 1;
477
        if (L != E)
478
          B.InputEndCol = UINT_MAX;
479
        else
480
          B.InputEndCol = DiagItr->InputEndCol;
481
        B.FoundAndExpectedMatch = A.FoundAndExpectedMatch;
482
        Annotations.push_back(B);
483
      }
484
    }
485
  }
486
}
487

488
static unsigned FindInputLineInFilter(
489
    DumpInputFilterValue DumpInputFilter, unsigned CurInputLine,
490
    const std::vector<InputAnnotation>::iterator &AnnotationBeg,
491
    const std::vector<InputAnnotation>::iterator &AnnotationEnd) {
492
  if (DumpInputFilter == DumpInputFilterAll)
493
    return CurInputLine;
494
  for (auto AnnotationItr = AnnotationBeg; AnnotationItr != AnnotationEnd;
495
       ++AnnotationItr) {
496
    switch (DumpInputFilter) {
497
    case DumpInputFilterAll:
498
      llvm_unreachable("unexpected DumpInputFilterAll");
499
      break;
500
    case DumpInputFilterAnnotationFull:
501
      return AnnotationItr->InputLine;
502
    case DumpInputFilterAnnotation:
503
      if (AnnotationItr->IsFirstLine)
504
        return AnnotationItr->InputLine;
505
      break;
506
    case DumpInputFilterError:
507
      if (AnnotationItr->IsFirstLine && AnnotationItr->Marker.FiltersAsError)
508
        return AnnotationItr->InputLine;
509
      break;
510
    }
511
  }
512
  return UINT_MAX;
513
}
514

515
/// To OS, print a vertical ellipsis (right-justified at LabelWidth) if it would
516
/// occupy less lines than ElidedLines, but print ElidedLines otherwise.  Either
517
/// way, clear ElidedLines.  Thus, if ElidedLines is empty, do nothing.
518
static void DumpEllipsisOrElidedLines(raw_ostream &OS, std::string &ElidedLines,
519
                                      unsigned LabelWidth) {
520
  if (ElidedLines.empty())
521
    return;
522
  unsigned EllipsisLines = 3;
523
  if (EllipsisLines < StringRef(ElidedLines).count('\n')) {
524
    for (unsigned i = 0; i < EllipsisLines; ++i) {
525
      WithColor(OS, raw_ostream::BLACK, /*Bold=*/true)
526
          << right_justify(".", LabelWidth);
527
      OS << '\n';
528
    }
529
  } else
530
    OS << ElidedLines;
531
  ElidedLines.clear();
532
}
533

534
static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req,
535
                               DumpInputFilterValue DumpInputFilter,
536
                               unsigned DumpInputContext,
537
                               StringRef InputFileText,
538
                               std::vector<InputAnnotation> &Annotations,
539
                               unsigned LabelWidth) {
540
  OS << "Input was:\n<<<<<<\n";
541

542
  // Sort annotations.
543
  llvm::sort(Annotations,
544
             [](const InputAnnotation &A, const InputAnnotation &B) {
545
               // 1. Sort annotations in the order of the input lines.
546
               //
547
               // This makes it easier to find relevant annotations while
548
               // iterating input lines in the implementation below.  FileCheck
549
               // does not always produce diagnostics in the order of input
550
               // lines due to, for example, CHECK-DAG and CHECK-NOT.
551
               if (A.InputLine != B.InputLine)
552
                 return A.InputLine < B.InputLine;
553
               // 2. Sort annotations in the temporal order FileCheck produced
554
               // their associated diagnostics.
555
               //
556
               // This sort offers several benefits:
557
               //
558
               // A. On a single input line, the order of annotations reflects
559
               //    the FileCheck logic for processing directives/patterns.
560
               //    This can be helpful in understanding cases in which the
561
               //    order of the associated directives/patterns in the check
562
               //    file or on the command line either (i) does not match the
563
               //    temporal order in which FileCheck looks for matches for the
564
               //    directives/patterns (due to, for example, CHECK-LABEL,
565
               //    CHECK-NOT, or `--implicit-check-not`) or (ii) does match
566
               //    that order but does not match the order of those
567
               //    diagnostics along an input line (due to, for example,
568
               //    CHECK-DAG).
569
               //
570
               //    On the other hand, because our presentation format presents
571
               //    input lines in order, there's no clear way to offer the
572
               //    same benefit across input lines.  For consistency, it might
573
               //    then seem worthwhile to have annotations on a single line
574
               //    also sorted in input order (that is, by input column).
575
               //    However, in practice, this appears to be more confusing
576
               //    than helpful.  Perhaps it's intuitive to expect annotations
577
               //    to be listed in the temporal order in which they were
578
               //    produced except in cases the presentation format obviously
579
               //    and inherently cannot support it (that is, across input
580
               //    lines).
581
               //
582
               // B. When diagnostics' annotations are split among multiple
583
               //    input lines, the user must track them from one input line
584
               //    to the next.  One property of the sort chosen here is that
585
               //    it facilitates the user in this regard by ensuring the
586
               //    following: when comparing any two input lines, a
587
               //    diagnostic's annotations are sorted in the same position
588
               //    relative to all other diagnostics' annotations.
589
               return A.DiagIndex < B.DiagIndex;
590
             });
591

592
  // Compute the width of the label column.
593
  const unsigned char *InputFilePtr = InputFileText.bytes_begin(),
594
                      *InputFileEnd = InputFileText.bytes_end();
595
  unsigned LineCount = InputFileText.count('\n');
596
  if (InputFileEnd[-1] != '\n')
597
    ++LineCount;
598
  unsigned LineNoWidth = std::log10(LineCount) + 1;
599
  // +3 below adds spaces (1) to the left of the (right-aligned) line numbers
600
  // on input lines and (2) to the right of the (left-aligned) labels on
601
  // annotation lines so that input lines and annotation lines are more
602
  // visually distinct.  For example, the spaces on the annotation lines ensure
603
  // that input line numbers and check directive line numbers never align
604
  // horizontally.  Those line numbers might not even be for the same file.
605
  // One space would be enough to achieve that, but more makes it even easier
606
  // to see.
607
  LabelWidth = std::max(LabelWidth, LineNoWidth) + 3;
608

609
  // Print annotated input lines.
610
  unsigned PrevLineInFilter = 0; // 0 means none so far
611
  unsigned NextLineInFilter = 0; // 0 means uncomputed, UINT_MAX means none
612
  std::string ElidedLines;
613
  raw_string_ostream ElidedLinesOS(ElidedLines);
614
  ColorMode TheColorMode =
615
      WithColor(OS).colorsEnabled() ? ColorMode::Enable : ColorMode::Disable;
616
  if (TheColorMode == ColorMode::Enable)
617
    ElidedLinesOS.enable_colors(true);
618
  auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end();
619
  for (unsigned Line = 1;
620
       InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd;
621
       ++Line) {
622
    const unsigned char *InputFileLine = InputFilePtr;
623

624
    // Compute the previous and next line included by the filter.
625
    if (NextLineInFilter < Line)
626
      NextLineInFilter = FindInputLineInFilter(DumpInputFilter, Line,
627
                                               AnnotationItr, AnnotationEnd);
628
    assert(NextLineInFilter && "expected NextLineInFilter to be computed");
629
    if (NextLineInFilter == Line)
630
      PrevLineInFilter = Line;
631

632
    // Elide this input line and its annotations if it's not within the
633
    // context specified by -dump-input-context of an input line included by
634
    // -dump-input-filter.  However, in case the resulting ellipsis would occupy
635
    // more lines than the input lines and annotations it elides, buffer the
636
    // elided lines and annotations so we can print them instead.
637
    raw_ostream *LineOS;
638
    if ((!PrevLineInFilter || PrevLineInFilter + DumpInputContext < Line) &&
639
        (NextLineInFilter == UINT_MAX ||
640
         Line + DumpInputContext < NextLineInFilter))
641
      LineOS = &ElidedLinesOS;
642
    else {
643
      LineOS = &OS;
644
      DumpEllipsisOrElidedLines(OS, ElidedLines, LabelWidth);
645
    }
646

647
    // Print right-aligned line number.
648
    WithColor(*LineOS, raw_ostream::BLACK, /*Bold=*/true, /*BF=*/false,
649
              TheColorMode)
650
        << format_decimal(Line, LabelWidth) << ": ";
651

652
    // For the case where -v and colors are enabled, find the annotations for
653
    // good matches for expected patterns in order to highlight everything
654
    // else in the line.  There are no such annotations if -v is disabled.
655
    std::vector<InputAnnotation> FoundAndExpectedMatches;
656
    if (Req.Verbose && TheColorMode == ColorMode::Enable) {
657
      for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line;
658
           ++I) {
659
        if (I->FoundAndExpectedMatch)
660
          FoundAndExpectedMatches.push_back(*I);
661
      }
662
    }
663

664
    // Print numbered line with highlighting where there are no matches for
665
    // expected patterns.
666
    bool Newline = false;
667
    {
668
      WithColor COS(*LineOS, raw_ostream::SAVEDCOLOR, /*Bold=*/false,
669
                    /*BG=*/false, TheColorMode);
670
      bool InMatch = false;
671
      if (Req.Verbose)
672
        COS.changeColor(raw_ostream::CYAN, true, true);
673
      for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) {
674
        bool WasInMatch = InMatch;
675
        InMatch = false;
676
        for (const InputAnnotation &M : FoundAndExpectedMatches) {
677
          if (M.InputStartCol <= Col && Col < M.InputEndCol) {
678
            InMatch = true;
679
            break;
680
          }
681
        }
682
        if (!WasInMatch && InMatch)
683
          COS.resetColor();
684
        else if (WasInMatch && !InMatch)
685
          COS.changeColor(raw_ostream::CYAN, true, true);
686
        if (*InputFilePtr == '\n') {
687
          Newline = true;
688
          COS << ' ';
689
        } else
690
          COS << *InputFilePtr;
691
        ++InputFilePtr;
692
      }
693
    }
694
    *LineOS << '\n';
695
    unsigned InputLineWidth = InputFilePtr - InputFileLine;
696

697
    // Print any annotations.
698
    while (AnnotationItr != AnnotationEnd &&
699
           AnnotationItr->InputLine == Line) {
700
      WithColor COS(*LineOS, AnnotationItr->Marker.Color, /*Bold=*/true,
701
                    /*BG=*/false, TheColorMode);
702
      // The two spaces below are where the ": " appears on input lines.
703
      COS << left_justify(AnnotationItr->Label, LabelWidth) << "  ";
704
      unsigned Col;
705
      for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col)
706
        COS << ' ';
707
      COS << AnnotationItr->Marker.Lead;
708
      // If InputEndCol=UINT_MAX, stop at InputLineWidth.
709
      for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth;
710
           ++Col)
711
        COS << '~';
712
      const std::string &Note = AnnotationItr->Marker.Note;
713
      if (!Note.empty()) {
714
        // Put the note at the end of the input line.  If we were to instead
715
        // put the note right after the marker, subsequent annotations for the
716
        // same input line might appear to mark this note instead of the input
717
        // line.
718
        for (; Col <= InputLineWidth; ++Col)
719
          COS << ' ';
720
        COS << ' ' << Note;
721
      }
722
      COS << '\n';
723
      ++AnnotationItr;
724
    }
725
  }
726
  DumpEllipsisOrElidedLines(OS, ElidedLines, LabelWidth);
727

728
  OS << ">>>>>>\n";
729
}
730

731
int main(int argc, char **argv) {
732
  // Enable use of ANSI color codes because FileCheck is using them to
733
  // highlight text.
734
  llvm::sys::Process::UseANSIEscapeCodes(true);
735

736
  InitLLVM X(argc, argv);
737
  cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr,
738
                              "FILECHECK_OPTS");
739

740
  // Select -dump-input* values.  The -help documentation specifies the default
741
  // value and which value to choose if an option is specified multiple times.
742
  // In the latter case, the general rule of thumb is to choose the value that
743
  // provides the most information.
744
  DumpInputValue DumpInput =
745
      DumpInputs.empty() ? DumpInputFail : *llvm::max_element(DumpInputs);
746
  DumpInputFilterValue DumpInputFilter;
747
  if (DumpInputFilters.empty())
748
    DumpInputFilter = DumpInput == DumpInputAlways ? DumpInputFilterAll
749
                                                   : DumpInputFilterError;
750
  else
751
    DumpInputFilter = *llvm::max_element(DumpInputFilters);
752
  unsigned DumpInputContext =
753
      DumpInputContexts.empty() ? 5 : *llvm::max_element(DumpInputContexts);
754

755
  if (DumpInput == DumpInputHelp) {
756
    DumpInputAnnotationHelp(outs());
757
    return 0;
758
  }
759
  if (CheckFilename.empty()) {
760
    errs() << "<check-file> not specified\n";
761
    return 2;
762
  }
763

764
  FileCheckRequest Req;
765
  append_range(Req.CheckPrefixes, CheckPrefixes);
766

767
  append_range(Req.CommentPrefixes, CommentPrefixes);
768

769
  append_range(Req.ImplicitCheckNot, ImplicitCheckNot);
770

771
  bool GlobalDefineError = false;
772
  for (StringRef G : GlobalDefines) {
773
    size_t EqIdx = G.find('=');
774
    if (EqIdx == std::string::npos) {
775
      errs() << "Missing equal sign in command-line definition '-D" << G
776
             << "'\n";
777
      GlobalDefineError = true;
778
      continue;
779
    }
780
    if (EqIdx == 0) {
781
      errs() << "Missing variable name in command-line definition '-D" << G
782
             << "'\n";
783
      GlobalDefineError = true;
784
      continue;
785
    }
786
    Req.GlobalDefines.push_back(G);
787
  }
788
  if (GlobalDefineError)
789
    return 2;
790

791
  Req.AllowEmptyInput = AllowEmptyInput;
792
  Req.AllowUnusedPrefixes = AllowUnusedPrefixes;
793
  Req.EnableVarScope = EnableVarScope;
794
  Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap;
795
  Req.Verbose = Verbose;
796
  Req.VerboseVerbose = VerboseVerbose;
797
  Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace;
798
  Req.MatchFullLines = MatchFullLines;
799
  Req.IgnoreCase = IgnoreCase;
800

801
  if (VerboseVerbose)
802
    Req.Verbose = true;
803

804
  FileCheck FC(Req);
805
  if (!FC.ValidateCheckPrefixes())
806
    return 2;
807

808
  SourceMgr SM;
809

810
  // Read the expected strings from the check file.
811
  ErrorOr<std::unique_ptr<MemoryBuffer>> CheckFileOrErr =
812
      MemoryBuffer::getFileOrSTDIN(CheckFilename, /*IsText=*/true);
813
  if (std::error_code EC = CheckFileOrErr.getError()) {
814
    errs() << "Could not open check file '" << CheckFilename
815
           << "': " << EC.message() << '\n';
816
    return 2;
817
  }
818
  MemoryBuffer &CheckFile = *CheckFileOrErr.get();
819

820
  SmallString<4096> CheckFileBuffer;
821
  StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer);
822

823
  unsigned CheckFileBufferID =
824
      SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
825
                                CheckFileText, CheckFile.getBufferIdentifier()),
826
                            SMLoc());
827

828
  std::pair<unsigned, unsigned> ImpPatBufferIDRange;
829
  if (FC.readCheckFile(SM, CheckFileText, &ImpPatBufferIDRange))
830
    return 2;
831

832
  // Open the file to check and add it to SourceMgr.
833
  ErrorOr<std::unique_ptr<MemoryBuffer>> InputFileOrErr =
834
      MemoryBuffer::getFileOrSTDIN(InputFilename, /*IsText=*/true);
835
  if (InputFilename == "-")
836
    InputFilename = "<stdin>"; // Overwrite for improved diagnostic messages
837
  if (std::error_code EC = InputFileOrErr.getError()) {
838
    errs() << "Could not open input file '" << InputFilename
839
           << "': " << EC.message() << '\n';
840
    return 2;
841
  }
842
  MemoryBuffer &InputFile = *InputFileOrErr.get();
843

844
  if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) {
845
    errs() << "FileCheck error: '" << InputFilename << "' is empty.\n";
846
    DumpCommandLine(argc, argv);
847
    return 2;
848
  }
849

850
  SmallString<4096> InputFileBuffer;
851
  StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer);
852

853
  SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
854
                            InputFileText, InputFile.getBufferIdentifier()),
855
                        SMLoc());
856

857
  std::vector<FileCheckDiag> Diags;
858
  int ExitCode = FC.checkInput(SM, InputFileText,
859
                               DumpInput == DumpInputNever ? nullptr : &Diags)
860
                     ? EXIT_SUCCESS
861
                     : 1;
862
  if (DumpInput == DumpInputAlways ||
863
      (ExitCode == 1 && DumpInput == DumpInputFail)) {
864
    errs() << "\n"
865
           << "Input file: " << InputFilename << "\n"
866
           << "Check file: " << CheckFilename << "\n"
867
           << "\n"
868
           << "-dump-input=help explains the following input dump.\n"
869
           << "\n";
870
    std::vector<InputAnnotation> Annotations;
871
    unsigned LabelWidth;
872
    BuildInputAnnotations(SM, CheckFileBufferID, ImpPatBufferIDRange, Diags,
873
                          Annotations, LabelWidth);
874
    DumpAnnotatedInput(errs(), Req, DumpInputFilter, DumpInputContext,
875
                       InputFileText, Annotations, LabelWidth);
876
  }
877

878
  return ExitCode;
879
}
880

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.