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

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

Differential Revision: https://reviews.llvm.org/D116775
This commit is contained in:
Markus Böck 2022-01-10 08:42:45 +01:00
parent 85e6e748d4
commit 6479f03dae
2 changed files with 60 additions and 12 deletions

View File

@ -4307,18 +4307,35 @@ void TypoCorrectionConsumer::addCorrection(TypoCorrection Correction) {
if (!CList.empty() && !CList.back().isResolved())
CList.pop_back();
if (NamedDecl *NewND = Correction.getCorrectionDecl()) {
std::string CorrectionStr = Correction.getAsString(SemaRef.getLangOpts());
for (TypoResultList::iterator RI = CList.begin(), RIEnd = CList.end();
RI != RIEnd; ++RI) {
// If the Correction refers to a decl already in the result list,
// replace the existing result if the string representation of Correction
// comes before the current result alphabetically, then stop as there is
// nothing more to be done to add Correction to the candidate set.
if (RI->getCorrectionDecl() == NewND) {
if (CorrectionStr < RI->getAsString(SemaRef.getLangOpts()))
*RI = Correction;
return;
}
auto RI = llvm::find_if(CList, [NewND](const TypoCorrection &TypoCorr) {
return TypoCorr.getCorrectionDecl() == NewND;
});
if (RI != CList.end()) {
// The Correction refers to a decl already in the list. No insertion is
// necessary and all further cases will return.
auto IsDeprecated = [](Decl *D) {
while (D) {
if (D->isDeprecated())
return true;
D = llvm::dyn_cast_or_null<NamespaceDecl>(D->getDeclContext());
}
return false;
};
// Prefer non deprecated Corrections over deprecated and only then
// sort using an alphabetical order.
std::pair<bool, std::string> NewKey = {
IsDeprecated(Correction.getFoundDecl()),
Correction.getAsString(SemaRef.getLangOpts())};
std::pair<bool, std::string> PrevKey = {
IsDeprecated(RI->getFoundDecl()),
RI->getAsString(SemaRef.getLangOpts())};
if (NewKey < PrevKey)
*RI = Correction;
return;
}
}
if (CList.empty() || Correction.isResolved())

View File

@ -757,3 +757,34 @@ namespace PR46487 {
b = g_volatile_uchar // expected-error {{did you mean 'g_volatile_char'}}
};
}
namespace PR47272
{
namespace Views {
int Take(); // expected-note{{'Views::Take' declared here}}
}
namespace [[deprecated("use Views instead")]] View {
using Views::Take;
}
namespace [[deprecated]] A { // expected-note{{'A' has been explicitly marked deprecated here}}
int pr47272;
}
namespace B {
using A::pr47272; // expected-note{{'B::pr47272' declared here}} expected-warning{{'A' is deprecated}}
}
namespace [[deprecated]] C {
using A::pr47272;
}
void function() {
int x = ::Take(); // expected-error{{no member named 'Take' in the global namespace; did you mean 'Views::Take'?}}
int y = ::pr47272; // expected-error{{no member named 'pr47272' in the global namespace; did you mean 'B::pr47272'?}}
}
}