From 6479f03daec1786d1e45f9148bd9298caafba9d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20B=C3=B6ck?= Date: Mon, 10 Jan 2022 08:42:45 +0100 Subject: [PATCH] [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 --- clang/lib/Sema/SemaLookup.cpp | 41 ++++++++++++++++++-------- clang/test/SemaCXX/typo-correction.cpp | 31 +++++++++++++++++++ 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 635e93ba8460..c905a10990d2 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -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(D->getDeclContext()); + } + return false; + }; + + // Prefer non deprecated Corrections over deprecated and only then + // sort using an alphabetical order. + std::pair NewKey = { + IsDeprecated(Correction.getFoundDecl()), + Correction.getAsString(SemaRef.getLangOpts())}; + + std::pair PrevKey = { + IsDeprecated(RI->getFoundDecl()), + RI->getAsString(SemaRef.getLangOpts())}; + + if (NewKey < PrevKey) + *RI = Correction; + return; } } if (CList.empty() || Correction.isResolved()) diff --git a/clang/test/SemaCXX/typo-correction.cpp b/clang/test/SemaCXX/typo-correction.cpp index 8b13e43661ee..7eca5d80770d 100644 --- a/clang/test/SemaCXX/typo-correction.cpp +++ b/clang/test/SemaCXX/typo-correction.cpp @@ -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'?}} +} +} + +