+size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer,
+ bool IsLabelScanMode, size_t &MatchLen,
+ StringMap<StringRef> &VariableTable) const {
+ size_t LastPos = 0;
+ std::vector<const Pattern *> NotStrings;
+
+ // IsLabelScanMode is true when we are scanning forward to find CHECK-LABEL
+ // bounds; we have not processed variable definitions within the bounded block
+ // yet so cannot handle any final CHECK-DAG yet; this is handled when going
+ // over the block again (including the last CHECK-LABEL) in normal mode.
+ if (!IsLabelScanMode) {
+ // Match "dag strings" (with mixed "not strings" if any).
+ LastPos = CheckDag(SM, Buffer, NotStrings, VariableTable);
+ if (LastPos == StringRef::npos)
+ return StringRef::npos;
+ }
+
+ // Match itself from the last position after matching CHECK-DAG.
+ StringRef MatchBuffer = Buffer.substr(LastPos);
+ size_t MatchPos = Pat.Match(MatchBuffer, MatchLen, VariableTable);
+ if (MatchPos == StringRef::npos) {
+ PrintCheckFailed(SM, *this, MatchBuffer, VariableTable);
+ return StringRef::npos;
+ }
+ MatchPos += LastPos;
+
+ // Similar to the above, in "label-scan mode" we can't yet handle CHECK-NEXT
+ // or CHECK-NOT
+ if (!IsLabelScanMode) {
+ StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos);
+
+ // If this check is a "CHECK-NEXT", verify that the previous match was on
+ // the previous line (i.e. that there is one newline between them).
+ if (CheckNext(SM, SkippedRegion))
+ return StringRef::npos;
+
+ // If this match had "not strings", verify that they don't exist in the
+ // skipped region.
+ if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable))
+ return StringRef::npos;
+ }
+
+ return MatchPos;
+}
+
+bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
+ if (CheckTy != Check::CheckNext)
+ return false;
+
+ // Count the number of newlines between the previous match and this one.
+ assert(Buffer.data() !=
+ SM.getMemoryBuffer(
+ SM.FindBufferContainingLoc(
+ SMLoc::getFromPointer(Buffer.data())))->getBufferStart() &&
+ "CHECK-NEXT can't be the first check in a file");
+
+ unsigned NumNewLines = CountNumNewlinesBetween(Buffer);
+
+ if (NumNewLines == 0) {
+ SM.PrintMessage(Loc, SourceMgr::DK_Error, Prefix +
+ "-NEXT: is on the same line as previous match");
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()),
+ SourceMgr::DK_Note, "'next' match was here");
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
+ "previous match ended here");
+ return true;
+ }
+
+ if (NumNewLines != 1) {
+ SM.PrintMessage(Loc, SourceMgr::DK_Error, Prefix +
+ "-NEXT: is not on the line after the previous match");
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()),
+ SourceMgr::DK_Note, "'next' match was here");
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
+ "previous match ended here");
+ return true;
+ }
+
+ return false;
+}
+
+bool CheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
+ const std::vector<const Pattern *> &NotStrings,
+ StringMap<StringRef> &VariableTable) const {
+ for (unsigned ChunkNo = 0, e = NotStrings.size();
+ ChunkNo != e; ++ChunkNo) {
+ const Pattern *Pat = NotStrings[ChunkNo];
+ assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
+
+ size_t MatchLen = 0;
+ size_t Pos = Pat->Match(Buffer, MatchLen, VariableTable);
+
+ if (Pos == StringRef::npos) continue;
+
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()+Pos),
+ SourceMgr::DK_Error,
+ Prefix + "-NOT: string occurred!");
+ SM.PrintMessage(Pat->getLoc(), SourceMgr::DK_Note,
+ Prefix + "-NOT: pattern specified here");
+ return true;
+ }
+
+ return false;
+}
+
+size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
+ std::vector<const Pattern *> &NotStrings,
+ StringMap<StringRef> &VariableTable) const {
+ if (DagNotStrings.empty())
+ return 0;
+
+ size_t LastPos = 0;
+ size_t StartPos = LastPos;
+
+ for (unsigned ChunkNo = 0, e = DagNotStrings.size();
+ ChunkNo != e; ++ChunkNo) {
+ const Pattern &Pat = DagNotStrings[ChunkNo];
+
+ assert((Pat.getCheckTy() == Check::CheckDAG ||
+ Pat.getCheckTy() == Check::CheckNot) &&
+ "Invalid CHECK-DAG or CHECK-NOT!");
+
+ if (Pat.getCheckTy() == Check::CheckNot) {
+ NotStrings.push_back(&Pat);
+ continue;
+ }
+
+ assert((Pat.getCheckTy() == Check::CheckDAG) && "Expect CHECK-DAG!");
+
+ size_t MatchLen = 0, MatchPos;
+
+ // CHECK-DAG always matches from the start.
+ StringRef MatchBuffer = Buffer.substr(StartPos);
+ MatchPos = Pat.Match(MatchBuffer, MatchLen, VariableTable);
+ // With a group of CHECK-DAGs, a single mismatching means the match on
+ // that group of CHECK-DAGs fails immediately.
+ if (MatchPos == StringRef::npos) {
+ PrintCheckFailed(SM, Pat.getLoc(), Pat, MatchBuffer, VariableTable);
+ return StringRef::npos;
+ }
+ // Re-calc it as the offset relative to the start of the original string.
+ MatchPos += StartPos;
+
+ if (!NotStrings.empty()) {
+ if (MatchPos < LastPos) {
+ // Reordered?
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + MatchPos),
+ SourceMgr::DK_Error,
+ Prefix + "-DAG: found a match of CHECK-DAG"
+ " reordering across a CHECK-NOT");
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + LastPos),
+ SourceMgr::DK_Note,
+ Prefix + "-DAG: the farthest match of CHECK-DAG"
+ " is found here");
+ SM.PrintMessage(NotStrings[0]->getLoc(), SourceMgr::DK_Note,
+ Prefix + "-NOT: the crossed pattern specified"
+ " here");
+ SM.PrintMessage(Pat.getLoc(), SourceMgr::DK_Note,
+ Prefix + "-DAG: the reordered pattern specified"
+ " here");
+ return StringRef::npos;
+ }
+ // All subsequent CHECK-DAGs should be matched from the farthest
+ // position of all precedent CHECK-DAGs (including this one.)
+ StartPos = LastPos;
+ // If there's CHECK-NOTs between two CHECK-DAGs or from CHECK to
+ // CHECK-DAG, verify that there's no 'not' strings occurred in that
+ // region.
+ StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos);
+ if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable))
+ return StringRef::npos;
+ // Clear "not strings".
+ NotStrings.clear();
+ }
+
+ // Update the last position with CHECK-DAG matches.
+ LastPos = std::max(MatchPos + MatchLen, LastPos);
+ }
+
+ return LastPos;
+}
+
+// A check prefix must contain only alphanumeric, hyphens and underscores.
+static bool ValidateCheckPrefix(StringRef CheckPrefix) {
+ Regex Validator("^[a-zA-Z0-9_-]*$");
+ return Validator.match(CheckPrefix);
+}
+
+static bool ValidateCheckPrefixes() {
+ StringSet<> PrefixSet;
+
+ for (prefix_iterator I = CheckPrefixes.begin(), E = CheckPrefixes.end();
+ I != E; ++I) {
+ StringRef Prefix(*I);
+
+ if (!PrefixSet.insert(Prefix))
+ return false;
+
+ if (!ValidateCheckPrefix(Prefix))
+ return false;
+ }
+
+ return true;
+}
+
+// I don't think there's a way to specify an initial value for cl::list,
+// so if nothing was specified, add the default
+static void AddCheckPrefixIfNeeded() {
+ if (CheckPrefixes.empty())
+ CheckPrefixes.push_back("CHECK");
+}
+