@@ -7332,8 +7332,8 @@ bool EquatableFormatArgument::VerifyCompatible(
73327332 S.PDiag(diag::warn_format_cmp_sensitivity_mismatch)
73337333 << Sensitivity << Other.Sensitivity,
73347334 FmtExpr, InFunctionCall);
7335- S.Diag(Other.ElementLoc, diag::note_format_cmp_with) << 0 << Other.Range;
7336- HadError = true ;
7335+ HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with)
7336+ << 0 << Other.Range ;
73377337 }
73387338
73397339 switch (ArgType.matchesArgType(S.Context, Other.ArgType)) {
@@ -7351,8 +7351,8 @@ bool EquatableFormatArgument::VerifyCompatible(
73517351 << buildFormatSpecifier()
73527352 << Other.buildFormatSpecifier(),
73537353 FmtExpr, InFunctionCall);
7354- S.Diag(Other.ElementLoc, diag::note_format_cmp_with) << 0 << Other.Range;
7355- HadError = true ;
7354+ HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with)
7355+ << 0 << Other.Range ;
73567356 break;
73577357
73587358 case MK::NoMatchPedantic:
@@ -7361,8 +7361,8 @@ bool EquatableFormatArgument::VerifyCompatible(
73617361 << buildFormatSpecifier()
73627362 << Other.buildFormatSpecifier(),
73637363 FmtExpr, InFunctionCall);
7364- S.Diag(Other.ElementLoc, diag::note_format_cmp_with) << 0 << Other.Range;
7365- HadError = true ;
7364+ HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with)
7365+ << 0 << Other.Range ;
73667366 break;
73677367
73687368 case MK::NoMatchSignedness:
@@ -7374,8 +7374,8 @@ bool EquatableFormatArgument::VerifyCompatible(
73747374 << buildFormatSpecifier()
73757375 << Other.buildFormatSpecifier(),
73767376 FmtExpr, InFunctionCall);
7377- S.Diag(Other.ElementLoc, diag::note_format_cmp_with) << 0 << Other.Range;
7378- HadError = true ;
7377+ HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with)
7378+ << 0 << Other.Range ;
73797379 }
73807380 break;
73817381 }
@@ -8500,52 +8500,50 @@ static bool CompareFormatSpecifiers(Sema &S, const StringLiteral *Ref,
85008500 auto RefIter = RefArgs.begin(), RefEnd = RefArgs.end();
85018501 while (FmtIter < FmtEnd && RefIter < RefEnd) {
85028502 // In positional-style format strings, the same specifier can appear
8503- // multiple times. In the Ref format string, we have already verified that
8504- // each repetition of the same positional specifier are mutually compatible,
8505- // so we only need to test each repetition in the Fmt string with the first
8506- // corresponding Ref specifier.
8507- auto FmtIterEnd = std::find_if(FmtIter + 1, FmtEnd, [=](const auto &Arg) {
8508- return Arg.getPosition() != FmtIter->getPosition();
8509- });
8510- auto RefIterEnd = std::find_if(RefIter + 1, RefEnd, [=](const auto &Arg) {
8511- return Arg.getPosition() != RefIter->getPosition();
8512- });
8503+ // multiple times (like %2$i %2$d). Specifiers in both RefArgs and FmtArgs
8504+ // are sorted by getPosition(), and we process each range of equal
8505+ // getPosition() values as one group.
8506+ // RefArgs are taken from a string literal that was given to
8507+ // attribute(format_matches), and if we got this far, we have already
8508+ // verified that if it has positional specifiers that appear in multiple
8509+ // locations, then they are all mutually compatible. What's left for us to
8510+ // do is verify that all specifiers with the same position in FmtArgs are
8511+ // compatible with the RefArgs specifiers. We check each specifier from
8512+ // FmtArgs against the first member of the RefArgs group.
8513+ for (; FmtIter < FmtEnd; ++FmtIter) {
8514+ // Clang does not diagnose missing format specifiers in positional-style
8515+ // strings (TODO: which it probably should do, as it is UB to skip over a
8516+ // format argument). Skip specifiers if needed.
8517+ if (FmtIter->getPosition() < RefIter->getPosition())
8518+ continue;
85138519
8514- // Clang does not diagnose missing format specifiers in positional-style
8515- // strings (TODO: which it probably should do, as it is UB to skip over a
8516- // format argument). Skip specifiers if needed.
8517- if (FmtIter->getPosition() < RefIter->getPosition()) {
8518- FmtIter = FmtIterEnd;
8519- continue;
8520- }
8521- if (RefIter->getPosition() < FmtIter->getPosition()) {
8522- RefIter = RefIterEnd;
8523- continue;
8524- }
8520+ // Delimits a new getPosition() value.
8521+ if (FmtIter->getPosition() > RefIter->getPosition())
8522+ break;
85258523
8526- while (FmtIter != FmtIterEnd) {
85278524 HadError |=
85288525 !FmtIter->VerifyCompatible(S, *RefIter, FmtExpr, InFunctionCall);
8529- ++FmtIter;
85308526 }
8531- RefIter = RefIterEnd;
8527+
8528+ // Jump RefIter to the start of the next group.
8529+ RefIter = std::find_if(RefIter + 1, RefEnd, [=](const auto &Arg) {
8530+ return Arg.getPosition() != RefIter->getPosition();
8531+ });
85328532 }
85338533
85348534 if (FmtIter < FmtEnd) {
85358535 CheckFormatHandler::EmitFormatDiagnostic(
85368536 S, InFunctionCall, FmtExpr,
85378537 S.PDiag(diag::warn_format_cmp_specifier_arity) << 1,
85388538 FmtExpr->getBeginLoc(), false, FmtIter->getSourceRange());
8539- S.Diag(Ref->getBeginLoc(), diag::note_format_cmp_with) << 1;
8540- HadError = true;
8539+ HadError = S.Diag(Ref->getBeginLoc(), diag::note_format_cmp_with) << 1;
85418540 } else if (RefIter < RefEnd) {
85428541 CheckFormatHandler::EmitFormatDiagnostic(
85438542 S, InFunctionCall, FmtExpr,
85448543 S.PDiag(diag::warn_format_cmp_specifier_arity) << 0,
85458544 FmtExpr->getBeginLoc(), false, Fmt->getSourceRange());
8546- S.Diag(Ref->getBeginLoc(), diag::note_format_cmp_with)
8547- << 1 << RefIter->getSourceRange();
8548- HadError = true;
8545+ HadError = S.Diag(Ref->getBeginLoc(), diag::note_format_cmp_with)
8546+ << 1 << RefIter->getSourceRange();
85498547 }
85508548 return !HadError;
85518549}
@@ -8683,21 +8681,22 @@ bool Sema::ValidateFormatString(FormatStringType Type,
86838681 true, Args))
86848682 return false;
86858683
8686- // If positional arguments are used multiple times in the same format string,
8687- // ensure that they are used in compatible ways.
8684+ // Group arguments by getPosition() value, and check that each member of the
8685+ // group is compatible with the first member. This verifies that when
8686+ // positional arguments are used multiple times (such as %2$i %2$d), all uses
8687+ // are mutually compatible. As an optimization, don't test the first member
8688+ // against itself.
86888689 bool HadError = false;
86898690 auto Iter = Args.begin();
86908691 auto End = Args.end();
86918692 while (Iter != End) {
8692- auto PosEnd = std::find_if(Iter + 1, End, [=]( const auto &Arg) {
8693- return Arg.getPosition() != Iter->getPosition() ;
8694- } );
8695- for (auto PosIter = Iter + 1; PosIter != PosEnd; ++PosIter ) {
8696- HadError |= !PosIter ->VerifyCompatible(*this, *Iter , Str, true);
8693+ const auto &FirstInGroup = *Iter;
8694+ for (++ Iter;
8695+ Iter != End && Iter->getPosition() == FirstInGroup.getPosition( );
8696+ ++Iter ) {
8697+ HadError |= !Iter ->VerifyCompatible(*this, FirstInGroup , Str, true);
86978698 }
8698- Iter = PosEnd;
86998699 }
8700-
87018700 return !HadError;
87028701}
87038702
0 commit comments