diff --git a/llvm/include/llvm/Support/FileCheck.h b/llvm/include/llvm/Support/FileCheck.h --- a/llvm/include/llvm/Support/FileCheck.h +++ b/llvm/include/llvm/Support/FileCheck.h @@ -88,7 +88,7 @@ /// What is the FileCheck directive for this diagnostic? Check::FileCheckType CheckTy; /// Where is the FileCheck directive for this diagnostic? - unsigned CheckLine, CheckCol; + SMLoc CheckLoc; /// What type of match result does this diagnostic describe? /// /// A directive's supplied pattern is said to be either expected or excluded @@ -160,7 +160,13 @@ /// /// Only expected strings whose prefix is one of those listed in \p PrefixRE /// are recorded. \returns true in case of an error, false otherwise. - bool readCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE); + /// + /// If \p ImpPatBufferIDRange, then the range (inclusive start, exclusive end) + /// of IDs for source buffers added to \p SM for implicit patterns are + /// recorded in it. The range is empty if there are none. + bool + readCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE, + std::pair *ImpPatBufferIDRange = nullptr); bool ValidateCheckPrefixes(); diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -1069,16 +1069,13 @@ const Check::FileCheckType &CheckTy, SMLoc CheckLoc, MatchType MatchTy, SMRange InputRange) - : CheckTy(CheckTy), MatchTy(MatchTy) { + : CheckTy(CheckTy), CheckLoc(CheckLoc), MatchTy(MatchTy) { auto Start = SM.getLineAndColumn(InputRange.Start); auto End = SM.getLineAndColumn(InputRange.End); InputStartLine = Start.first; InputStartCol = Start.second; InputEndLine = End.first; InputEndCol = End.second; - Start = SM.getLineAndColumn(CheckLoc); - CheckLine = Start.first; - CheckCol = Start.second; } static bool IsPartOfWord(char c) { @@ -1269,8 +1266,12 @@ FileCheck::~FileCheck() = default; -bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer, - Regex &PrefixRE) { +bool FileCheck::readCheckFile( + SourceMgr &SM, StringRef Buffer, Regex &PrefixRE, + std::pair *ImpPatBufferIDRange) { + if (ImpPatBufferIDRange) + ImpPatBufferIDRange->first = ImpPatBufferIDRange->second = 0; + Error DefineError = PatternContext->defineCmdlineVariables(Req.GlobalDefines, SM); if (DefineError) { @@ -1291,7 +1292,17 @@ StringRef PatternInBuffer = CmdLine->getBuffer().substr(Prefix.size(), PatternString.size()); - SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); + unsigned BufferID = SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); + if (ImpPatBufferIDRange) { + if (ImpPatBufferIDRange->first == ImpPatBufferIDRange->second) { + ImpPatBufferIDRange->first = BufferID; + ImpPatBufferIDRange->second = BufferID + 1; + } else { + assert(BufferID == ImpPatBufferIDRange->second && + "expected consecutive source buffer IDs"); + ++ImpPatBufferIDRange->second; + } + } ImplicitNegativeChecks.push_back( Pattern(Check::CheckNot, PatternContext.get())); diff --git a/llvm/test/FileCheck/dump-input-annotations.txt b/llvm/test/FileCheck/dump-input-annotations.txt --- a/llvm/test/FileCheck/dump-input-annotations.txt +++ b/llvm/test/FileCheck/dump-input-annotations.txt @@ -494,3 +494,64 @@ ; LAB-NEXT: label:3'0 ~~~ ; LAB-NEXT: >>>>>> ; LAB-NOT: {{.}} + +;-------------------------------------------------- +; --implicit-check-not +; +; The first two --implicit-check-not patterns have no match (success). The +; third has an unexpected match (error). To check per-input-line annotation +; sorting, all of those plus the CHECK directives have annotations on the same +; input line. +;-------------------------------------------------- + +; RUN: echo 'hello world again!' > %t.in + +; RUN: echo 'CHECK: hel' > %t.chk +; RUN: echo 'CHECK: wor' >> %t.chk +; RUN: echo 'CHECK: !' >> %t.chk + +; RUN: %ProtectFileCheckOutput \ +; RUN: not FileCheck -dump-input=always -input-file=%t.in %t.chk 2>&1 \ +; RUN: --implicit-check-not='goodbye' \ +; RUN: --implicit-check-not='world' \ +; RUN: --implicit-check-not='again' \ +; RUN: | FileCheck -match-full-lines %s -check-prefix=IMPNOT \ +; RUN: -implicit-check-not='remark:' +; RUN: %ProtectFileCheckOutput \ +; RUN: not FileCheck -dump-input=always -input-file=%t.in %t.chk -v 2>&1 \ +; RUN: --implicit-check-not='goodbye' \ +; RUN: --implicit-check-not='world' \ +; RUN: --implicit-check-not='again' \ +; RUN: | FileCheck -match-full-lines %s -check-prefixes=IMPNOT,IMPNOT-V \ +; RUN: -implicit-check-not='remark:' +; RUN: %ProtectFileCheckOutput \ +; RUN: not FileCheck -dump-input=always -input-file=%t.in %t.chk -vv 2>&1 \ +; RUN: --implicit-check-not='goodbye' \ +; RUN: --implicit-check-not='world' \ +; RUN: --implicit-check-not='again' \ +; RUN: | FileCheck -match-full-lines %s \ +; RUN: -check-prefixes=IMPNOT,IMPNOT-V,IMPNOT-VV \ +; RUN: -implicit-check-not='remark:' + +; Verbose diagnostics are suppressed but not errors. +; IMPNOT:{{.*}}error:{{.*}} + +; FIXME: All occurrences of imp1, imp2, and imp3 are sorting after the first +; directive. They should instead be sorted by when they execute. + +; IMPNOT:<<<<<< +; IMPNOT-NEXT: 1: hello world again! +; IMPNOT-V-NEXT:check:1 ^~~ +; IMPNOT-VV-NEXT:not:imp1 X +; IMPNOT-VV-NEXT:not:imp2 X +; IMPNOT-VV-NEXT:not:imp3 X +; IMPNOT-VV-NEXT:not:imp1 X~~ +; IMPNOT-VV-NEXT:not:imp2 X~~ +; IMPNOT-VV-NEXT:not:imp3 X~~ +; IMPNOT-VV-NEXT:not:imp1 X~~~~~~~ +; IMPNOT-VV-NEXT:not:imp2 X~~~~~~~ +; IMPNOT-NEXT:not:imp3 !~~~~ error: no match expected +; IMPNOT-V-NEXT:check:2 ^~~ +; IMPNOT-V-NEXT:check:3 ^ +; IMPNOT-NEXT:>>>>>> +; IMPNOT-NOT:{{.}} diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp --- a/llvm/utils/FileCheck/FileCheck.cpp +++ b/llvm/utils/FileCheck/FileCheck.cpp @@ -193,14 +193,15 @@ // Labels for annotation lines. OS << " - "; WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L"; - OS << " labels the only match result for a pattern of type T from " - << "line L of\n" - << " the check file\n"; + OS << " labels the only match result for either (1) a pattern of type T" + << " from\n" + << " line L of the check file if L is an integer or (2) the" + << " I-th implicit\n" + << " pattern if L is \"imp\" followed by an integer " + << "I (index origin one)\n"; OS << " - "; WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N"; - OS << " labels the Nth match result for a pattern of type T from line " - << "L of\n" - << " the check file\n"; + OS << " labels the Nth match result for such a pattern\n"; // Markers on annotation lines. OS << " - "; @@ -293,9 +294,12 @@ llvm_unreachable("unknown FileCheckType"); } -static void BuildInputAnnotations(const std::vector &Diags, - std::vector &Annotations, - unsigned &LabelWidth) { +static void +BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID, + const std::pair &ImpPatBufferIDRange, + const std::vector &Diags, + std::vector &Annotations, + unsigned &LabelWidth) { // How many diagnostics has the current check seen so far? unsigned CheckDiagCount = 0; // What's the widest label? @@ -305,14 +309,24 @@ InputAnnotation A; // Build label, which uniquely identifies this check result. - A.CheckLine = DiagItr->CheckLine; + unsigned CheckBufferID = SM.FindBufferContainingLoc(DiagItr->CheckLoc); + auto CheckLineAndCol = + SM.getLineAndColumn(DiagItr->CheckLoc, CheckBufferID); + A.CheckLine = CheckLineAndCol.first; llvm::raw_string_ostream Label(A.Label); - Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":" - << DiagItr->CheckLine; + Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":"; + if (CheckBufferID == CheckFileBufferID) + Label << CheckLineAndCol.first; + else if (ImpPatBufferIDRange.first <= CheckBufferID && + CheckBufferID < ImpPatBufferIDRange.second) + Label << "imp" << (CheckBufferID - ImpPatBufferIDRange.first + 1); + else + llvm_unreachable("expected diagnostic's check location to be either in " + "the check file or for an implicit pattern"); A.CheckDiagIndex = UINT_MAX; auto DiagNext = std::next(DiagItr); if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy && - DiagItr->CheckLine == DiagNext->CheckLine) + DiagItr->CheckLoc == DiagNext->CheckLoc) A.CheckDiagIndex = CheckDiagCount++; else if (CheckDiagCount) { A.CheckDiagIndex = CheckDiagCount; @@ -606,11 +620,13 @@ SmallString<4096> CheckFileBuffer; StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer); - SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( - CheckFileText, CheckFile.getBufferIdentifier()), - SMLoc()); + unsigned CheckFileBufferID = + SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( + CheckFileText, CheckFile.getBufferIdentifier()), + SMLoc()); - if (FC.readCheckFile(SM, CheckFileText, PrefixRE)) + std::pair ImpPatBufferIDRange; + if (FC.readCheckFile(SM, CheckFileText, PrefixRE, &ImpPatBufferIDRange)) return 2; // Open the file to check and add it to SourceMgr. @@ -658,7 +674,8 @@ << "\n"; std::vector Annotations; unsigned LabelWidth; - BuildInputAnnotations(Diags, Annotations, LabelWidth); + BuildInputAnnotations(SM, CheckFileBufferID, ImpPatBufferIDRange, Diags, + Annotations, LabelWidth); DumpAnnotatedInput(errs(), Req, InputFileText, Annotations, LabelWidth); }