diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst --- a/llvm/docs/CommandGuide/FileCheck.rst +++ b/llvm/docs/CommandGuide/FileCheck.rst @@ -910,8 +910,16 @@ The ``--enable-var-scope`` option has the same effect on numeric variables as on string variables. -Important note: In its current implementation, an expression cannot use a -numeric variable defined earlier in the same CHECK directive. +Important note: In its current implementation, an expression using a variable +defined on the same line can fail to match even though an input line satisfies +it. This happens when there is more than one possible match in the line when +ignoring such expressions and considering only their matching format +(ie. matching an unsigned number with [[:digit:]]+ and the first match does not +satisfy the constraint. This is due to those expressions not being able to be +expressed at the regular expression level and the regular expression engine not +offering the possibility of getting all possible matches for a given regular +expression. To avoid false positive, using a variable whose value is not known +at match time is forbidden for ``CHECK-NOT:`` directives. FileCheck Pseudo Numeric Variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp --- a/llvm/lib/FileCheck/FileCheck.cpp +++ b/llvm/lib/FileCheck/FileCheck.cpp @@ -362,6 +362,29 @@ return LeftOperand; } +Expected Expression::eval(bool SetTentative) const { + assert(AST != nullptr && "Evaluating empty expression"); + Expected EvaluatedValue = AST->eval(); + if (!EvaluatedValue) + return EvaluatedValue.takeError(); + if (SetTentative && DefinedNumericVariable != nullptr) + DefinedNumericVariable->setValue(*EvaluatedValue, true); + return *EvaluatedValue; +} + +bool Expression::verifyConstraint(ExpressionValue MatchedValue) const { + // Nothing to verify: variable definition with empty expression. + if (AST == nullptr) + return false; + + Expected EvaluatedValue = AST->eval(); + if (!EvaluatedValue) { + consumeError(EvaluatedValue.takeError()); + return true; + } + return *EvaluatedValue != MatchedValue; +} + Expected NumericVariableUse::eval() const { Optional Value = Variable->getValue(); if (Value) @@ -385,7 +408,11 @@ return std::move(Err); } - return EvalBinop(*LeftOp, *RightOp); + Expected ResultValue = EvalBinop(*LeftOp, *RightOp); + if (!ResultValue) + return ResultValue; + + return ResultValue; } Expected @@ -416,10 +443,13 @@ } Expected NumericSubstitution::getResult() const { - assert(ExpressionPointer->getAST() != nullptr && - "Substituting empty expression"); + // Evaluate expression and store result tentatively in any variable it may + // define so that subsequent expressions in the directive can use that + // variable. This allows them to be substituted with the right value + // accordingly rather than match anything and verify later, which introduce + // potential false negative match. Expected EvaluatedValue = - ExpressionPointer->getAST()->eval(); + ExpressionPointer->eval(/*SetTentative=*/true); if (!EvaluatedValue) return EvaluatedValue.takeError(); ExpressionFormat Format = ExpressionPointer->getFormat(); @@ -506,20 +536,20 @@ NumericVariable *DefinedNumericVariable; auto VarTableIter = Context->GlobalNumericVariableTable.find(Name); if (VarTableIter != Context->GlobalNumericVariableTable.end()) { - DefinedNumericVariable = VarTableIter->second; + DefinedNumericVariable = VarTableIter->second.first; if (DefinedNumericVariable->getImplicitFormat() != ImplicitFormat) return ErrorDiagnostic::get( SM, Expr, "format different from previous variable definition"); } else - DefinedNumericVariable = - Context->makeNumericVariable(Name, ImplicitFormat, LineNumber); + DefinedNumericVariable = Context->makeNumericVariable(Name, ImplicitFormat); return DefinedNumericVariable; } Expected> Pattern::parseNumericVariableUse( - StringRef Name, bool IsPseudo, Optional LineNumber, - FileCheckPatternContext *Context, const SourceMgr &SM) { + StringRef Name, bool IsPseudo, bool AllowSameUse, bool &MatchTimeKnown, + Optional LineNumber, FileCheckPatternContext *Context, + const SourceMgr &SM) { if (IsPseudo && !Name.equals("@LINE")) return ErrorDiagnostic::get( SM, Name, "invalid pseudo numeric variable '" + Name + "'"); @@ -534,33 +564,39 @@ // in printNoMatch() after failing to match. auto VarTableIter = Context->GlobalNumericVariableTable.find(Name); NumericVariable *NumericVariable; - if (VarTableIter != Context->GlobalNumericVariableTable.end()) - NumericVariable = VarTableIter->second; - else { + bool MatchTimeKnownDefinition = false; + Optional DefLineNumber = None; + if (VarTableIter != Context->GlobalNumericVariableTable.end()) { + NumericVariable = VarTableIter->second.first; + DefLineNumber = VarTableIter->second.second; + MatchTimeKnownDefinition = + Context->NumericVariableDefinitionTable.find(Name)->second; + } else { NumericVariable = Context->makeNumericVariable( Name, ExpressionFormat(ExpressionFormat::Kind::Unsigned)); - Context->GlobalNumericVariableTable[Name] = NumericVariable; + Context->GlobalNumericVariableTable[Name] = {NumericVariable, + DefLineNumber}; } - Optional DefLineNumber = NumericVariable->getDefLineNumber(); - if (DefLineNumber && LineNumber && *DefLineNumber == *LineNumber) - return ErrorDiagnostic::get( - SM, Name, - "numeric variable '" + Name + - "' defined earlier in the same CHECK directive"); + MatchTimeKnown = !DefLineNumber || !LineNumber || + *DefLineNumber != *LineNumber || MatchTimeKnownDefinition; + if (!MatchTimeKnown && !AllowSameUse) + return ErrorDiagnostic::get(SM, Name, + "unknown variable value at match time"); return std::make_unique(Name, NumericVariable); } Expected> Pattern::parseNumericOperand( StringRef &Expr, AllowedOperand AO, bool MaybeInvalidConstraint, - Optional LineNumber, FileCheckPatternContext *Context, - const SourceMgr &SM) { + bool AllowSameUse, bool &MatchTimeKnown, Optional LineNumber, + FileCheckPatternContext *Context, const SourceMgr &SM) { if (Expr.startswith("(")) { if (AO != AllowedOperand::Any) return ErrorDiagnostic::get( SM, Expr, "parenthesized expression not permitted here"); - return parseParenExpr(Expr, LineNumber, Context, SM); + return parseParenExpr(Expr, AllowSameUse, MatchTimeKnown, LineNumber, + Context, SM); } if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) { @@ -574,13 +610,13 @@ return ErrorDiagnostic::get(SM, ParseVarResult->Name, "unexpected function call"); - return parseCallExpr(Expr, ParseVarResult->Name, LineNumber, Context, - SM); + return parseCallExpr(Expr, ParseVarResult->Name, AllowSameUse, + MatchTimeKnown, LineNumber, Context, SM); } return parseNumericVariableUse(ParseVarResult->Name, - ParseVarResult->IsPseudo, LineNumber, - Context, SM); + ParseVarResult->IsPseudo, AllowSameUse, + MatchTimeKnown, LineNumber, Context, SM); } if (AO == AllowedOperand::LineVar) @@ -593,6 +629,7 @@ int64_t SignedLiteralValue; uint64_t UnsignedLiteralValue; StringRef SaveExpr = Expr; + MatchTimeKnown = true; // Accept both signed and unsigned literal, default to signed literal. if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0, UnsignedLiteralValue)) @@ -611,7 +648,8 @@ } Expected> -Pattern::parseParenExpr(StringRef &Expr, Optional LineNumber, +Pattern::parseParenExpr(StringRef &Expr, bool AllowSameUse, + bool &MatchTimeKnown, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM) { Expr = Expr.ltrim(SpaceChars); assert(Expr.startswith("(")); @@ -624,13 +662,14 @@ // Note: parseNumericOperand handles nested opening parentheses. Expected> SubExprResult = parseNumericOperand( - Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber, - Context, SM); + Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, AllowSameUse, + MatchTimeKnown, LineNumber, Context, SM); Expr = Expr.ltrim(SpaceChars); while (SubExprResult && !Expr.empty() && !Expr.startswith(")")) { StringRef OrigExpr = Expr; - SubExprResult = parseBinop(OrigExpr, Expr, std::move(*SubExprResult), false, - LineNumber, Context, SM); + SubExprResult = + parseBinop(OrigExpr, Expr, std::move(*SubExprResult), false, + AllowSameUse, MatchTimeKnown, LineNumber, Context, SM); Expr = Expr.ltrim(SpaceChars); } if (!SubExprResult) @@ -646,7 +685,8 @@ Expected> Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr, std::unique_ptr LeftOp, - bool IsLegacyLineExpr, Optional LineNumber, + bool IsLegacyLineExpr, bool AllowSameUse, + bool &MatchTimeKnown, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM) { RemainingExpr = RemainingExpr.ltrim(SpaceChars); if (RemainingExpr.empty()) @@ -677,20 +717,22 @@ // The second operand in a legacy @LINE expression is always a literal. AllowedOperand AO = IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any; - Expected> RightOpResult = - parseNumericOperand(RemainingExpr, AO, /*MaybeInvalidConstraint=*/false, - LineNumber, Context, SM); + bool MatchTimeKnownRightOp; + Expected> RightOpResult = parseNumericOperand( + RemainingExpr, AO, /*MaybeInvalidConstraint=*/false, AllowSameUse, + MatchTimeKnownRightOp, LineNumber, Context, SM); if (!RightOpResult) return RightOpResult; Expr = Expr.drop_back(RemainingExpr.size()); + MatchTimeKnown = MatchTimeKnown && MatchTimeKnownRightOp; return std::make_unique(Expr, EvalBinop, std::move(LeftOp), std::move(*RightOpResult)); } Expected> -Pattern::parseCallExpr(StringRef &Expr, StringRef FuncName, - Optional LineNumber, +Pattern::parseCallExpr(StringRef &Expr, StringRef FuncName, bool AllowSameUse, + bool &MatchTimeKnown, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM) { Expr = Expr.ltrim(SpaceChars); assert(Expr.startswith("(")); @@ -720,8 +762,8 @@ // Parse the argument, which is an arbitary expression. StringRef OuterBinOpExpr = Expr; Expected> Arg = parseNumericOperand( - Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber, - Context, SM); + Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, + AllowSameUse, MatchTimeKnown, LineNumber, Context, SM); while (Arg && !Expr.empty()) { Expr = Expr.ltrim(SpaceChars); // Have we reached an argument terminator? @@ -729,8 +771,8 @@ break; // Arg = Arg - Arg = parseBinop(OuterBinOpExpr, Expr, std::move(*Arg), false, LineNumber, - Context, SM); + Arg = parseBinop(OuterBinOpExpr, Expr, std::move(*Arg), false, + AllowSameUse, MatchTimeKnown, LineNumber, Context, SM); } // Prefer an expression error over a generic invalid argument message. @@ -764,13 +806,13 @@ Twine(NumArgs) + " given"); } -Expected> Pattern::parseNumericSubstitutionBlock( - StringRef Expr, Optional &DefinedNumericVariable, - bool IsLegacyLineExpr, Optional LineNumber, +Expected Pattern::parseNumericSubstitutionBlock( + StringRef Expr, NumericVariable *&DefinedNumericVariable, + bool IsLegacyLineExpr, bool AllowSameUse, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM) { std::unique_ptr ExpressionASTPointer = nullptr; StringRef DefExpr = StringRef(); - DefinedNumericVariable = None; + DefinedNumericVariable = nullptr; ExpressionFormat ExplicitFormat = ExpressionFormat(); unsigned Precision = 0; @@ -852,7 +894,10 @@ // Parse the expression itself. Expr = Expr.ltrim(SpaceChars); + bool MatchTimeKnown = false; if (Expr.empty()) { + if (DefEnd != StringRef::npos) + MatchTimeKnown = true; if (HasParsedValidConstraint) return ErrorDiagnostic::get( SM, Expr, "empty numeric expression should not have a constraint"); @@ -863,11 +908,13 @@ // pseudo variable. AllowedOperand AO = IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any; - Expected> ParseResult = parseNumericOperand( - Expr, AO, !HasParsedValidConstraint, LineNumber, Context, SM); + Expected> ParseResult = + parseNumericOperand(Expr, AO, !HasParsedValidConstraint, AllowSameUse, + MatchTimeKnown, LineNumber, Context, SM); while (ParseResult && !Expr.empty()) { ParseResult = parseBinop(OuterBinOpExpr, Expr, std::move(*ParseResult), - IsLegacyLineExpr, LineNumber, Context, SM); + IsLegacyLineExpr, AllowSameUse, MatchTimeKnown, + LineNumber, Context, SM); // Legacy @LINE expressions only allow 2 operands. if (ParseResult && IsLegacyLineExpr && !Expr.empty()) return ErrorDiagnostic::get( @@ -896,21 +943,22 @@ if (!Format) Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned, Precision); - std::unique_ptr ExpressionPointer = - std::make_unique(std::move(ExpressionASTPointer), Format); - // Parse the numeric variable definition. if (DefEnd != StringRef::npos) { DefExpr = DefExpr.ltrim(SpaceChars); Expected ParseResult = parseNumericVariableDefinition( - DefExpr, Context, LineNumber, ExpressionPointer->getFormat(), SM); + DefExpr, Context, LineNumber, Format, SM); if (!ParseResult) return ParseResult.takeError(); DefinedNumericVariable = *ParseResult; } - return std::move(ExpressionPointer); + Expression *ExpressionPointer = + Context->makeExpression(std::move(ExpressionASTPointer), Format, + MatchTimeKnown, DefinedNumericVariable); + + return ExpressionPointer; } bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix, @@ -1029,6 +1077,7 @@ bool IsDefinition = false; bool SubstNeeded = false; + bool CaptureNeeded = false; // Whether the substitution block is a legacy use of @LINE with string // substitution block syntax. bool IsLegacyLineExpr = false; @@ -1059,7 +1108,7 @@ StringRef Name = ParseVarResult->Name; bool IsPseudo = ParseVarResult->IsPseudo; - IsDefinition = (VarEndIdx != StringRef::npos); + CaptureNeeded = IsDefinition = (VarEndIdx != StringRef::npos); SubstNeeded = !IsDefinition; if (IsDefinition) { if ((IsPseudo || !MatchStr.consume_front(":"))) { @@ -1097,23 +1146,23 @@ } // Parse numeric substitution block. - std::unique_ptr ExpressionPointer; - Optional DefinedNumericVariable; + Expression *ExpressionPointer; + NumericVariable *DefinedNumericVariable = nullptr; if (IsNumBlock) { - Expected> ParseResult = - parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, - IsLegacyLineExpr, LineNumber, Context, - SM); + Expected ParseResult = parseNumericSubstitutionBlock( + MatchStr, DefinedNumericVariable, IsLegacyLineExpr, + CheckTy != Check::CheckNot, LineNumber, Context, SM); if (!ParseResult) { logAllUnhandledErrors(ParseResult.takeError(), errs()); return true; } - ExpressionPointer = std::move(*ParseResult); - SubstNeeded = ExpressionPointer->getAST() != nullptr; + ExpressionPointer = *ParseResult; + SubstNeeded = ExpressionPointer->isMatchTimeKnown(); if (DefinedNumericVariable) { IsDefinition = true; - DefName = (*DefinedNumericVariable)->getName(); + DefName = DefinedNumericVariable->getName(); } + CaptureNeeded = IsDefinition || !ExpressionPointer->isMatchTimeKnown(); if (SubstNeeded) SubstStr = MatchStr; else { @@ -1123,21 +1172,24 @@ } } - // Handle variable definition: [[:(...)]] and [[#(...):(...)]]. - if (IsDefinition) { + // Handle variable definition and expression needing verifying. + if (CaptureNeeded) { RegExStr += '('; ++SubstInsertIdx; if (IsNumBlock) { - NumericVariableMatch NumericVariableDefinition = { - *DefinedNumericVariable, CurParen}; - NumericVariableDefs[DefName] = NumericVariableDefinition; + NumericExpressionMatch CapturedExpression = { + ExpressionPointer, DefinedNumericVariable, CurParen}; + CapturedExpressions.push_back(CapturedExpression); + // TODO: reword // This store is done here rather than in match() to allow // parseNumericVariableUse() to get the pointer to the class instance // of the right variable definition corresponding to a given numeric // variable use. - Context->GlobalNumericVariableTable[DefName] = - *DefinedNumericVariable; + Context->GlobalNumericVariableTable[DefName] = { + DefinedNumericVariable, LineNumber}; + Context->NumericVariableDefinitionTable[DefName] = + ExpressionPointer->isMatchTimeKnown(); } else { VariableDefs[DefName] = CurParen; // Mark string variable as defined to detect collisions between @@ -1155,7 +1207,7 @@ if (!MatchRegexp.empty() && AddRegExToRegEx(MatchRegexp, CurParen, SM)) return true; - if (IsDefinition) + if (CaptureNeeded) RegExStr += ')'; // Handle substitutions: [[foo]] and [[#]]. @@ -1178,7 +1230,7 @@ Substitution *Substitution = IsNumBlock ? Context->makeNumericSubstitution( - SubstStr, std::move(ExpressionPointer), SubstInsertIdx) + SubstStr, ExpressionPointer, SubstInsertIdx) : Context->makeStringSubstitution(SubstStr, SubstInsertIdx); Substitutions.push_back(Substitution); } @@ -1246,12 +1298,31 @@ if (!Substitutions.empty()) { TmpStr = RegExStr; if (LineNumber) - Context->LineVariable->setValue(ExpressionValue(*LineNumber)); + Context->LineVariable->setValue(ExpressionValue(*LineNumber), + /*Tentative=*/false); size_t InsertOffset = 0; // Substitute all string variables and expressions whose values are only // now known. Use of string variables defined on the same line are handled - // by back-references. + // by back-references. For expressions using variable with empty expression + // defined on the same line, RegExStr is filled with a suitable wildcard + // pattern and the constraint is then verified against the matched value. + // + // Known issue: Due to the constraints for expressions using variable + // variable defined in earlier line not being expressed at the regular + // expression level, the wrong values might be matched, leading to a + // constraint check failure. An example of this is what happens when + // checking the line "1 3 4" against the directive + // "CHECK: [[#N:]] [[#N+1]]" which gets substituted by + // "[[:digit:]]+ [[:digit:]]+". This would match 1 3 which then fails the + // constraint checks. + // + // One possible solution would be to augment the regular expression engine + // to be able to return all matches for a given pattern. FileCheck could + // then test all possibility and fail the check only if none of them + // satisfy all expressions. This would work both for expression using a + // numeric variable defined on the same line as well as expressions with a + // comparison rather than equality constraint. Error Errs = Error::success(); for (const auto &Substitution : Substitutions) { // Substitute and check for failure (e.g. use of undefined variable). @@ -1288,48 +1359,90 @@ RegExToMatch = TmpStr; } + bool AllExpressionsSatisfied; SmallVector MatchInfo; + Match TheMatch; unsigned int Flags = Regex::Newline; if (IgnoreCase) Flags |= Regex::IgnoreCase; - if (!Regex(RegExToMatch, Flags).match(Buffer, &MatchInfo)) - return make_error(); + do { + AllExpressionsSatisfied = true; + if (!Regex(RegExToMatch, Flags).match(Buffer, &MatchInfo)) + return make_error(); - // Successful regex match. - assert(!MatchInfo.empty() && "Didn't get any match"); - StringRef FullMatch = MatchInfo[0]; + // Successful regex match. + assert(!MatchInfo.empty() && "Didn't get any match"); + // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after + // the required preceding newline, which is consumed by the pattern in the + // case of CHECK-EMPTY but not CHECK-NEXT. + size_t MatchStartSkip = CheckTy == Check::CheckEmpty; + StringRef FullMatch = MatchInfo[0]; + TheMatch.Pos = FullMatch.data() - Buffer.data() + MatchStartSkip; + TheMatch.Len = FullMatch.size() - MatchStartSkip; + + // If this defines any string variables, remember their values. This is + // safe to do even if this iteration fails to verify all expressions + // because use of string variable defined on the same lined is matched by + // using back-reference. Only variables defined in earlier lines are + // matched by their value. + for (const auto &VariableDef : VariableDefs) { + assert(VariableDef.second < MatchInfo.size() && "Internal paren error"); + Context->GlobalVariableTable[VariableDef.first] = + MatchInfo[VariableDef.second]; + } - // If this defines any string variables, remember their values. - for (const auto &VariableDef : VariableDefs) { - assert(VariableDef.second < MatchInfo.size() && "Internal paren error"); - Context->GlobalVariableTable[VariableDef.first] = - MatchInfo[VariableDef.second]; - } + // Clear value tentatively set in previous iteration. + for (const auto &CapturedExpression : CapturedExpressions) { + NumericVariable *DefinedNumericVariable = + CapturedExpression.DefinedNumericVariable; + if (DefinedNumericVariable != nullptr) + DefinedNumericVariable->clearTentativeValue(); + } - // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after - // the required preceding newline, which is consumed by the pattern in the - // case of CHECK-EMPTY but not CHECK-NEXT. - size_t MatchStartSkip = CheckTy == Check::CheckEmpty; - Match TheMatch; - TheMatch.Pos = FullMatch.data() - Buffer.data() + MatchStartSkip; - TheMatch.Len = FullMatch.size() - MatchStartSkip; - - // If this defines any numeric variables, remember their values. - for (const auto &NumericVariableDef : NumericVariableDefs) { - const NumericVariableMatch &NumericVariableMatch = - NumericVariableDef.getValue(); - unsigned CaptureParenGroup = NumericVariableMatch.CaptureParenGroup; - assert(CaptureParenGroup < MatchInfo.size() && "Internal paren error"); - NumericVariable *DefinedNumericVariable = - NumericVariableMatch.DefinedNumericVariable; + // Check matched values satisfy their corresponding expression and store + // them in the numeric variables being defined if any. + for (const auto &CapturedExpression : CapturedExpressions) { + unsigned CaptureParenGroup = CapturedExpression.CaptureParenGroup; + assert(CaptureParenGroup < MatchInfo.size() && "Internal paren error"); + Expression *Expr = CapturedExpression.Expr; + NumericVariable *DefinedNumericVariable = + CapturedExpression.DefinedNumericVariable; + + StringRef MatchedValue = MatchInfo[CaptureParenGroup]; + ExpressionFormat Format = Expr->getFormat(); + Expected Value = + Format.valueFromStringRepr(MatchedValue, SM); + + if (!Value) + return MatchResult(TheMatch, Value.takeError()); + + // Now verify the constraint of the expression. + if (Expr->verifyConstraint(*Value)) { + AllExpressionsSatisfied = false; + // Try matching again from the line following the one matched. There + // might be other possible matches in the current line but we have no + // way to ask for them to the regular expression engine. However this + // hazard can be alleviated if the user write a CHECK pattern that uses + // expression for all numeric values that the line to match contains. + Buffer = Buffer.substr(Buffer.find_first_of('\n')).substr(1); + break; + } + + // If this defines a numeric variables, mark the verified value as + // tentative and store it. + if (DefinedNumericVariable != nullptr) { + DefinedNumericVariable->setValue(*Value, /*Tentative=*/true, + MatchedValue); + } + } + } while (!AllExpressionsSatisfied && !Buffer.empty()); - StringRef MatchedValue = MatchInfo[CaptureParenGroup]; - ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat(); - Expected Value = - Format.valueFromStringRepr(MatchedValue, SM); - if (!Value) - return MatchResult(TheMatch, Value.takeError()); - DefinedNumericVariable->setValue(*Value, MatchedValue); + // All constraints satisfied, make variable definitions definitive. + for (const auto &CapturedExpression : CapturedExpressions) { + NumericVariable *DefinedNumericVariable = + CapturedExpression.DefinedNumericVariable; + if (DefinedNumericVariable != nullptr) + DefinedNumericVariable->commitValue(); } return MatchResult(TheMatch, Error::success()); @@ -1356,7 +1469,9 @@ SMRange Range, FileCheckDiag::MatchType MatchTy, std::vector *Diags) const { - // Print what we know about substitutions. + // Print what we know about substitutions. This covers both string and + // numeric substitutions as long as they only use variables whose value is + // known at match time. if (!Substitutions.empty()) { for (const auto &Substitution : Substitutions) { SmallString<256> Msg; @@ -1369,6 +1484,7 @@ continue; } + // Substitution succeeded and expression satisfied its constrained. OS << "with \""; OS.write_escaped(Substitution->getFromString()) << "\" equal to \""; OS.write_escaped(*MatchedValue) << "\""; @@ -1389,7 +1505,7 @@ void Pattern::printVariableDefs(const SourceMgr &SM, FileCheckDiag::MatchType MatchTy, std::vector *Diags) const { - if (VariableDefs.empty() && NumericVariableDefs.empty()) + if (VariableDefs.empty() && CapturedExpressions.empty()) return; // Build list of variable captures. struct VarCapture { @@ -1406,11 +1522,14 @@ VC.Range = SMRange(Start, End); VarCaptures.push_back(VC); } - for (const auto &VariableDef : NumericVariableDefs) { + for (const auto &CapturedExpression : CapturedExpressions) { VarCapture VC; - VC.Name = VariableDef.getKey(); - Optional StrValue = - VariableDef.getValue().DefinedNumericVariable->getStringValue(); + NumericVariable *DefinedNumericVariable = + CapturedExpression.DefinedNumericVariable; + if (DefinedNumericVariable == nullptr) + continue; + VC.Name = DefinedNumericVariable->getName(); + Optional StrValue = DefinedNumericVariable->getStringValue(); if (!StrValue) continue; SMLoc Start = SMLoc::getFromPointer(StrValue->data()); @@ -1513,12 +1632,22 @@ return VarIter->second; } -template -NumericVariable *FileCheckPatternContext::makeNumericVariable(Types... args) { - NumericVariables.push_back(std::make_unique(args...)); +NumericVariable * +FileCheckPatternContext::makeNumericVariable(StringRef Name, + ExpressionFormat ImplicitFormat) { + NumericVariables.push_back( + std::make_unique(Name, ImplicitFormat)); return NumericVariables.back().get(); } +Expression *FileCheckPatternContext::makeExpression( + std::unique_ptr AST, ExpressionFormat Format, + bool MatchTimeKnown, NumericVariable *DefinedNumericVariable) { + Expressions.push_back(std::make_unique( + std::move(AST), Format, MatchTimeKnown, DefinedNumericVariable)); + return Expressions.back().get(); +} + Substitution * FileCheckPatternContext::makeStringSubstitution(StringRef VarName, size_t InsertIdx) { @@ -1528,10 +1657,9 @@ } Substitution *FileCheckPatternContext::makeNumericSubstitution( - StringRef ExpressionStr, std::unique_ptr Expression, - size_t InsertIdx) { + StringRef ExpressionStr, Expression *Expression, size_t InsertIdx) { Substitutions.push_back(std::make_unique( - this, ExpressionStr, std::move(Expression), InsertIdx)); + this, ExpressionStr, Expression, InsertIdx)); return Substitutions.back().get(); } @@ -1846,7 +1974,8 @@ StringRef LineName = "@LINE"; LineVariable = makeNumericVariable( LineName, ExpressionFormat(ExpressionFormat::Kind::Unsigned)); - GlobalNumericVariableTable[LineName] = LineVariable; + GlobalNumericVariableTable[LineName] = {LineVariable, None}; + NumericVariableDefinitionTable[LineName] = true; } FileCheck::FileCheck(FileCheckRequest Req) @@ -2672,31 +2801,34 @@ // Now parse the definition both to check that the syntax is correct and // to create the necessary class instance. StringRef CmdlineDefExpr = CmdlineDef.substr(1); - Optional DefinedNumericVariable; - Expected> ExpressionResult = + NumericVariable *DefinedNumericVariable; + Expected ExpressionResult = Pattern::parseNumericSubstitutionBlock( - CmdlineDefExpr, DefinedNumericVariable, false, None, this, SM); + CmdlineDefExpr, DefinedNumericVariable, + /*IsLegacyLineExpr=*/false, /*AllowSameUse=*/false, None, this, + SM); if (!ExpressionResult) { Errs = joinErrors(std::move(Errs), ExpressionResult.takeError()); continue; } - std::unique_ptr Expression = std::move(*ExpressionResult); + Expression *Expr = *ExpressionResult; // Now evaluate the expression whose value this variable should be set // to, since the expression of a command-line variable definition should // only use variables defined earlier on the command-line. If not, this // is an error and we report it. - Expected Value = Expression->getAST()->eval(); + Expected Value = Expr->eval(/*SetTentative=*/false); if (!Value) { Errs = joinErrors(std::move(Errs), Value.takeError()); continue; } - assert(DefinedNumericVariable && "No variable defined"); - (*DefinedNumericVariable)->setValue(*Value); + assert(DefinedNumericVariable != nullptr && "No variable defined"); + DefinedNumericVariable->setValue(*Value, /*Tentative=*/false); // Record this variable definition. - GlobalNumericVariableTable[(*DefinedNumericVariable)->getName()] = - *DefinedNumericVariable; + StringRef DefName = DefinedNumericVariable->getName(); + GlobalNumericVariableTable[DefName] = {DefinedNumericVariable, None}; + NumericVariableDefinitionTable[DefName] = true; } else { // String variable definition. std::pair CmdlineNameVal = CmdlineDef.split('='); @@ -2757,10 +2889,10 @@ // also mark the variable for removal from GlobalNumericVariableTable since // this is what defineCmdlineVariables checks to decide that no global // variable has been defined. - for (const auto &Var : GlobalNumericVariableTable) - if (Var.first()[0] != '$') { - Var.getValue()->clearValue(); - LocalNumericVars.push_back(Var.first()); + for (const auto &VarLineNum : GlobalNumericVariableTable) + if (VarLineNum.first()[0] != '$') { + VarLineNum.getValue().first->clearValue(); + LocalNumericVars.push_back(VarLineNum.first()); } for (const auto &Var : LocalPatternVars) diff --git a/llvm/lib/FileCheck/FileCheckImpl.h b/llvm/lib/FileCheck/FileCheckImpl.h --- a/llvm/lib/FileCheck/FileCheckImpl.h +++ b/llvm/lib/FileCheck/FileCheckImpl.h @@ -233,6 +233,8 @@ } }; +class NumericVariable; + /// Class representing an expression and its matching format. class Expression { private: @@ -242,17 +244,36 @@ /// Format to use (e.g. hex upper case letters) when matching the value. ExpressionFormat Format; -public: - /// Generic constructor for an expression represented by the given \p AST and - /// whose matching format is \p Format. - Expression(std::unique_ptr AST, ExpressionFormat Format) - : AST(std::move(AST)), Format(Format) {} + /// Whether the value of this expression can be known at match time + /// (i.e. when substituting it in the regular expression). If false, the + /// value is known after matching has happened. + bool MatchTimeKnown; - /// \returns pointer to AST of the expression. Pointer is guaranteed to be - /// valid as long as this object is. - ExpressionAST *getAST() const { return AST.get(); } + /// Numeric variable defined by this expression, if any. + NumericVariable *DefinedNumericVariable; + +public: + /// Generic constructor for an expression represented by the given \p AST, + /// whose matching format is \p Format and which is known at match time if + /// \p MatchTimeKnown is true. + Expression(std::unique_ptr AST, ExpressionFormat Format, + bool MatchTimeKnown, NumericVariable *DefinedNumericVariable) + : AST(std::move(AST)), Format(Format), MatchTimeKnown(MatchTimeKnown), + DefinedNumericVariable(DefinedNumericVariable) {} ExpressionFormat getFormat() const { return Format; } + + bool isMatchTimeKnown() const { return MatchTimeKnown; } + + /// Evaluates and \returns the value of this assumed non-null expression or + /// an error if evaluation fails. It should only be invoked on expression + /// with a non null AST. + Expected eval(bool SetTentative) const; + + /// Verifies that the matched value in \p MatchedValue satisfies the + /// constraint expressed by this expression. \returns true if constraint is + /// not satisfied. + bool verifyConstraint(ExpressionValue MatchedValue) const; }; /// Class representing a numeric variable and its associated current value. @@ -268,23 +289,22 @@ /// Value of numeric variable, if defined, or None otherwise. Optional Value; + /// Tentative value of numeric variable when the constraints of the + /// expression where it is defined have not been verified yet. + Optional TentativeValue; + /// The input buffer's string from which Value was parsed, or None. See /// comments on getStringValue for a discussion of the None case. Optional StrValue; - /// Line number where this variable is defined, or None if defined before - /// input is parsed. Used to determine whether a variable is defined on the - /// same line as a given use. - Optional DefLineNumber; + /// The input buffer's string from which TentativeValue was parsed, or None. + Optional StrTentativeValue; public: - /// Constructor for a variable \p Name with implicit format \p ImplicitFormat - /// defined at line \p DefLineNumber or defined before input is parsed if - /// \p DefLineNumber is None. - explicit NumericVariable(StringRef Name, ExpressionFormat ImplicitFormat, - Optional DefLineNumber = None) - : Name(Name), ImplicitFormat(ImplicitFormat), - DefLineNumber(DefLineNumber) {} + /// Constructor for a variable \p Name with implicit format + /// \p ImplicitFormat. + explicit NumericVariable(StringRef Name, ExpressionFormat ImplicitFormat) + : Name(Name), ImplicitFormat(ImplicitFormat) {} /// \returns name of this numeric variable. StringRef getName() const { return Name; } @@ -292,23 +312,47 @@ /// \returns implicit format of this numeric variable. ExpressionFormat getImplicitFormat() const { return ImplicitFormat; } - /// \returns this variable's value. - Optional getValue() const { return Value; } + /// \returns this variable's tentative value if there is one, otherwise its + /// value. + Optional getValue() const { + if (TentativeValue) + return TentativeValue; + return Value; + } - /// \returns the input buffer's string from which this variable's value was - /// parsed, or None if the value is not yet defined or was not parsed from the - /// input buffer. For example, the value of @LINE is not parsed from the - /// input buffer, and some numeric variables are parsed from the command - /// line instead. - Optional getStringValue() const { return StrValue; } + /// \returns the input buffer's string from which this variable's value + /// (tentative if any) was parsed, or None if the value is not yet defined or + /// was not parsed from the input buffer. For example, the value of @LINE is + /// not parsed from the input buffer, and some numeric variables are parsed + /// from the command line instead. + Optional getStringValue() const { + if (StrTentativeValue) + return StrTentativeValue; + return StrValue; + } /// Sets value of this numeric variable to \p NewValue, and sets the input - /// buffer string from which it was parsed to \p NewStrValue. See comments on - /// getStringValue for a discussion of when the latter can be None. - void setValue(ExpressionValue NewValue, + /// buffer string from which it was parsed to \p NewStrValue. If \p Tentative + /// is set, it is the tentative value and corresponding input string which + /// are set. See comments on getStringValue for a discussion of when the + /// latter can be None. + void setValue(ExpressionValue NewValue, bool Tentative, Optional NewStrValue = None) { - Value = NewValue; - StrValue = NewStrValue; + if (Tentative) { + TentativeValue = NewValue; + StrTentativeValue = NewStrValue; + } else { + Value = NewValue; + StrValue = NewStrValue; + } + } + + /// Marks value as definitive (i.e. not tentative). + void commitValue() { + assert(TentativeValue && "No value to commit"); + Value = TentativeValue; + StrValue = StrTentativeValue; + clearTentativeValue(); } /// Clears value of this numeric variable, regardless of whether it is @@ -318,9 +362,12 @@ StrValue = None; } - /// \returns the line number where this variable is defined, if any, or None - /// if defined before input is parsed. - Optional getDefLineNumber() const { return DefLineNumber; } + /// Clears tentative value of this numeric variable and its corresponding + /// input string, regardless of whether it is currently defined or not. + void clearTentativeValue() { + TentativeValue = None; + StrTentativeValue = None; + } }; /// Class representing the use of a numeric variable in the AST of an @@ -435,14 +482,13 @@ private: /// Pointer to the class representing the expression whose value is to be /// substituted. - std::unique_ptr ExpressionPointer; + Expression *ExpressionPointer; public: NumericSubstitution(FileCheckPatternContext *Context, StringRef ExpressionStr, - std::unique_ptr ExpressionPointer, - size_t InsertIdx) + Expression *ExpressionPointer, size_t InsertIdx) : Substitution(Context, ExpressionStr, InsertIdx), - ExpressionPointer(std::move(ExpressionPointer)) {} + ExpressionPointer(ExpressionPointer) {} /// \returns a string containing the result of evaluating the expression in /// this substitution, or an error if evaluation failed. @@ -471,11 +517,15 @@ /// the former is defined on a later line than the latter. StringMap DefinedVariableTable; - /// When matching a given pattern, this holds the pointers to the classes - /// representing the numeric variables defined in previous patterns. When - /// matching a pattern all definitions for that pattern are recorded in the - /// NumericVariableDefs table in the Pattern instance of that pattern. - StringMap GlobalNumericVariableTable; + /// Holds the pointers to classes instances representing all the numeric + /// variables defined in patterns parsed so far and the line number where + /// they were last defined. + StringMap>> + GlobalNumericVariableTable; + + /// Holds whether the value of the last definition of a given numeric + /// variable is known at match time. + StringMap NumericVariableDefinitionTable; /// Pointer to the class instance representing the @LINE pseudo variable for /// easily updating its value. @@ -518,8 +568,14 @@ private: /// Makes a new numeric variable and registers it for destruction when the /// context is destroyed. - template NumericVariable *makeNumericVariable(Types... args); - + NumericVariable *makeNumericVariable(StringRef Name, + ExpressionFormat ImplicitFormat); + + /// Makes a new expression and registers it for destruction when the context + /// is destroyed. + Expression *makeExpression(std::unique_ptr AST, + ExpressionFormat Format, bool MatchTimeKnown, + NumericVariable *DefinedNumericVariable); /// Makes a new string substitution and registers it for destruction when the /// context is destroyed. Substitution *makeStringSubstitution(StringRef VarName, size_t InsertIdx); @@ -527,7 +583,7 @@ /// Makes a new numeric substitution and registers it for destruction when /// the context is destroyed. Substitution *makeNumericSubstitution(StringRef ExpressionStr, - std::unique_ptr Expression, + Expression *Expression, size_t InsertIdx); }; @@ -643,24 +699,29 @@ /// iterating over values. std::map VariableDefs; - /// Structure representing the definition of a numeric variable in a pattern. - /// It holds the pointer to the class instance holding the value and matching - /// format of the numeric variable whose value is being defined and the - /// number of the parenthesis group in RegExStr to capture that value. - struct NumericVariableMatch { + /// Structure representing an expression whose value is verified after + /// matching (either a variable definition with an empty expression or an + /// expression using directly or indirectly such a variable). It holds the + /// pointer to the class instance holding the expression to verify, the + /// pointer to the class instance holding the value and matching format of + /// the numeric variable whose value is being defined if any, and the number + /// of the parenthesis group in RegExStr to capture that value. + struct NumericExpressionMatch { + /// Pointer to class instance representing the expression to verify. + Expression *Expr; + /// Pointer to class instance holding the value and matching format of the - /// numeric variable being defined. + /// numeric variable being defined, if any. NumericVariable *DefinedNumericVariable; - /// Number of the parenthesis group in RegExStr that captures the value of - /// this numeric variable definition. + /// Number of the parenthesis group in the regex that captures the value of + /// this expression. unsigned CaptureParenGroup; }; - /// Holds the number of the parenthesis group in RegExStr and pointer to the - /// corresponding NumericVariable class instance of all numeric variable - /// definitions. Used to set the matched value of all those variables. - StringMap NumericVariableDefs; + /// Holds for expression that need to be verified the information needed to + /// do so, + SmallVector CapturedExpressions; /// Pointer to a class instance holding the global state shared by all /// patterns: @@ -717,10 +778,10 @@ /// holding a diagnostic against \p SM if parsing fails. If substitution was /// successful, sets \p DefinedNumericVariable to point to the class /// representing the numeric variable defined in this numeric substitution - /// block, or None if this block does not define any variable. - static Expected> parseNumericSubstitutionBlock( - StringRef Expr, Optional &DefinedNumericVariable, - bool IsLegacyLineExpr, Optional LineNumber, + /// block, or null if this block does not define any variable. + static Expected parseNumericSubstitutionBlock( + StringRef Expr, NumericVariable *&DefinedNumericVariable, + bool IsLegacyLineExpr, bool AllowSameUse, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM); /// Parses the pattern in \p PatternStr and initializes this Pattern instance /// accordingly. @@ -756,6 +817,9 @@ /// match defines new numeric values. MatchResult match(StringRef Buffer, const SourceMgr &SM) const; /// Prints the value of successful substitutions. + /// + /// Note: Expressions directly or indirectly using variables defined on the + /// same line are not shown because their value is not known at match time. void printSubstitutions(const SourceMgr &SM, StringRef Buffer, SMRange MatchRange, FileCheckDiag::MatchType MatchTy, std::vector *Diags) const; @@ -800,61 +864,82 @@ /// at line \p LineNumber, or before input is parsed if \p LineNumber is /// None. Parameter \p Context points to the class instance holding the live /// string and numeric variables. \returns the pointer to the class instance - /// representing that variable if successful, or an error holding a - /// diagnostic against \p SM otherwise. - static Expected> parseNumericVariableUse( - StringRef Name, bool IsPseudo, Optional LineNumber, - FileCheckPatternContext *Context, const SourceMgr &SM); + /// representing that variable if successful and whether the variable's value + /// is known at match time in \p MatchTimeKnown, or an error holding a + /// diagnostic against \p SM otherwise, for instance if the use is for a + /// variable defined on the same line and \p AllowSameUse is false. + static Expected> + parseNumericVariableUse(StringRef Name, bool IsPseudo, bool AllowSameUse, + bool &MatchTimeKnown, Optional LineNumber, + FileCheckPatternContext *Context, + const SourceMgr &SM); enum class AllowedOperand { LineVar, LegacyLiteral, Any }; /// Parses \p Expr for use of a numeric operand at line \p LineNumber, or /// before input is parsed if \p LineNumber is None. Accepts literal values, /// numeric variables and function calls, depending on the value of \p AO. - /// \p MaybeInvalidConstraint indicates whether the text being parsed could - /// be an invalid constraint. \p Context points to the class instance holding - /// the live string and numeric variables. \returns the class representing - /// that operand in the AST of the expression or an error holding a - /// diagnostic against \p SM otherwise. If \p Expr starts with a "(" this - /// function will attempt to parse a parenthesized expression. + /// \p AllowSameUse indicates whether to accept a variable defined earlier + /// on the same line and \p MaybeInvalidConstraint indicates whether the text + /// being parsed could be an invalid constraint. \p Context points to the + /// class instance holding the live string and numeric variables. If \p Expr + /// starts with a "(" this function will attempt to parse a parenthesized + /// expression. \returns the class representing that operand in the AST of + /// the expression or an error holding a diagnostic against \p SM otherwise. + /// Also sets \p MatchTimeKnown to whether the operand's value is known at + /// match time. static Expected> - parseNumericOperand(StringRef &Expr, AllowedOperand AO, bool ConstraintParsed, - Optional LineNumber, + parseNumericOperand(StringRef &Expr, AllowedOperand AO, + bool MaybeInvalidConstraint, bool AllowSameUse, + bool &MatchTimeKnown, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM); /// Parses and updates \p RemainingExpr for a binary operation at line /// \p LineNumber, or before input is parsed if \p LineNumber is None. The - /// left operand of this binary operation is given in \p LeftOp and \p Expr - /// holds the string for the full expression, including the left operand. - /// Parameter \p IsLegacyLineExpr indicates whether we are parsing a legacy - /// @LINE expression. Parameter \p Context points to the class instance + /// left operand of this binary operation is given in \p LeftOp and + /// \p MatchTimeKnown holds whether the left operand's value is known at + /// match time. \p Expr holds the string for the full expression, including + /// the left operand. Parameter \p IsLegacyLineExpr indicates whether we + /// are parsing a legacy @LINE expression and parameter \p AllowSameUse + /// indicates whether the use of a variable defined earlier on the same line + /// is a valid operand. Parameter \p Context points to the class instance /// holding the live string and numeric variables. \returns the class /// representing the binary operation in the AST of the expression, or an - /// error holding a diagnostic against \p SM otherwise. + /// error holding a diagnostic against \p SM otherwise. Also update + /// \p MatchTimeKnown according to whether the right operand is also known + /// at match time. static Expected> parseBinop(StringRef Expr, StringRef &RemainingExpr, std::unique_ptr LeftOp, bool IsLegacyLineExpr, + bool AllowSameUse, bool &MatchTimeKnown, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM); /// Parses a parenthesized expression inside \p Expr at line \p LineNumber, or /// before input is parsed if \p LineNumber is None. \p Expr must start with /// a '('. Accepts both literal values and numeric variables. Parameter \p - /// Context points to the class instance holding the live string and numeric - /// variables. \returns the class representing that operand in the AST of the + /// MatchTimeKnown is set to true if the expression is known at match time. + /// Parameter \p AllowSameUse indicates whether the expression allows use of + /// variable defined earlier on the same line. Parameter \p Context points + /// to the class instance holding the live string and numeric variables. + /// \returns the class representing that operand in the AST of the /// expression or an error holding a diagnostic against \p SM otherwise. static Expected> - parseParenExpr(StringRef &Expr, Optional LineNumber, - FileCheckPatternContext *Context, const SourceMgr &SM); + parseParenExpr(StringRef &Expr, bool AllowSameUse, bool &MatchTimeKnown, + Optional LineNumber, FileCheckPatternContext *Context, + const SourceMgr &SM); /// Parses \p Expr for an argument list belonging to a call to function \p /// FuncName at line \p LineNumber, or before input is parsed if \p LineNumber - /// is None. Parameter \p FuncLoc is the source location used for diagnostics. - /// Parameter \p Context points to the class instance holding the live string - /// and numeric variables. \returns the class representing that call in the - /// AST of the expression or an error holding a diagnostic against \p SM + /// is None. Parameter \p FuncName is the name of the function. Parameter \p + /// MatchTimeKnown is set to true if the expression is known at match time. + /// Parameter \p AllowSameUse indicate whether parameters of the function are + /// allowed to be variable defined earlier on the same line. Parameter + /// \p Context points to the class instance holding the live string and + /// numeric variables. \returns the class representing that call in the AST + /// of the expression or an error holding a diagnostic against \p SM /// otherwise. static Expected> - parseCallExpr(StringRef &Expr, StringRef FuncName, - Optional LineNumber, FileCheckPatternContext *Context, - const SourceMgr &SM); + parseCallExpr(StringRef &Expr, StringRef FuncName, bool AllowSameUse, + bool &MatchTimeKnown, Optional LineNumber, + FileCheckPatternContext *Context, const SourceMgr &SM); }; //===----------------------------------------------------------------------===// diff --git a/llvm/test/FileCheck/numeric-expression.txt b/llvm/test/FileCheck/numeric-expressions/numeric-expressions.txt rename from llvm/test/FileCheck/numeric-expression.txt rename to llvm/test/FileCheck/numeric-expressions/numeric-expressions.txt --- a/llvm/test/FileCheck/numeric-expression.txt +++ b/llvm/test/FileCheck/numeric-expressions/numeric-expressions.txt @@ -42,10 +42,10 @@ ERR-LABEL: DEF INVALID FMT INVALID-FMT-SPEC1-NEXT: INVVAR1=[[#%c,INVVAR1:]] INVALID-FMT-SPEC2-NEXT: INVVAR2=[[#%hhd,INVVAR2:]] -INVALID-FMT-SPEC-MSG1: numeric-expression.txt:[[#@LINE-2]]:37: error: invalid format specifier in expression +INVALID-FMT-SPEC-MSG1: numeric-expressions.txt:[[#@LINE-2]]:37: error: invalid format specifier in expression INVALID-FMT-SPEC-MSG1-NEXT: {{I}}NVALID-FMT-SPEC1-NEXT: INVVAR1={{\[\[#%c,INVVAR1:\]\]}} INVALID-FMT-SPEC-MSG1-NEXT: {{^}} ^{{$}} -INVALID-FMT-SPEC-MSG2: numeric-expression.txt:[[#@LINE-4]]:37: error: invalid format specifier in expression +INVALID-FMT-SPEC-MSG2: numeric-expressions.txt:[[#@LINE-4]]:37: error: invalid format specifier in expression INVALID-FMT-SPEC-MSG2-NEXT: {{I}}NVALID-FMT-SPEC2-NEXT: INVVAR2={{\[\[#%hhd,INVVAR2:\]\]}} INVALID-FMT-SPEC-MSG2-NEXT: {{^}} ^{{$}} @@ -66,10 +66,10 @@ INVALID-ALT-FORM-LABEL: DEF INVALID ALT FORM INVALID-ALT-FORM1-NEXT: PREFIXED_DEC=[[#%#u,PREFIXED_UNSI:]] INVALID-ALT-FORM2-NEXT: PREFIXED_DEC=[[#%#d,PREFIXED_SIGN:]] -INVALID-ALT-FORM-MSG1: numeric-expression.txt:[[#@LINE-2]]:42: error: alternate form only supported for hex values +INVALID-ALT-FORM-MSG1: numeric-expressions.txt:[[#@LINE-2]]:42: error: alternate form only supported for hex values INVALID-ALT-FORM-MSG1-NEXT: {{I}}NVALID-ALT-FORM1-NEXT: PREFIXED_DEC={{\[\[#%#u,PREFIXED_UNSI:\]\]}} INVALID-ALT-FORM-MSG1-NEXT: {{^}} ^{{$}} -INVALID-ALT-FORM-MSG2: numeric-expression.txt:[[#@LINE-4]]:42: error: alternate form only supported for hex values +INVALID-ALT-FORM-MSG2: numeric-expressions.txt:[[#@LINE-4]]:42: error: alternate form only supported for hex values INVALID-ALT-FORM-MSG2-NEXT: {{I}}NVALID-ALT-FORM2-NEXT: PREFIXED_DEC={{\[\[#%#d,PREFIXED_SIGN:\]\]}} INVALID-ALT-FORM-MSG2-NEXT: {{^}} ^{{$}} @@ -290,7 +290,7 @@ NUMEXPR-CONSTRAINT-NOMATCH-LABEL: USE DEF FMT EXPL NO MATCH NUMEXPR-CONSTRAINT-NOMATCH-NEXT: UNSI=[[#UNSI:]] NUMEXPR-CONSTRAINT-NOMATCH-NEXT: UNSI: [[#==UNSI]] -NUMEXPR-CONSTRAINT-NOMATCH-MSG: numeric-expression.txt:[[#@LINE-1]]:34: error: {{N}}UMEXPR-CONSTRAINT-NOMATCH-NEXT: expected string not found in input +NUMEXPR-CONSTRAINT-NOMATCH-MSG: numeric-expressions.txt:[[#@LINE-1]]:34: error: {{N}}UMEXPR-CONSTRAINT-NOMATCH-NEXT: expected string not found in input NUMEXPR-CONSTRAINT-NOMATCH-MSG-NEXT: {{N}}UMEXPR-CONSTRAINT-NOMATCH-NEXT: UNSI: {{\[\[#==UNSI\]\]}} NUMEXPR-CONSTRAINT-NOMATCH-MSG-NEXT: {{^}} ^{{$}} @@ -303,7 +303,7 @@ 18 EMPTY-NUMEXPR-CONSTRAINT-LABEL: EMPTY NUMEXPR USE WITH CONSTRAINT EMPTY-NUMEXPR-CONSTRAINT-NEXT: [[# ==]] -EMPTY-NUMEXPR-CONSTRAINT-MSG: numeric-expression.txt:[[#@LINE-1]]:38: error: empty numeric expression should not have a constraint +EMPTY-NUMEXPR-CONSTRAINT-MSG: numeric-expressions.txt:[[#@LINE-1]]:38: error: empty numeric expression should not have a constraint EMPTY-NUMEXPR-CONSTRAINT-MSG-NEXT: {{E}}MPTY-NUMEXPR-CONSTRAINT-NEXT: {{\[\[# ==\]\]}} EMPTY-NUMEXPR-CONSTRAINT-MSG-NEXT: {{^}} ^{{$}} @@ -316,7 +316,7 @@ 18 EMPTY-NUMDEF-CONSTRAINT-LABEL: EMPTY NUMEXPR CONSTRAINT EMPTY-NUMDEF-CONSTRAINT-NEXT: [[#VARDEF: ==]] -EMPTY-NUMDEF-CONSTRAINT-MSG: numeric-expression.txt:[[#@LINE-1]]:44: error: empty numeric expression should not have a constraint +EMPTY-NUMDEF-CONSTRAINT-MSG: numeric-expressions.txt:[[#@LINE-1]]:44: error: empty numeric expression should not have a constraint EMPTY-NUMDEF-CONSTRAINT-MSG-NEXT: {{E}}MPTY-NUMDEF-CONSTRAINT-NEXT: {{\[\[#VARDEF: ==\]\]}} EMPTY-NUMDEF-CONSTRAINT-MSG-NEXT: {{^}} ^{{$}} @@ -348,7 +348,7 @@ 23 FMT-CONFLICT1-LABEL: VAR USE IMPL FMT CONFLICT FMT-CONFLICT1-NEXT: [[#UNSI + LHEX]] -FMT-CONFLICT1-MSG: numeric-expression.txt:[[#@LINE-1]]:24: error: implicit format conflict between 'UNSI' (%u) and 'LHEX' (%x), need an explicit format specifier +FMT-CONFLICT1-MSG: numeric-expressions.txt:[[#@LINE-1]]:24: error: implicit format conflict between 'UNSI' (%u) and 'LHEX' (%x), need an explicit format specifier FMT-CONFLICT1-MSG-NEXT: {{F}}MT-CONFLICT1-NEXT: {{\[\[#UNSI \+ LHEX\]\]}} FMT-CONFLICT1-MSG-NEXT: {{^ \^$}} @@ -356,7 +356,7 @@ 34 FMT-CONFLICT2-LABEL: VAR USE IMPL FMT CONFLICT FMT-CONFLICT2-NEXT: [[#UNSI + UNSIa + LHEX]] -FMT-CONFLICT2-MSG: numeric-expression.txt:[[#@LINE-1]]:24: error: implicit format conflict between 'UNSI + UNSIa' (%u) and 'LHEX' (%x), need an explicit format specifier +FMT-CONFLICT2-MSG: numeric-expressions.txt:[[#@LINE-1]]:24: error: implicit format conflict between 'UNSI + UNSIa' (%u) and 'LHEX' (%x), need an explicit format specifier FMT-CONFLICT2-MSG-NEXT: {{F}}MT-CONFLICT2-NEXT: {{\[\[#UNSI \+ UNSIa \+ LHEX\]\]}} FMT-CONFLICT2-MSG-NEXT: {{^ \^$}} @@ -387,7 +387,7 @@ 11 // PAREN-OP-NEXT: [[#(NUMVAR+2)-1]] 11 // PAREN-OP-NEXT: [[#NUMVAR+(2-1)]] 11 // PAREN-OP-NEXT: [[#NUMVAR+(2-1]] -PAREN-OP-MSG: numeric-expression.txt:[[#@LINE-1]]:36: error: missing ')' at end of nested expression +PAREN-OP-MSG: numeric-expressions.txt:[[#@LINE-1]]:36: error: missing ')' at end of nested expression PAREN-OP-MSG-NEXT: {{P}}AREN-OP-NEXT: {{\[\[#NUMVAR\+\(2\-1]\]}} PAREN-OP-MSG-NEXT: {{^}} ^{{$}} @@ -400,10 +400,10 @@ UNDEFVAR: 11 UNDEF-USE-LABEL: UNDEF VAR USE UNDEF-USE-NEXT: UNDEFVAR: [[#UNDEFVAR1+UNDEFVAR2]] -UNDEF-USE-MSG: numeric-expression.txt:[[#@LINE-1]]:30: error: undefined variable: UNDEFVAR1 +UNDEF-USE-MSG: numeric-expressions.txt:[[#@LINE-1]]:30: error: undefined variable: UNDEFVAR1 UNDEF-USE-MSG-NEXT: {{U}}NDEF-USE-NEXT: {{U}}NDEFVAR: {{\[\[#UNDEFVAR1\+UNDEFVAR2\]\]}} UNDEF-USE-MSG-NEXT: {{^}} ^{{$}} -UNDEF-USE-MSG-NEXT: numeric-expression.txt:[[#@LINE-4]]:40: error: undefined variable: UNDEFVAR2 +UNDEF-USE-MSG-NEXT: numeric-expressions.txt:[[#@LINE-4]]:40: error: undefined variable: UNDEFVAR2 UNDEF-USE-MSG-NEXT: {{U}}NDEF-USE-NEXT: {{U}}NDEFVAR: {{\[\[#UNDEFVAR1\+UNDEFVAR2\]\]}} UNDEF-USE-MSG-NEXT: {{^}} ^{{$}} @@ -417,10 +417,10 @@ UNDEF-USE2-LABEL: CHECK NOT UNDEF VAR USE UNDEF-USE2-NOT: UNDEFVAR: [[#UNDEFVAR1+UNDEFVAR2]] UNDEF-USE2: END MARKER -UNDEF-USE-MSG2: numeric-expression.txt:[[#@LINE-2]]:30: error: undefined variable: UNDEFVAR1 +UNDEF-USE-MSG2: numeric-expressions.txt:[[#@LINE-2]]:30: error: undefined variable: UNDEFVAR1 UNDEF-USE-MSG2-NEXT: {{U}}NDEF-USE2-NOT: {{U}}NDEFVAR: {{\[\[#UNDEFVAR1\+UNDEFVAR2\]\]}} UNDEF-USE-MSG2-NEXT: {{^}} ^{{$}} -UNDEF-USE-MSG2-NEXT: numeric-expression.txt:[[#@LINE-5]]:40: error: undefined variable: UNDEFVAR2 +UNDEF-USE-MSG2-NEXT: numeric-expressions.txt:[[#@LINE-5]]:40: error: undefined variable: UNDEFVAR2 UNDEF-USE-MSG2-NEXT: {{U}}NDEF-USE2-NOT: {{U}}NDEFVAR: {{\[\[#UNDEFVAR1\+UNDEFVAR2\]\]}} UNDEF-USE-MSG2-NEXT: {{^}} ^{{$}} @@ -434,7 +434,7 @@ NUMVAR*2: 22 INVAL-OP-LABEL: INVALID OPERATOR INVAL-OP-NEXT: NUMVAR*2: [[#NUMVAR*2]] -INVAL-OP-MSG: numeric-expression.txt:[[#@LINE-1]]:35: error: unsupported operation '*' +INVAL-OP-MSG: numeric-expressions.txt:[[#@LINE-1]]:35: error: unsupported operation '*' INVAL-OP-MSG-NEXT: {{I}}NVAL-OP-NEXT: NUMVAR*2: {{\[\[#NUMVAR\*2\]\]}} INVAL-OP-MSG-NEXT: {{^}} ^{{$}} @@ -474,13 +474,13 @@ CONFLICT2: [[NUMVAR:foo.*]] CONFLICT3: [[STRVAR:foo.*]] CONFLICT4: redef2 [[#STRVAR:]] -INPUT-STR-CONFLICT: numeric-expression.txt:[[#@LINE-3]]:14: error: numeric variable with name 'NUMVAR' already exists +INPUT-STR-CONFLICT: numeric-expressions.txt:[[#@LINE-3]]:14: error: numeric variable with name 'NUMVAR' already exists INPUT-STR-CONFLICT-NEXT: {{C}}ONFLICT2: {{\[\[NUMVAR:foo\.\*\]\]}} INPUT-STR-CONFLICT-NEXT: {{^}} ^{{$}} CLI-STR-CONFLICT: Global defines:2:19: error: numeric variable with name 'NUMVAR' already exists CLI-STR-CONFLICT-NEXT: Global define #2: NUMVAR=foobar CLI-STR-CONFLICT-NEXT: {{^}} ^{{$}} -INPUT-NUM-CONFLICT: numeric-expression.txt:[[#@LINE-7]]:22: error: string variable with name 'STRVAR' already exists +INPUT-NUM-CONFLICT: numeric-expressions.txt:[[#@LINE-7]]:22: error: string variable with name 'STRVAR' already exists INPUT-NUM-CONFLICT-NEXT: CONFLICT4: redef2 {{\[\[#STRVAR:\]\]}} INPUT-NUM-CONFLICT-NEXT: {{^}} ^{{$}} CLI-NUM-CONFLICT: Global defines:2:45: error: string variable with name 'STRVAR' already exists @@ -496,7 +496,7 @@ NUMVAR: 10000000000000000000000 BIGVAL-LABEL: BIG VALUE BIGVAL-NEXT: NUMVAR: [[#NUMVAR:]] -BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: unable to represent numeric value +BIGVAL-MSG: numeric-expressions.txt:[[#@LINE-3]]:9: error: unable to represent numeric value BIGVAL-MSG-NEXT: {{N}}UMVAR: 10000000000000000000000 BIGVAL-MSG-NEXT: {{^}} ^{{$}} @@ -512,36 +512,10 @@ DEF-EXPR-FAIL-LABEL: DEF EXPR WRONG MATCH DEF-EXPR-FAIL-NEXT: [[# VAR20:]] DEF-EXPR-FAIL-NEXT: [[# VAR42: VAR20+22]] -DEF-EXPR-FAIL-MSG: numeric-expression.txt:[[#@LINE-1]]:21: error: {{D}}EF-EXPR-FAIL-NEXT: is not on the line after the previous match +DEF-EXPR-FAIL-MSG: numeric-expressions.txt:[[#@LINE-1]]:21: error: {{D}}EF-EXPR-FAIL-NEXT: is not on the line after the previous match DEF-EXPR-FAIL-MSG-NEXT: {{D}}EF-EXPR-FAIL-NEXT: {{\[\[# VAR42: VAR20\+22\]\]}} DEF-EXPR-FAIL-MSG-NEXT: {{^}} ^{{$}} -; Verify that using a numeric variable defined on the same line (whether from -; input or from an expression) is rejected. -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck --check-prefix SAME-LINE-USE1 --input-file %s %s 2>&1 \ -RUN: | FileCheck --strict-whitespace --check-prefix SAME-LINE-USE-MSG1 %s -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck --check-prefix SAME-LINE-USE2 --input-file %s %s 2>&1 \ -RUN: | FileCheck --strict-whitespace --check-prefix SAME-LINE-USE-MSG2 %s - -SAME LINE USE -3 -4 5 -SAME-LINE-USE1-LABEL: SAME LINE USE -SAME-LINE-USE1-NEXT: [[#]] -SAME-LINE-USE1-NEXT: [[#UNSI:]] [[#UNSI+1]] -SAME-LINE-USE-MSG1: numeric-expression.txt:[[#@LINE-1]]:36: error: numeric variable 'UNSI' defined earlier in the same CHECK directive -SAME-LINE-USE-MSG1-NEXT: {{S}}AME-LINE-USE1-NEXT: {{\[\[#UNSI:\]\] \[\[#UNSI\+1\]\]}} -SAME-LINE-USE-MSG1-NEXT: {{^}} ^{{$}} - -SAME-LINE-USE2-LABEL: SAME LINE USE -SAME-LINE-USE2-NEXT: [[#UNSI:]] -SAME-LINE-USE2-NEXT: [[#UNSI2:UNSI+1]] [[#UNSI2+1]] -SAME-LINE-USE-MSG2: numeric-expression.txt:[[#@LINE-1]]:43: error: numeric variable 'UNSI2' defined earlier in the same CHECK directive -SAME-LINE-USE-MSG2-NEXT: {{S}}AME-LINE-USE2-NEXT: {{\[\[#UNSI2:UNSI\+1\]\] \[\[#UNSI2\+1\]\]}} -SAME-LINE-USE-MSG2-NEXT: {{^}} ^{{$}} - ; Invalid change of format in variable redefinition. RUN: %ProtectFileCheckOutput \ RUN: not FileCheck --check-prefix REDEF-NEW-FMT --input-file %s %s 2>&1 \ @@ -553,7 +527,7 @@ REDEF-NEW-FMT-LABEL: VAR REDEF FMT CHANGE REDEF-NEW-FMT-NEXT: [[#UNSI:]] REDEF-NEW-FMT-NEXT: [[#%X,UNSI:]] -REDEF-NEW-FMT-MSG: numeric-expression.txt:[[#@LINE-1]]:31: error: format different from previous variable definition +REDEF-NEW-FMT-MSG: numeric-expressions.txt:[[#@LINE-1]]:31: error: format different from previous variable definition REDEF-NEW-FMT-MSG-NEXT: {{R}}EDEF-NEW-FMT-NEXT: {{\[\[#%X,UNSI:\]\]}} REDEF-NEW-FMT-MSG-NEXT: {{^}} ^{{$}} @@ -566,7 +540,7 @@ BIGVAR=10000000000000000 OVERFLOW-LABEL: OVERFLOW OVERFLOW-NEXT: BIGVAR: [[#BIGVAR:0x8000000000000000+0x8000000000000000]] -OVERFLOW-MSG: numeric-expression.txt:[[#@LINE-1]]:27: error: unable to substitute variable or numeric expression +OVERFLOW-MSG: numeric-expressions.txt:[[#@LINE-1]]:27: error: unable to substitute variable or numeric expression OVERFLOW-MSG-NEXT: {{O}}VERFLOW-NEXT: BIGVAR: {{\[\[#BIGVAR:0x8000000000000000\+0x8000000000000000\]\]}} OVERFLOW-MSG-NEXT: {{^}} ^{{$}} @@ -579,7 +553,7 @@ TINYVAR=-10000000000000000 UNDERFLOW-LABEL: UNDERFLOW UNDERFLOW-NEXT: TINYVAR: [[#%d,TINYVAR:-0x8000000000000000-0x8000000000000000]] -UNDERFLOW-MSG: numeric-expression.txt:[[#@LINE-1]]:29: error: unable to substitute variable or numeric expression +UNDERFLOW-MSG: numeric-expressions.txt:[[#@LINE-1]]:29: error: unable to substitute variable or numeric expression UNDERFLOW-MSG-NEXT: {{U}}NDERFLOW-NEXT: TINYVAR: {{\[\[#%d,TINYVAR:-0x8000000000000000-0x8000000000000000\]\]}} UNDERFLOW-MSG-NEXT: {{^}} ^{{$}} @@ -591,7 +565,7 @@ 30 CALL-MISSING-CLOSING-BRACKET-LABEL: CALL MISSING CLOSING BRACKET CALL-MISSING-CLOSING-BRACKET-NEXT: [[#add(NUMVAR,3]] -CALL-MISSING-CLOSING-BRACKET-MSG: numeric-expression.txt:[[#@LINE-1]]:51: error: missing ')' at end of call expression +CALL-MISSING-CLOSING-BRACKET-MSG: numeric-expressions.txt:[[#@LINE-1]]:51: error: missing ')' at end of call expression CALL-MISSING-CLOSING-BRACKET-MSG-NEXT: {{C}}ALL-MISSING-CLOSING-BRACKET-NEXT: {{\[\[#add\(NUMVAR,3\]\]}} CALL-MISSING-CLOSING-BRACKET-MSG-NEXT: {{^}} ^{{$}} @@ -603,7 +577,7 @@ 30 CALL-MISSING-ARGUMENT-LABEL: CALL MISSING ARGUMENT CALL-MISSING-ARGUMENT-NEXT: [[#add(NUMVAR,)]] -CALL-MISSING-ARGUMENT-MSG: numeric-expression.txt:[[#@LINE-1]]:43: error: missing argument +CALL-MISSING-ARGUMENT-MSG: numeric-expressions.txt:[[#@LINE-1]]:43: error: missing argument CALL-MISSING-ARGUMENT-MSG-NEXT: {{C}}ALL-MISSING-ARGUMENT-NEXT: {{\[\[#add\(NUMVAR,\)\]\]}} CALL-MISSING-ARGUMENT-MSG-NEXT: {{^}} ^{{$}} @@ -615,7 +589,7 @@ 30 CALL-WRONG-ARGUMENT-COUNT-LABEL: CALL WRONG ARGUMENT COUNT CALL-WRONG-ARGUMENT-COUNT-NEXT: [[#add(NUMVAR)]] -CALL-WRONG-ARGUMENT-COUNT-MSG: numeric-expression.txt:[[#@LINE-1]]:36: error: function 'add' takes 2 arguments but 1 given +CALL-WRONG-ARGUMENT-COUNT-MSG: numeric-expressions.txt:[[#@LINE-1]]:36: error: function 'add' takes 2 arguments but 1 given CALL-WRONG-ARGUMENT-COUNT-MSG-NEXT: {{C}}ALL-WRONG-ARGUMENT-COUNT-NEXT: {{\[\[#add\(NUMVAR\)\]\]}} CALL-WRONG-ARGUMENT-COUNT-MSG-NEXT: {{^}} ^{{$}} @@ -627,6 +601,6 @@ 30 CALL-UNDEFINED-FUNCTION-LABEL: CALL UNDEFINED FUNCTION CALL-UNDEFINED-FUNCTION-NEXT: [[#bogus_function(NUMVAR)]] -CALL-UNDEFINED-FUNCTION-MSG: numeric-expression.txt:[[#@LINE-1]]:34: error: call to undefined function 'bogus_function' +CALL-UNDEFINED-FUNCTION-MSG: numeric-expressions.txt:[[#@LINE-1]]:34: error: call to undefined function 'bogus_function' CALL-UNDEFINED-FUNCTION-MSG-NEXT: {{C}}ALL-UNDEFINED-FUNCTION-NEXT: {{\[\[#bogus_function\(NUMVAR\)\]\]}} CALL-UNDEFINED-FUNCTION-MSG-NEXT: {{^}} ^{{$}} diff --git a/llvm/test/FileCheck/numeric-expressions/same-line-var-use.txt b/llvm/test/FileCheck/numeric-expressions/same-line-var-use.txt new file mode 100644 --- /dev/null +++ b/llvm/test/FileCheck/numeric-expressions/same-line-var-use.txt @@ -0,0 +1,79 @@ +RUN: FileCheck --check-prefixes=CHECK,LABELS --input-file %s %s + +; Verify that a CHECK directive using a variable defined for the first time on +; the same line matches valid input with no ambiguity. +FIRST DEF SIMPLE CASE // LABELS-LABEL: FIRST DEF SIMPLE CASE +13 14 // CHECK-NEXT: [[#UNSI:]] [[#UNSI+1]] + +; Verify that incorrect values for uses of variable defined for the first time +; on the same line are rejected. +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck --check-prefixes=LABELS,SAME-LINE-FAIL --input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace --check-prefixes=SAME-LINE-FAIL-MSG %s + +FIRST DEF FAIL +4 6 +LABELS-LABEL: FIRST DEF FAIL +SAME-LINE-FAIL-NEXT: [[#UNSI:]] [[#UNSI+1]] +SAME-LINE-FAIL-MSG: same-line-var-use.txt:[[#@LINE-1]]:22: error: {{S}}AME-LINE-FAIL-NEXT: expected string not found in input +SAME-LINE-FAIL-MSG-NEXT: {{S}}AME-LINE-FAIL-NEXT: {{\[\[#UNSI:\]\] \[\[#UNSI\+1\]\]}} +SAME-LINE-FAIL-MSG-NEXT: {{^}} ^{{$}} + +; Using a variable defined for the first time on the same line can lead to +; match failure for input that satisfy the constraint. +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck --check-prefixes=LABELS,SAME-LINE-FALSE-NEG --input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace --check-prefixes=SAME-LINE-FALSE-NEG-MSG %s + +SAME LINE FALSE NEGATIVE +13 15 16 +LABELS-LABEL: SAME LINE FALSE NEGATIVE +SAME-LINE-FALSE-NEG-NEXT: [[#UNSI:]] [[#UNSI+1]] +SAME-LINE-FALSE-NEG-MSG: same-line-var-use.txt:[[#@LINE-1]]:27: error: {{S}}AME-LINE-FALSE-NEG-NEXT: expected string not found in input +SAME-LINE-FALSE-NEG-MSG-NEXT: {{S}}AME-LINE-FALSE-NEG-NEXT: {{\[\[#UNSI:\]\] \[\[#UNSI\+1\]\]}} +SAME-LINE-FALSE-NEG-MSG-NEXT: {{^}} ^{{$}} + +; Verify that CHECK-NOT are not allowed to use a variable defined from an +; empty expression on the same line. +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck --check-prefixes=LABELS,SAME-LINE-NOT-KO --input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace --check-prefix SAME-LINE-NOT-KO-MSG %s +SAME LINE NOT FORBIDDEN +2 4 +LABELS-LABEL: SAME LINE NOT FORBIDDEN +SAME-LINE-NOT-KO-NOT: [[#UNSI:]] [[#UNSI+1]] +SAME-LINE-NOT-KO-MSG: same-line-var-use.txt:[[#@LINE-1]]:37: error: unknown variable value at match time +SAME-LINE-NOT-KO-MSG-NEXT: {{S}}AME-LINE-NOT-KO-NOT: {{\[\[#UNSI:\]\] \[\[#UNSI\+1\]\]}} +SAME-LINE-NOT-KO-MSG-NEXT: {{^}} ^{{$}} + +; Verify that a CHECK directive using a variable defined on the same line also +; already defined on a previous line matches valid input with no ambiguity. +ALREADY DEF SIMPLE CASE // CHECK-LABEL: ALREADY DEF SIMPLE CASE +13 // CHECK-NEXT: [[#UNSI:]] +14 15 // CHECK-NEXT: [[#UNSI:]] [[#UNSI+1]] +14 15 16 // CHECK-NEXT: [[#UNSI]] [[#UNSI:]] [[#UNSI+1]] + +; Verify that a CHECK directive using a variable defined for the first time on +; the same line from ; an expression that is known at match time matches valid +; input even with ambiguity. +FIRST DEF FROM EXPR // CHECK-LABEL: FIRST DEF FROM EXPR +13 // CHECK-NEXT: [[#UNSI1:]] +14 15 // CHECK-NEXT: [[#UNSI2:UNSI1+1]] [[#UNSI2+1]] +14 16 17 // CHECK-NEXT: [[#UNSI3:UNSI1+3]] [[#UNSI3+1]] + +; Verify that incorrect values for uses of variable defined for the first time +; on the same line from an expression are rejected. +RUN: %ProtectFileCheckOutput \ +RUN: not FileCheck --check-prefixes=LABELS,FROM-EXPR-FAIL --input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace --check-prefix FROM-EXPR-FAIL-MSG %s + +FROM EXPR FAIL +3 +4 6 +LABELS-LABEL: FROM EXPR FAIL +FROM-EXPR-FAIL-NEXT: [[#UNSI1:]] +FROM-EXPR-FAIL-NEXT: [[#UNSI2:UNSI1+1]] [[#UNSI2+1]] +FROM-EXPR-FAIL-MSG: same-line-var-use.txt:[[#@LINE-1]]:22: error: {{F}}ROM-EXPR-FAIL-NEXT: expected string not found in input +FROM-EXPR-FAIL-MSG-NEXT: {{F}}ROM-EXPR-FAIL-NEXT: {{\[\[#UNSI2:UNSI1\+1\]\] \[\[#UNSI2\+1\]\]}} +FROM-EXPR-FAIL-MSG-NEXT: {{^}} ^{{$}} +