From: Kaelyn Uhrain Date: Wed, 10 Jul 2013 17:34:22 +0000 (+0000) Subject: Offer typo suggestions for 'using' declarations. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0daf1f4a0ff1cd6487c7040ac8f97eee48185536;p=clang Offer typo suggestions for 'using' declarations. Patch courtesy of Luke Zarko git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@186019 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index e42d249542..1c21b2cf20 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -7024,6 +7024,35 @@ void Sema::HideUsingShadowDecl(Scope *S, UsingShadowDecl *Shadow) { // be possible for this to happen, because...? } +class UsingValidatorCCC : public CorrectionCandidateCallback { +public: + UsingValidatorCCC(bool IsTypeName, bool IsInstantiation) + : IsTypeName(IsTypeName), IsInstantiation(IsInstantiation) {} + + virtual bool ValidateCandidate(const TypoCorrection &Candidate) { + if (NamedDecl *ND = Candidate.getCorrectionDecl()) { + if (isa(ND)) + return false; + // Completely unqualified names are invalid for a 'using' declaration. + bool droppedSpecifier = Candidate.WillReplaceSpecifier() && + !Candidate.getCorrectionSpecifier(); + if (droppedSpecifier) + return false; + else if (isa(ND)) + return IsTypeName || !IsInstantiation; + else + return !IsTypeName; + } else { + // Keywords are not valid here. + return false; + } + } + +private: + bool IsTypeName; + bool IsInstantiation; +}; + /// Builds a using declaration. /// /// \param IsInstantiation - Whether this call arises from an @@ -7133,11 +7162,32 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS, LookupQualifiedName(R, LookupContext); + // Try to correct typos if possible. if (R.empty()) { - Diag(IdentLoc, diag::err_no_member) - << NameInfo.getName() << LookupContext << SS.getRange(); - UD->setInvalidDecl(); - return UD; + UsingValidatorCCC CCC(IsTypeName, IsInstantiation); + if (TypoCorrection Corrected = CorrectTypo(R.getLookupNameInfo(), + R.getLookupKind(), S, &SS, CCC)){ + // We reject any correction for which ND would be NULL. + NamedDecl *ND = Corrected.getCorrectionDecl(); + std::string CorrectedStr(Corrected.getAsString(getLangOpts())); + std::string CorrectedQuotedStr(Corrected.getQuoted(getLangOpts())); + R.setLookupName(Corrected.getCorrection()); + R.addDecl(ND); + // We reject candidates where droppedSpecifier == true, hence the + // literal '0' below. + Diag(R.getNameLoc(), diag::err_no_member_suggest) + << NameInfo.getName() << LookupContext << 0 + << CorrectedQuotedStr << SS.getRange() + << FixItHint::CreateReplacement(Corrected.getCorrectionRange(), + CorrectedStr); + Diag(ND->getLocation(), diag::note_previous_decl) + << CorrectedQuotedStr; + } else { + Diag(IdentLoc, diag::err_no_member) + << NameInfo.getName() << LookupContext << SS.getRange(); + UD->setInvalidDecl(); + return UD; + } } if (R.isAmbiguous()) { diff --git a/test/FixIt/typo-using.cpp b/test/FixIt/typo-using.cpp new file mode 100644 index 0000000000..3644dea3b2 --- /dev/null +++ b/test/FixIt/typo-using.cpp @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: cp %s %t +// RUN: not %clang_cc1 -fsyntax-only -fixit -x c++ %t +// RUN: %clang_cc1 -fsyntax-only -pedantic -Werror -x c++ %t +// RUN: grep using_suggestion_tyname_ty_dropped_specifier %t + +// These tests have been separated from typo.cpp to keep the maximum typo +// correction counter from ticking over; this causes spurious failures. + +namespace using_suggestion_ty { +namespace N { class AAA {}; } // expected-note {{'AAA' declared here}} +using N::AAB; // expected-error {{no member named 'AAB' in namespace 'using_suggestion_ty::N'; did you mean 'AAA'?}} +} + +namespace using_suggestion_tyname_ty { +namespace N { class AAA {}; } // expected-note {{'AAA' declared here}} +using typename N::AAB; // expected-error {{no member named 'AAB' in namespace 'using_suggestion_tyname_ty::N'; did you mean 'AAA'?}} +} + +namespace using_suggestion_val { +namespace N { void FFF() {} } // expected-note {{'FFF' declared here}} +using N::FFG; // expected-error {{no member named 'FFG' in namespace 'using_suggestion_val::N'; did you mean 'FFF'?}} +} + +namespace using_suggestion_ty_dropped_specifier { +class AAA {}; // expected-note {{'::using_suggestion_ty_dropped_specifier::AAA' declared here}} +namespace N { } +using N::AAA; // expected-error {{no member named 'AAA' in namespace 'using_suggestion_ty_dropped_specifier::N'; did you mean '::using_suggestion_ty_dropped_specifier::AAA'?}} +} + +namespace using_suggestion_tyname_ty_dropped_specifier { +class AAA {}; // expected-note {{'::using_suggestion_tyname_ty_dropped_specifier::AAA' declared here}} +namespace N { } +using typename N::AAA; // expected-error {{no member named 'AAA' in namespace 'using_suggestion_tyname_ty_dropped_specifier::N'; did you mean '::using_suggestion_tyname_ty_dropped_specifier::AAA'?}} +} + +namespace using_suggestion_val_dropped_specifier { +void FFF() {} // expected-note {{'::using_suggestion_val_dropped_specifier::FFF' declared here}} +namespace N { } +using N::FFF; // expected-error {{no member named 'FFF' in namespace 'using_suggestion_val_dropped_specifier::N'; did you mean '::using_suggestion_val_dropped_specifier::FFF'?}} +} + +namespace using_suggestion_member_ty { +class CCC { public: typedef int AAA; }; // expected-note {{'AAA' declared here}} +class DDD : public CCC { public: using CCC::AAB; }; // expected-error {{no member named 'AAB' in 'using_suggestion_member_ty::CCC'; did you mean 'AAA'?}} +} + +namespace using_suggestion_member_val { +class CCC { public: void AAA() { } }; // expected-note {{'AAA' declared here}} +class DDD : public CCC { public: using CCC::AAB; }; // expected-error {{no member named 'AAB' in 'using_suggestion_member_val::CCC'; did you mean 'AAA'?}} +} + +namespace using_suggestion_member_tyname_ty { +class CCC { public: typedef int AAA; }; // expected-note {{'AAA' declared here}} +class DDD : public CCC { public: using typename CCC::AAB; }; // expected-error {{no member named 'AAB' in 'using_suggestion_member_tyname_ty::CCC'; did you mean 'AAA'?}} +} diff --git a/test/SemaCXX/using-decl-1.cpp b/test/SemaCXX/using-decl-1.cpp index ebe97f6cca..41710cc909 100644 --- a/test/SemaCXX/using-decl-1.cpp +++ b/test/SemaCXX/using-decl-1.cpp @@ -118,3 +118,45 @@ namespace foo using ::foo::Class1::Function; // expected-error{{incomplete type 'foo::Class1' named in nested name specifier}} }; } + +// Don't suggest non-typenames for positions requiring typenames. +namespace using_suggestion_tyname_val { +namespace N { void FFF() {} } +using typename N::FFG; // expected-error {{no member named 'FFG' in namespace 'using_suggestion_tyname_val::N'}} +} + +namespace using_suggestion_member_tyname_val { +class CCC { public: void AAA() { } }; +class DDD : public CCC { public: using typename CCC::AAB; }; // expected-error {{no member named 'AAB' in 'using_suggestion_member_tyname_val::CCC'}} +} + +namespace using_suggestion_tyname_val_dropped_specifier { +void FFF() {} +namespace N { } +using typename N::FFG; // expected-error {{no member named 'FFG' in namespace 'using_suggestion_tyname_val_dropped_specifier::N'}} +} + +// Currently hints aren't provided to drop out the incorrect M::. +namespace using_suggestion_ty_dropped_nested_specifier { +namespace N { +class AAA {}; +namespace M { } +} +using N::M::AAA; // expected-error {{no member named 'AAA' in namespace 'using_suggestion_ty_dropped_nested_specifier::N::M'}} +} + +namespace using_suggestion_tyname_ty_dropped_nested_specifier { +namespace N { +class AAA {}; +namespace M { } +} +using typename N::M::AAA; // expected-error {{no member named 'AAA' in namespace 'using_suggestion_tyname_ty_dropped_nested_specifier::N::M'}} +} + +namespace using_suggestion_val_dropped_nested_specifier { +namespace N { +void FFF() {} +namespace M { } +} +using N::M::FFF; // expected-error {{no member named 'FFF' in namespace 'using_suggestion_val_dropped_nested_specifier::N::M'}} +}