Index: llvm/lib/Support/FileCheck.cpp =================================================================== --- llvm/lib/Support/FileCheck.cpp +++ llvm/lib/Support/FileCheck.cpp @@ -94,7 +94,7 @@ if (StrVal.getAsInteger(10, SignedValue)) return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr); - return ExpressionValue(SignedValue); + return ExpressionValue(SignedValue, StrVal); } bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower; @@ -102,7 +102,7 @@ if (StrVal.getAsInteger(Hex ? 16 : 10, UnsignedValue)) return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr); - return ExpressionValue(UnsignedValue); + return ExpressionValue(UnsignedValue, StrVal); } static int64_t getAsSigned(uint64_t UnsignedValue) { @@ -1295,6 +1295,58 @@ } } +void Pattern::printVariableDefs(const SourceMgr &SM, StringRef Buffer, + FileCheckDiag::MatchType MatchTy, + std::vector *Diags) const { + if (VariableDefs.empty() && NumericVariableDefs.empty()) + return; + // Build list of variable captures. + struct VarCapture { + StringRef Name; + SMRange Range; + }; + SmallVector VarCaptures; + for (const auto &VariableDef : VariableDefs) { + VarCapture VC; + VC.Name = VariableDef.first; + StringRef Value = Context->GlobalVariableTable[VC.Name]; + SMLoc Start = SMLoc::getFromPointer(Value.data()); + SMLoc End = SMLoc::getFromPointer(Value.data() + Value.size()); + VC.Range = SMRange(Start, End); + VarCaptures.push_back(VC); + } + for (const auto &VariableDef : NumericVariableDefs) { + VarCapture VC; + VC.Name = VariableDef.getKey(); + StringRef StrValue = VariableDef.getValue() + .DefinedNumericVariable->getValue() + .getValue() + .getStringValue(); + SMLoc Start = SMLoc::getFromPointer(StrValue.data()); + SMLoc End = SMLoc::getFromPointer(StrValue.data() + StrValue.size()); + VC.Range = SMRange(Start, End); + VarCaptures.push_back(VC); + } + // Sort variable captures by the order in which they matched the input. + // Ranges shouldn't be overlapping, so we can just compare the start. + std::sort(VarCaptures.begin(), VarCaptures.end(), + [](const VarCapture &A, const VarCapture &B) { + assert(A.Range.Start != B.Range.Start && + "unexpected overlapping variable captures"); + return A.Range.Start.getPointer() < B.Range.Start.getPointer(); + }); + // Create notes for the sorted captures. + for (const VarCapture &VC : VarCaptures) { + SmallString<256> Msg; + raw_svector_ostream OS(Msg); + OS << "captured var \"" << VC.Name << "\""; + if (Diags) + Diags->emplace_back(SM, CheckTy, getLoc(), MatchTy, VC.Range, OS.str()); + else + SM.PrintMessage(VC.Range.Start, SourceMgr::DK_Note, OS.str(), VC.Range); + } +} + static SMRange ProcessMatchResult(FileCheckDiag::MatchType MatchTy, const SourceMgr &SM, SMLoc Loc, Check::FileCheckType CheckTy, @@ -1876,8 +1928,10 @@ : FileCheckDiag::MatchFoundButExcluded; SMRange MatchRange = ProcessMatchResult(MatchTy, SM, Loc, Pat.getCheckTy(), Buffer, MatchPos, MatchLen, Diags); - if (Diags) + if (Diags) { Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, Diags); + Pat.printVariableDefs(SM, Buffer, MatchTy, Diags); + } if (!PrintDiag) return; @@ -1893,6 +1947,7 @@ SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here", {MatchRange}); Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, nullptr); + Pat.printVariableDefs(SM, Buffer, MatchTy, nullptr); } static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM, Index: llvm/lib/Support/FileCheckImpl.h =================================================================== --- llvm/lib/Support/FileCheckImpl.h +++ llvm/lib/Support/FileCheckImpl.h @@ -114,10 +114,12 @@ private: uint64_t Value; bool Negative; + StringRef StrValue; public: template - explicit ExpressionValue(T Val) : Value(Val), Negative(Val < 0) {} + explicit ExpressionValue(T Val, StringRef StrVal = StringRef()) + : Value(Val), Negative(Val < 0), StrValue(StrVal) {} bool operator==(const ExpressionValue &Other) const { return Value == Other.Value && isNegative() == Other.isNegative(); @@ -144,6 +146,10 @@ /// \returns an unsigned ExpressionValue instance whose value is the absolute /// value to this object's value. ExpressionValue getAbsolute() const; + + /// \returns the input buffer's string from which this value was parsed, or an + /// empty string if none. + StringRef getStringValue() const { return StrValue; } }; /// Performs operation and \returns its result or an error in case of failure, @@ -691,6 +697,9 @@ bool hasVariable() const { return !(Substitutions.empty() && VariableDefs.empty()); } + void printVariableDefs(const SourceMgr &SM, StringRef Buffer, + FileCheckDiag::MatchType MatchTy, + std::vector *Diags) const; Check::FileCheckType getCheckTy() const { return CheckTy; } Index: llvm/test/FileCheck/dump-input-annotations.txt =================================================================== --- llvm/test/FileCheck/dump-input-annotations.txt +++ llvm/test/FileCheck/dump-input-annotations.txt @@ -665,12 +665,61 @@ ; SUBST-NEG-NEXT:>>>>>> ;-------------------------------------------------- -; Substitutions: CHECK-NEXT, CHECK-SAME, CHECK-DAG fixups. +; Captured variables +;-------------------------------------------------- + +; RUN: echo 'strvar: foo' > %t.in +; RUN: echo 'numvar no expr: 51' >> %t.in +; RUN: echo 'numvar expr: -72' >> %t.in +; RUN: echo 'multiple vars: foo 100 8 bar' >> %t.in +; RUN: echo 'var in neg match: foo' >> %t.in +; RUN: echo 'END' >> %t.in + +; RUN: echo 'CHECK: strvar: [[STRVAR:[a-z]+]]' > %t.chk +; RUN: echo 'CHECK: numvar no expr: [[#NUMVAR_NO_EXPR:]]' >> %t.chk +; RUN: echo 'CHECK: numvar expr: [[#%d,NUMVAR_EXPR:-72]]' >> %t.chk +; Capture many variables of different kinds in a different order than their +; names sort alphabetically to ensure they're sorted in capture order. +; RUN: echo 'CHECK: multiple vars: [[VAR1:foo]] [[#%d,VAR3:]] [[#VAR2:]] [[VAR4:bar]]' >> %t.chk +; RUN: echo 'CHECK-NOT: var in neg match: [[VAR:foo]]' >> %t.chk +; RUN: echo 'CHECK: END' >> %t.chk + +; RUN: %ProtectFileCheckOutput \ +; RUN: not FileCheck -dump-input=always -vv -input-file=%t.in %t.chk 2>&1 \ +; RUN: | FileCheck -strict-whitespace -match-full-lines %s -check-prefix=CAPTURE-NEG + +; CAPTURE-NEG:<<<<<< +; CAPTURE-NEG-NEXT: 1: strvar: foo +; CAPTURE-NEG-NEXT:check:1'0 ^~~~~~~~~~~ +; CAPTURE-NEG-NEXT:check:1'1 ^~~ captured var "STRVAR" +; CAPTURE-NEG-NEXT: 2: numvar no expr: 51 +; CAPTURE-NEG-NEXT:check:2'0 ^~~~~~~~~~~~~~~~~~ +; CAPTURE-NEG-NEXT:check:2'1 ^~ captured var "NUMVAR_NO_EXPR" +; CAPTURE-NEG-NEXT: 3: numvar expr: -72 +; CAPTURE-NEG-NEXT:check:3'0 ^~~~~~~~~~~~~~~~ +; CAPTURE-NEG-NEXT:check:3'1 with "%d,NUMVAR_EXPR:-72" equal to "-72" +; CAPTURE-NEG-NEXT:check:3'2 ^~~ captured var "NUMVAR_EXPR" +; CAPTURE-NEG-NEXT: 4: multiple vars: foo 100 8 bar +; CAPTURE-NEG-NEXT:check:4'0 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ +; CAPTURE-NEG-NEXT:check:4'1 ^~~ captured var "VAR1" +; CAPTURE-NEG-NEXT:check:4'2 ^~~ captured var "VAR3" +; CAPTURE-NEG-NEXT:check:4'3 ^ captured var "VAR2" +; CAPTURE-NEG-NEXT:check:4'4 ^~~ captured var "VAR4" +; CAPTURE-NEG-NEXT: 5: var in neg match: foo +; CAPTURE-NEG-NEXT:not:5'0 !~~~~~~~~~~~~~~~~~~~~ error: no match expected +; CAPTURE-NEG-NEXT:not:5'1 !~~ captured var "VAR" +; CAPTURE-NEG-NEXT: 6: END +; CAPTURE-NEG-NEXT:check:6 ^~~ +; CAPTURE-NEG-NEXT:>>>>>> + +;-------------------------------------------------- +; CHECK-NEXT, CHECK-SAME, CHECK-DAG note fixups. ; ; When CHECK-NEXT or CHECK-SAME fails for the wrong line, or when a CHECK-DAG ; match is discarded, the associated diagnostic type must be converted from -; successful to failed or discarded. However, any substitution diagnostics must -; be traversed to find that diagnostic. +; successful to failed or discarded. However, any note annotation must be +; traversed to find that diagnostic. We check this behavior here only for +; substitutions, but it's the same mechanism for all note annotations. ;-------------------------------------------------- ;- - - - - - - - - - - - - - - - - - - - - - - - - Index: llvm/test/FileCheck/verbose.txt =================================================================== --- llvm/test/FileCheck/verbose.txt +++ llvm/test/FileCheck/verbose.txt @@ -55,6 +55,31 @@ VV-NEXT: {{^}}bar{{$}} VV-NEXT: {{^}}^{{$}} +STRVAR=foobar +STRVAR:foobar +CHECK: STRVAR=[[STRVAR:[a-z]+]] +CHECK-NEXT: STRVAR:[[STRVAR]] + + V: verbose.txt:[[#@LINE-3]]:8: remark: {{C}}HECK: expected string found in input +V-NEXT: {{C}}HECK: {{STRVAR=\[\[STRVAR:\[a-z\]\+\]\]}} +V-NEXT: {{^}} ^{{$}} +V-NEXT: verbose.txt:[[#@LINE-8]]:1: note: found here +V-NEXT: {{^}}STRVAR=foobar{{$}} +V-NEXT: {{^}}^~~~~~~~~~~~~{{$}} +V-NEXT: verbose.txt:[[#@LINE-11]]:8: note: captured var "STRVAR" +V-NEXT: {{^}}STRVAR=foobar{{$}} +V-NEXT: {{^}} ^~~~~~{{$}} + +V-NEXT: verbose.txt:[[#@LINE-12]]:13: remark: {{C}}HECK-NEXT: expected string found in input +V-NEXT: {{C}}HECK-NEXT: {{STRVAR:\[\[STRVAR\]\]}} +V-NEXT: {{^}} ^{{$}} +V-NEXT: verbose.txt:[[#@LINE-17]]:1: note: found here +V-NEXT: {{^}}STRVAR:foobar{{$}} +V-NEXT: {{^}}^~~~~~~~~~~~~{{$}} +V-NEXT: verbose.txt:[[#@LINE-20]]:1: note: with "STRVAR" equal to "foobar" +V-NEXT: {{^}}STRVAR:foobar{{$}} +V-NEXT: {{^}}^{{$}} + NUMVAR=42 NUMVAR - 1:41 CHECK: NUMVAR=[[#NUMVAR:]] @@ -67,21 +92,24 @@ V-NEXT: verbose.txt:[[#@LINE-9]]:1: note: found here V-NEXT: {{^}}NUMVAR=42{{$}} V-NEXT: {{^}}^~~~~~~~~{{$}} +V-NEXT: verbose.txt:[[#@LINE-12]]:8: note: captured var "NUMVAR" +V-NEXT: NUMVAR=42 +V-NEXT: ^~ -V-NEXT: verbose.txt:[[#@LINE-9]]:13: remark: {{C}}HECK-NEXT: expected string found in input +V-NEXT: verbose.txt:[[#@LINE-12]]:13: remark: {{C}}HECK-NEXT: expected string found in input V-NEXT: {{C}}HECK-NEXT: {{NUMVAR - 1:[[][[]#NUMVAR - 1[]][]]$}} V-NEXT: {{^}} ^{{$}} -V-NEXT: verbose.txt:[[#@LINE-15]]:1: note: found here +V-NEXT: verbose.txt:[[#@LINE-18]]:1: note: found here V-NEXT: {{^}}NUMVAR - 1:41{{$}} V-NEXT: {{^}}^~~~~~~~~~~~~{{$}} -V-NEXT: verbose.txt:[[#@LINE-18]]:1: note: with "NUMVAR - 1" equal to "41" +V-NEXT: verbose.txt:[[#@LINE-21]]:1: note: with "NUMVAR - 1" equal to "41" V-NEXT: {{^}}NUMVAR - 1:41{{$}} V-NEXT: {{^}}^{{$}} -VV-NEXT: verbose.txt:[[#@LINE-20]]:12: remark: {{C}}HECK-NOT: excluded string not found in input +VV-NEXT: verbose.txt:[[#@LINE-23]]:12: remark: {{C}}HECK-NOT: excluded string not found in input VV-NEXT: {{C}}HECK-NOT: {{[[][[]#NUMVAR [+] 1[]][]]$}} VV-NEXT: {{^}} ^{{$}} -VV-NEXT: verbose.txt:[[#@LINE-25]]:1: note: scanning from here +VV-NEXT: verbose.txt:[[#@LINE-28]]:1: note: scanning from here VV-NEXT: {{^}}NUMVAR - 1:41{{$}} VV-NEXT: {{^}}^{{$}}