diff options
Diffstat (limited to 'llvm/lib/Support/FileCheck.cpp')
-rw-r--r-- | llvm/lib/Support/FileCheck.cpp | 266 |
1 files changed, 157 insertions, 109 deletions
diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp index acee5f8..c5946df 100644 --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -24,6 +24,31 @@ using namespace llvm; +llvm::Optional<std::string> FileCheckPatternSubstitution::getResult() const { + if (IsNumExpr) { + return utostr(NumExpr->getValue()); + } else { + // Look up the value and escape it so that we can put it into the + // regex. + llvm::Optional<StringRef> VarVal = Context->getPatternVarValue(FromStr); + if (!VarVal) + return llvm::None; + return Regex::escape(*VarVal); + } +} + +StringRef FileCheckPatternSubstitution::getUndefVarName() const { + // Parsing guarantees only @LINE is ever referenced and it is not undefined + // by ClearLocalVars. + if (IsNumExpr) + return StringRef(); + + if (!Context->getPatternVarValue(FromStr)) + return FromStr; + + return StringRef(); +} + bool FileCheckPattern::isValidVarNameStart(char C) { return C == '_' || isalpha(C); } @@ -55,6 +80,10 @@ bool FileCheckPattern::parseVariable(StringRef Str, bool &IsPseudo, return false; } +// StringRef holding all characters considered as horizontal whitespaces by +// FileCheck input canonicalization. +StringRef SpaceChars = " \t"; + // Parsing helper function that strips the first character in S and returns it. static char popFront(StringRef &S) { char C = S.front(); @@ -62,49 +91,58 @@ static char popFront(StringRef &S) { return C; } -bool FileCheckPattern::parseExpression(StringRef Name, StringRef Trailer, - const SourceMgr &SM) const { +FileCheckNumExpr * +FileCheckPattern::parseNumericExpression(StringRef Name, StringRef Trailer, + const SourceMgr &SM) const { if (!Name.equals("@LINE")) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, - "invalid pseudo variable '" + Name + "'"); - return true; + "invalid pseudo numeric variable '" + Name + "'"); + return nullptr; } // Check if this is a supported operation and select function to perform it. + Trailer = Trailer.ltrim(SpaceChars); if (Trailer.empty()) - return false; + return Context->makeNumExpr(LineNumber); SMLoc OpLoc = SMLoc::getFromPointer(Trailer.data()); char Operator = popFront(Trailer); - switch (Operator) { - case '+': - case '-': - break; - default: - SM.PrintMessage(OpLoc, SourceMgr::DK_Error, - Twine("unsupported numeric operation '") + Twine(Operator) + - "'"); - return true; - } // Parse right operand. + Trailer = Trailer.ltrim(SpaceChars); if (Trailer.empty()) { SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error, "missing operand in numeric expression '" + Trailer + "'"); - return true; + return nullptr; } uint64_t Offset; if (Trailer.consumeInteger(10, Offset)) { SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error, "invalid offset in numeric expression '" + Trailer + "'"); - return true; + return nullptr; } + Trailer = Trailer.ltrim(SpaceChars); if (!Trailer.empty()) { SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error, "unexpected characters at end of numeric expression '" + Trailer + "'"); - return true; + return nullptr; } - return false; + + uint64_t Value; + switch (Operator) { + case '+': + Value = LineNumber + Offset; + break; + case '-': + Value = LineNumber - Offset; + break; + default: + SM.PrintMessage(OpLoc, SourceMgr::DK_Error, + Twine("unsupported numeric operation '") + Twine(Operator) + + "'"); + return nullptr; + } + return Context->makeNumExpr(Value); } /// Parses the given string into the Pattern. @@ -194,33 +232,45 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix, continue; } - // Named RegEx matches. These are of two forms: [[foo:.*]] which matches .* - // (or some other regex) and assigns it to the FileCheck variable 'foo'. The - // second form is [[foo]] which is a reference to foo. The variable name - // itself must be of the form "[a-zA-Z_][0-9a-zA-Z_]*", otherwise we reject - // it. This is to catch some common errors. + // Pattern and numeric expression matches. Pattern expressions come in two + // forms: [[foo:.*]] and [[foo]]. The former matches .* (or some other + // regex) and assigns it to the FileCheck variable 'foo'. The latter + // substitutes foo's value. Numeric expressions start with a '#' sign after + // the double brackets and only have the substitution form. Pattern + // variables must satisfy the regular expression "[a-zA-Z_][0-9a-zA-Z_]*" + // to be valid, as this helps catch some common errors. Numeric expressions + // only support the @LINE pseudo numeric variable. if (PatternStr.startswith("[[")) { - StringRef MatchStr = PatternStr.substr(2); + StringRef UnparsedPatternStr = PatternStr.substr(2); // Find the closing bracket pair ending the match. End is going to be an // offset relative to the beginning of the match string. - size_t End = FindRegexVarEnd(MatchStr, SM); + size_t End = FindRegexVarEnd(UnparsedPatternStr, SM); + StringRef MatchStr = UnparsedPatternStr.substr(0, End); + bool IsNumExpr = MatchStr.consume_front("#"); + const char *RefTypeStr = + IsNumExpr ? "numeric expression" : "pattern variable"; if (End == StringRef::npos) { - SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()), - SourceMgr::DK_Error, - "invalid named regex reference, no ]] found"); + SM.PrintMessage( + SMLoc::getFromPointer(PatternStr.data()), SourceMgr::DK_Error, + Twine("Invalid ") + RefTypeStr + " reference, no ]] found"); return true; } - - MatchStr = MatchStr.substr(0, End); - PatternStr = PatternStr.substr(End + 4); + // Strip the subtitution we are parsing. End points to the start of the + // "]]" closing the expression so account for it in computing the index + // of the first unparsed character. + PatternStr = UnparsedPatternStr.substr(End + 2); size_t VarEndIdx = MatchStr.find(":"); - size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t"); - if (SpacePos != StringRef::npos) { - SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data() + SpacePos), - SourceMgr::DK_Error, "unexpected whitespace"); - return true; + if (IsNumExpr) + MatchStr = MatchStr.ltrim(SpaceChars); + else { + size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t"); + if (SpacePos != StringRef::npos) { + SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data() + SpacePos), + SourceMgr::DK_Error, "unexpected whitespace"); + return true; + } } // Get the regex name (e.g. "foo") and verify it is well formed. @@ -228,10 +278,13 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix, unsigned TrailIdx; if (parseVariable(MatchStr, IsPseudo, TrailIdx)) { SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()), - SourceMgr::DK_Error, "invalid name in named regex"); + SourceMgr::DK_Error, "invalid variable name"); return true; } + size_t SubstInsertIdx = RegExStr.size(); + FileCheckNumExpr *NumExpr; + StringRef Name = MatchStr.substr(0, TrailIdx); StringRef Trailer = MatchStr.substr(TrailIdx); bool IsVarDef = (VarEndIdx != StringRef::npos); @@ -239,30 +292,39 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix, if (IsVarDef && (IsPseudo || !Trailer.consume_front(":"))) { SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()), SourceMgr::DK_Error, - "invalid name in named regex definition"); + "invalid name in pattern variable definition"); return true; } if (!IsVarDef && IsPseudo) { - if (parseExpression(Name, Trailer, SM)) + NumExpr = parseNumericExpression(Name, Trailer, SM); + if (NumExpr == nullptr) return true; + IsNumExpr = true; } // Handle [[foo]]. if (!IsVarDef) { - // Handle variables that were defined earlier on the same line by - // emitting a backreference. - if (VariableDefs.find(Name) != VariableDefs.end()) { - unsigned VarParenNum = VariableDefs[Name]; - if (VarParenNum < 1 || VarParenNum > 9) { + // Handle use of pattern variables that were defined earlier on the + // same line by emitting a backreference. + if (!IsNumExpr && VariableDefs.find(Name) != VariableDefs.end()) { + unsigned CaptureParen = VariableDefs[Name]; + if (CaptureParen < 1 || CaptureParen > 9) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, "Can't back-reference more than 9 variables"); return true; } - AddBackrefToRegEx(VarParenNum); + AddBackrefToRegEx(CaptureParen); } else { - VariableUses.push_back(std::make_pair(MatchStr, RegExStr.size())); + // Handle use of pattern variables ([[<var>]]) defined in previous + // CHECK pattern or use of a numeric expression. + FileCheckPatternSubstitution Substitution = + IsNumExpr ? FileCheckPatternSubstitution(Context, MatchStr, + NumExpr, SubstInsertIdx) + : FileCheckPatternSubstitution(Context, MatchStr, + SubstInsertIdx); + Substitutions.push_back(Substitution); } continue; } @@ -315,19 +377,6 @@ void FileCheckPattern::AddBackrefToRegEx(unsigned BackrefNum) { RegExStr += Backref; } -/// Evaluates expression and stores the result to \p Value. -void FileCheckPattern::evaluateExpression(StringRef Expr, - std::string &Value) const { - Expr = Expr.substr(StringRef("@LINE").size()); - int Offset = 0; - if (!Expr.empty()) { - if (Expr[0] == '+') - Expr = Expr.substr(1); - Expr.getAsInteger(10, Offset); - } - Value = llvm::itostr(LineNumber + Offset); -} - /// Matches the pattern string against the input buffer \p Buffer /// /// This returns the position that is matched or npos if there is no match. If @@ -335,8 +384,8 @@ void FileCheckPattern::evaluateExpression(StringRef Expr, /// MatchLen. /// /// The GlobalVariableTable StringMap in the FileCheckPatternContext class -/// instance provides the current values of FileCheck variables and is updated -/// if this match defines new values. +/// instance provides the current values of FileCheck pattern variables and is +/// updated if this match defines new values. size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const { // If this is the EOF pattern, match it immediately. if (CheckTy == Check::CheckEOF) { @@ -356,30 +405,23 @@ size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const { // actual value. StringRef RegExToMatch = RegExStr; std::string TmpStr; - if (!VariableUses.empty()) { + if (!Substitutions.empty()) { TmpStr = RegExStr; - unsigned InsertOffset = 0; - for (const auto &VariableUse : VariableUses) { - std::string Value; - - if (VariableUse.first[0] == '@') { - evaluateExpression(VariableUse.first, Value); - } else { - llvm::Optional<StringRef> ValueRef = - Context->getVarValue(VariableUse.first); - // If the variable is undefined, return an error. - if (!ValueRef) - return StringRef::npos; - - // Look up the value and escape it so that we can put it into the regex. - Value += Regex::escape(*ValueRef); - } + size_t InsertOffset = 0; + // Substitute all pattern variables and numeric expressions whose value is + // known just now. Use of pattern variables defined on the same line are + // handled by back-references. + for (const auto &Substitution : Substitutions) { + // Substitute and check for failure (e.g. use of undefined variable). + llvm::Optional<std::string> Value = Substitution.getResult(); + if (!Value) + return StringRef::npos; // Plop it into the regex at the adjusted offset. - TmpStr.insert(TmpStr.begin() + VariableUse.second + InsertOffset, - Value.begin(), Value.end()); - InsertOffset += Value.size(); + TmpStr.insert(TmpStr.begin() + Substitution.getIndex() + InsertOffset, + Value->begin(), Value->end()); + InsertOffset += Value->size(); } // Match the newly constructed regex. @@ -394,7 +436,7 @@ size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const { assert(!MatchInfo.empty() && "Didn't get any match"); StringRef FullMatch = MatchInfo[0]; - // If this defines any variables, remember their values. + // If this defines any pattern variables, remember their values. for (const auto &VariableDef : VariableDefs) { assert(VariableDef.second < MatchInfo.size() && "Internal paren error"); Context->GlobalVariableTable[VariableDef.first] = @@ -429,33 +471,33 @@ unsigned FileCheckPattern::computeMatchDistance(StringRef Buffer) const { return BufferPrefix.edit_distance(ExampleString); } -void FileCheckPattern::printVariableUses(const SourceMgr &SM, StringRef Buffer, - SMRange MatchRange) const { - // If this was a regular expression using variables, print the current - // variable values. - if (!VariableUses.empty()) { - for (const auto &VariableUse : VariableUses) { +void FileCheckPattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer, + SMRange MatchRange) const { + // Print what we know about substitutions. This covers both uses of pattern + // variables and numeric subsitutions. + if (!Substitutions.empty()) { + for (const auto &Substitution : Substitutions) { SmallString<256> Msg; raw_svector_ostream OS(Msg); - StringRef Var = VariableUse.first; - if (Var[0] == '@') { - std::string Value; - evaluateExpression(Var, Value); - OS << "with expression \""; - OS.write_escaped(Var) << "\" equal to \""; - OS.write_escaped(Value) << "\""; + bool IsNumExpr = Substitution.isNumExpr(); + llvm::Optional<std::string> MatchedValue = Substitution.getResult(); + + // Substitution failed or is not known at match time, print the undefined + // variable it uses. + if (!MatchedValue) { + StringRef UndefVarName = Substitution.getUndefVarName(); + if (UndefVarName.empty()) + continue; + OS << "uses undefined variable \""; + OS.write_escaped(UndefVarName) << "\""; } else { - llvm::Optional<StringRef> VarValue = Context->getVarValue(Var); - - // Check for undefined variable references. - if (!VarValue) { - OS << "uses undefined variable \""; - OS.write_escaped(Var) << "\""; - } else { + // Substitution succeeded. Print substituted value. + if (IsNumExpr) + OS << "with numeric expression \""; + else OS << "with variable \""; - OS.write_escaped(Var) << "\" equal to \""; - OS.write_escaped(*VarValue) << "\""; - } + OS.write_escaped(Substitution.getFromString()) << "\" equal to \""; + OS.write_escaped(*MatchedValue) << "\""; } if (MatchRange.isValid()) @@ -534,7 +576,7 @@ void FileCheckPattern::printFuzzyMatch( } llvm::Optional<StringRef> -FileCheckPatternContext::getVarValue(StringRef VarName) { +FileCheckPatternContext::getPatternVarValue(StringRef VarName) { auto VarIter = GlobalVariableTable.find(VarName); if (VarIter == GlobalVariableTable.end()) return llvm::None; @@ -542,6 +584,12 @@ FileCheckPatternContext::getVarValue(StringRef VarName) { return VarIter->second; } +template <class... Types> +FileCheckNumExpr *FileCheckPatternContext::makeNumExpr(Types... Args) { + NumExprs.emplace_back(new FileCheckNumExpr(Args...)); + return NumExprs.back().get(); +} + /// Finds the closing sequence of a regex variable usage or definition. /// /// \p Str has to point in the beginning of the definition (right after the @@ -998,7 +1046,7 @@ static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM, Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message); SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here", {MatchRange}); - Pat.printVariableUses(SM, Buffer, MatchRange); + Pat.printSubstitutions(SM, Buffer, MatchRange); } static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM, @@ -1049,7 +1097,7 @@ static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note, "scanning from here"); // Allow the pattern to print additional information if desired. - Pat.printVariableUses(SM, Buffer); + Pat.printSubstitutions(SM, Buffer); if (ExpectedMatch) Pat.printFuzzyMatch(SM, Buffer, Diags); |