This is an archive of the discontinued LLVM Phabricator instance.

[clang][#47272] Avoid suggesting deprecated version of a declaration over another in typo correction
ClosedPublic

Authored by zero9178 on Jan 6 2022, 3:55 PM.

Details

Summary

Prior to this patch, clang might suggest a deprecated name of a declaration over another name as the only mechanism for resolving two typo corrections referring to the same underlying declaration has previously been an alphabetical sort.

This patch adjusts this resolve by also taking into account whether one of two declarations are deprecated. If the new one is deprecated it may not replace a previous correction with a non-deprecated correction and a previous deprecated correction always gets replaced by a non-deprecated new correction.

Fixes https://github.com/llvm/llvm-project/issues/47272

Diff Detail

Event Timeline

zero9178 requested review of this revision.Jan 6 2022, 3:55 PM
zero9178 created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptJan 6 2022, 3:55 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript
Quuxplusone requested changes to this revision.Jan 6 2022, 6:48 PM
Quuxplusone added inline comments.
clang/lib/Sema/SemaLookup.cpp
4339

It seems like this code is overdue for a "compare these two corrections" predicate. The simple version would be

auto IsBetter = [&](const TypoCorrection& TC1, const TypoCorrection& TC2) {
    std::pair<bool, std::string> Key1 = { IsDeprecated(TC1.getFoundDecl()), TC1.getAsString(SemaRef.getLangOpts()) };
    std::pair<bool, std::string> Key2 = { IsDeprecated(TC2.getFoundDecl()), TC2.getAsString(SemaRef.getLangOpts()) };
    return Key1 < Key2;  // prefer non-deprecated, alphabetically earlier declarations
};
for (TypoCorrection& TC : CList) {
    if (IsBetter(Correction, TC))
        TC = Correction;
}

Actually, you could even replicate the hoisting of the loop-invariant stuff, the way the old code did:

std::pair<bool, std::string> CorrectionKey = { IsDeprecated(Correction.getFoundDecl()), Correction.getAsString(SemaRef.getLangOpts()) };
auto CorrectionIsBetterThan = [&](const TypoCorrection& TC) {
    std::pair<bool, std::string> Key = { IsDeprecated(TC.getFoundDecl()), TC.getAsString(SemaRef.getLangOpts()) };
    return CorrectionKey < Key;  // prefer non-deprecated, alphabetically earlier declarations
};
for (TypoCorrection& TC : CList) {
    if (CorrectionIsBetterThan(TC))
        TC = Correction;
}
clang/test/SemaCXX/typo-correction.cpp
764–769

I suggest three namespaces, named [[deprecated]] A, B, and [[deprecated]] C, and declared in that lexical order too, so that no matter how the preference predicate is expressed, we're still testing that the non-deprecated (middle) declaration gets chosen.

This revision now requires changes to proceed.Jan 6 2022, 6:48 PM
zero9178 updated this revision to Diff 398151.Jan 7 2022, 8:06 AM

Utilize operator< of std::pair to simplify the comparison A LOT, simplify using llvm::find_if and add additional test case

zero9178 marked 2 inline comments as done.Jan 7 2022, 8:08 AM
zero9178 added inline comments.
clang/lib/Sema/SemaLookup.cpp
4339

Thanks I really liked the idea of using std::pairs comparison for that.
I chose to separate the concerns, by first finding the declaration in the list and only then using the comparison of the found decl and the new correction.

aaron.ballman accepted this revision.Jan 11 2022, 6:44 AM

LGTM aside from some nits, though please wait for @Quuxplusone to respond before landing.

clang/lib/Sema/SemaLookup.cpp
4310–4339
4317

Feel free to pick a better name than D (other than Decl, please!), just cleaning up the coding style nit.

4327
4328–4332

LGTM % comments, but I'll take one more look.

clang/test/SemaCXX/typo-correction.cpp
772–782

I'd like B to be declared second.

namespace [[deprecated]] A { int pr47272; }
namespace B { using A::pr47272; } // expected-note{{'B::pr47272' declared here}}
namespace [[deprecated]] C { using A::pr47272; }

~~~
  int y = ::pr47272; // expected-error{{no member named 'pr47272' in the global namespace; did you mean 'B::pr47272'?}}

I also changed it from a function to a variable, just to get a little more coverage (since Take() was already covering functions).

zero9178 updated this revision to Diff 398962.Jan 11 2022, 8:29 AM

Addressed reviewer nits:

  • Used proper naming conventions
  • Adjusted tests
This revision is now accepted and ready to land.Jan 11 2022, 1:54 PM