@@ -7332,8 +7332,8 @@ bool EquatableFormatArgument::VerifyCompatible(
7332
7332
S.PDiag(diag::warn_format_cmp_sensitivity_mismatch)
7333
7333
<< Sensitivity << Other.Sensitivity,
7334
7334
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 ;
7337
7337
}
7338
7338
7339
7339
switch (ArgType.matchesArgType(S.Context, Other.ArgType)) {
@@ -7351,8 +7351,8 @@ bool EquatableFormatArgument::VerifyCompatible(
7351
7351
<< buildFormatSpecifier()
7352
7352
<< Other.buildFormatSpecifier(),
7353
7353
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 ;
7356
7356
break;
7357
7357
7358
7358
case MK::NoMatchPedantic:
@@ -7361,8 +7361,8 @@ bool EquatableFormatArgument::VerifyCompatible(
7361
7361
<< buildFormatSpecifier()
7362
7362
<< Other.buildFormatSpecifier(),
7363
7363
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 ;
7366
7366
break;
7367
7367
7368
7368
case MK::NoMatchSignedness:
@@ -7374,8 +7374,8 @@ bool EquatableFormatArgument::VerifyCompatible(
7374
7374
<< buildFormatSpecifier()
7375
7375
<< Other.buildFormatSpecifier(),
7376
7376
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 ;
7379
7379
}
7380
7380
break;
7381
7381
}
@@ -8500,52 +8500,50 @@ static bool CompareFormatSpecifiers(Sema &S, const StringLiteral *Ref,
8500
8500
auto RefIter = RefArgs.begin(), RefEnd = RefArgs.end();
8501
8501
while (FmtIter < FmtEnd && RefIter < RefEnd) {
8502
8502
// 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;
8513
8519
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;
8525
8523
8526
- while (FmtIter != FmtIterEnd) {
8527
8524
HadError |=
8528
8525
!FmtIter->VerifyCompatible(S, *RefIter, FmtExpr, InFunctionCall);
8529
- ++FmtIter;
8530
8526
}
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
+ });
8532
8532
}
8533
8533
8534
8534
if (FmtIter < FmtEnd) {
8535
8535
CheckFormatHandler::EmitFormatDiagnostic(
8536
8536
S, InFunctionCall, FmtExpr,
8537
8537
S.PDiag(diag::warn_format_cmp_specifier_arity) << 1,
8538
8538
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;
8541
8540
} else if (RefIter < RefEnd) {
8542
8541
CheckFormatHandler::EmitFormatDiagnostic(
8543
8542
S, InFunctionCall, FmtExpr,
8544
8543
S.PDiag(diag::warn_format_cmp_specifier_arity) << 0,
8545
8544
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();
8549
8547
}
8550
8548
return !HadError;
8551
8549
}
@@ -8683,21 +8681,22 @@ bool Sema::ValidateFormatString(FormatStringType Type,
8683
8681
true, Args))
8684
8682
return false;
8685
8683
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.
8688
8689
bool HadError = false;
8689
8690
auto Iter = Args.begin();
8690
8691
auto End = Args.end();
8691
8692
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);
8697
8698
}
8698
- Iter = PosEnd;
8699
8699
}
8700
-
8701
8700
return !HadError;
8702
8701
}
8703
8702
0 commit comments