From: Kaelyn Uhrain Date: Thu, 26 Sep 2013 19:10:29 +0000 (+0000) Subject: Teach typo correction to look inside of classes like it does namespaces. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3d9559b91c00757b296354cc6ca93e899266c7d2;p=clang Teach typo correction to look inside of classes like it does namespaces. Unlike with namespaces, searching inside of classes requires also checking the access to correction candidates (i.e. don't suggest a correction to a private class member for a correction occurring outside that class and its methods or friends). Included is a small (one line) fix for a bug, that was uncovered while cleaning up the unit tests, where the decls from a TypoCorrection candidate were preserved in new TypoCorrection candidates that are derived (copied) from the old TypoCorrection--notably when creating a new candidate by changing the NestedNameSpecifier associated with the base idenitifer. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@191449 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/TypoCorrection.h b/include/clang/Sema/TypoCorrection.h index 0deb74201c..f0b7726498 100644 --- a/include/clang/Sema/TypoCorrection.h +++ b/include/clang/Sema/TypoCorrection.h @@ -137,6 +137,11 @@ public: return dyn_cast_or_null(getCorrectionDecl()); } + /// \brief Clears the list of NamedDecls. + void ClearCorrectionDecls() { + CorrectionDecls.clear(); + } + /// \brief Clears the list of NamedDecls before adding the new one. void setCorrectionDecl(NamedDecl *CDecl) { CorrectionDecls.clear(); diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp index 4ecbf0ba77..6dbfad4e18 100644 --- a/lib/Sema/SemaAccess.cpp +++ b/lib/Sema/SemaAccess.cpp @@ -1390,6 +1390,8 @@ static AccessResult IsAccessible(Sema &S, CXXBasePath *Path = FindBestPath(S, EC, Entity, FinalAccess, Paths); if (!Path) return AR_dependent; + if (Path->Access == AS_none) // This can happen during typo correction. + return AR_inaccessible; assert(Path->Access <= UnprivilegedAccess && "access along best path worse than direct?"); diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index a645986ac3..75b0f984f8 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -3601,6 +3601,10 @@ class NamespaceSpecifierSet { /// NestedNameSpecifier and its distance in the process. void AddNamespace(NamespaceDecl *ND); + /// \brief Add the record to the set, computing the corresponding + /// NestedNameSpecifier and its distance in the process. + void AddRecord(RecordDecl *RD); + typedef SpecifierInfoList::iterator iterator; iterator begin() { if (!isSorted) SortNamespaces(); @@ -3702,6 +3706,72 @@ void NamespaceSpecifierSet::AddNamespace(NamespaceDecl *ND) { DistanceMap[NumSpecifiers].push_back(SpecifierInfo(Ctx, NNS, NumSpecifiers)); } +void NamespaceSpecifierSet::AddRecord(RecordDecl *RD) { + if (!RD->isBeingDefined() && !RD->isCompleteDefinition()) + return; + + DeclContext *Ctx = cast(RD); + NestedNameSpecifier *NNS = NULL; + unsigned NumSpecifiers = 0; + DeclContextList NamespaceDeclChain(BuildContextChain(Ctx)); + DeclContextList FullNamespaceDeclChain(NamespaceDeclChain); + + // Eliminate common elements from the two DeclContext chains. + for (DeclContextList::reverse_iterator C = CurContextChain.rbegin(), + CEnd = CurContextChain.rend(); + C != CEnd && !NamespaceDeclChain.empty() && + NamespaceDeclChain.back() == *C; ++C) { + NamespaceDeclChain.pop_back(); + } + + // Add an explicit leading '::' specifier if needed. + if (NamespaceDeclChain.empty()) { + NamespaceDeclChain = FullNamespaceDeclChain; + NNS = NestedNameSpecifier::GlobalSpecifier(Context); + } else if (NamespaceDecl *ND = + dyn_cast_or_null(NamespaceDeclChain.back())) { + IdentifierInfo *Name = ND->getIdentifier(); + if (std::find(CurContextIdentifiers.begin(), CurContextIdentifiers.end(), + Name) != CurContextIdentifiers.end() || + std::find(CurNameSpecifierIdentifiers.begin(), + CurNameSpecifierIdentifiers.end(), + Name) != CurNameSpecifierIdentifiers.end()) { + NamespaceDeclChain = FullNamespaceDeclChain; + NNS = NestedNameSpecifier::GlobalSpecifier(Context); + } + } + + // Build the NestedNameSpecifier from what is left of the NamespaceDeclChain + for (DeclContextList::reverse_iterator C = NamespaceDeclChain.rbegin(), + CEnd = NamespaceDeclChain.rend(); + C != CEnd; ++C) { + if (NamespaceDecl *ND = dyn_cast_or_null(*C)) { + NNS = NestedNameSpecifier::Create(Context, NNS, ND); + ++NumSpecifiers; + } else if (RecordDecl *RD = dyn_cast_or_null(*C)) { + NNS = NestedNameSpecifier::Create(Context, NNS, RD->isTemplateDecl(), + RD->getTypeForDecl()); + ++NumSpecifiers; + } + } + + // If the built NestedNameSpecifier would be replacing an existing + // NestedNameSpecifier, use the number of component identifiers that + // would need to be changed as the edit distance instead of the number + // of components in the built NestedNameSpecifier. + if (NNS && !CurNameSpecifierIdentifiers.empty()) { + SmallVector NewNameSpecifierIdentifiers; + getNestedNameSpecifierIdentifiers(NNS, NewNameSpecifierIdentifiers); + NumSpecifiers = llvm::ComputeEditDistance( + ArrayRef(CurNameSpecifierIdentifiers), + ArrayRef(NewNameSpecifierIdentifiers)); + } + + isSorted = false; + Distances.insert(NumSpecifiers); + DistanceMap[NumSpecifiers].push_back(SpecifierInfo(Ctx, NNS, NumSpecifiers)); +} + /// \brief Perform name lookup for a possible result for typo correction. static void LookupPotentialTypoResult(Sema &SemaRef, LookupResult &Res, @@ -4153,12 +4223,22 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, for (unsigned I = 0, N = ExternalKnownNamespaces.size(); I != N; ++I) KnownNamespaces[ExternalKnownNamespaces[I]] = true; } - - for (llvm::MapVector::iterator + + for (llvm::MapVector::iterator KNI = KnownNamespaces.begin(), KNIEnd = KnownNamespaces.end(); KNI != KNIEnd; ++KNI) Namespaces.AddNamespace(KNI->first); + + for (ASTContext::type_iterator TI = Context.types_begin(), + TIEnd = Context.types_end(); + TI != TIEnd; ++TI) { + if (CXXRecordDecl *CD = (*TI)->getAsCXXRecordDecl()) { + if (!CD->isDependentType() && !CD->isAnonymousStructOrUnion() && + !CD->isUnion()) + Namespaces.AddRecord(CD); + } + } } // Weed out any names that could not be found by name lookup or, if a @@ -4295,6 +4375,17 @@ retry_lookup: NIEnd = Namespaces.end(); NI != NIEnd; ++NI) { DeclContext *Ctx = NI->DeclCtx; + const Type *NSType = NI->NameSpecifier->getAsType(); + + // If the current NestedNameSpecifier refers to a class and the + // current correction candidate is the name of that class, then skip + // it as it is unlikely a qualified version of the class' constructor + // is an appropriate correction. + if (CXXRecordDecl *NSDecl = + NSType ? NSType->getAsCXXRecordDecl() : 0) { + if (NSDecl->getIdentifier() == QRI->getCorrectionAsIdentifierInfo()) + continue; + } // FIXME: Stop searching once the namespaces are too far away to create // acceptable corrections for this identifier (since the namespaces @@ -4310,14 +4401,20 @@ retry_lookup: case LookupResult::Found: case LookupResult::FoundOverloaded: { TypoCorrection TC(*QRI); + TC.ClearCorrectionDecls(); TC.setCorrectionSpecifier(NI->NameSpecifier); TC.setQualifierDistance(NI->EditDistance); TC.setCallbackDistance(0); // Reset the callback distance for (LookupResult::iterator TRD = TmpRes.begin(), TRDEnd = TmpRes.end(); - TRD != TRDEnd; ++TRD) - TC.addCorrectionDecl(*TRD); - Consumer.addCorrection(TC); + TRD != TRDEnd; ++TRD) { + if (CheckMemberAccess(TC.getCorrectionRange().getBegin(), + NSType ? NSType->getAsCXXRecordDecl() : 0, + *TRD) == AR_accessible) + TC.addCorrectionDecl(*TRD); + } + if (TC.isResolved()) + Consumer.addCorrection(TC); break; } case LookupResult::NotFound: diff --git a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp index df9a2cd14c..e61df3e586 100644 --- a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp +++ b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp @@ -5,7 +5,7 @@ namespace N { X operator+(X, X); - void f(X); + void f(X); // expected-note 2 {{'N::f' declared here}} void g(X); // expected-note{{candidate function}} void test_multiadd(X x) { @@ -17,7 +17,7 @@ namespace M { struct Y : N::X { }; } -void f(); // expected-note 2 {{'f' declared here}} +void f(); void test_operator_adl(N::X x, M::Y y) { (void)(x + x); @@ -27,8 +27,8 @@ void test_operator_adl(N::X x, M::Y y) { void test_func_adl(N::X x, M::Y y) { f(x); f(y); - (f)(x); // expected-error{{too many arguments to function call}} - ::f(x); // expected-error{{too many arguments to function call}} + (f)(x); // expected-error{{too many arguments to function call, expected 0, have 1; did you mean 'N::f'?}} + ::f(x); // expected-error{{too many arguments to function call, expected 0, have 1; did you mean 'N::f'?}} } namespace N { diff --git a/test/CXX/class/class.nested.type/p1.cpp b/test/CXX/class/class.nested.type/p1.cpp index 4a04a44859..9295654412 100644 --- a/test/CXX/class/class.nested.type/p1.cpp +++ b/test/CXX/class/class.nested.type/p1.cpp @@ -2,12 +2,12 @@ class X { public: - typedef int I; - class Y { }; + typedef int I; // expected-note{{'X::I' declared here}} + class Y { }; // expected-note{{'X::Y' declared here}} I a; }; -I b; // expected-error{{unknown type name 'I'}} -Y c; // expected-error{{unknown type name 'Y'}} +I b; // expected-error{{unknown type name 'I'; did you mean 'X::I'?}} +Y c; // expected-error{{unknown type name 'Y'; did you mean 'X::Y'?}} X::Y d; X::I e; diff --git a/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp b/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp index 385e45dadf..27ebb8e036 100644 --- a/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp +++ b/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp @@ -1,16 +1,16 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s struct A { - virtual void f(int a = 7); + virtual void f(int a = 7); // expected-note{{'A::f' declared here}} }; struct B : public A { - void f(int a); // expected-note{{'f' declared here}} + void f(int a); }; void m() { B* pb = new B; A* pa = pb; pa->f(); // OK, calls pa->B::f(7) - pb->f(); // expected-error{{too few arguments}} + pb->f(); // expected-error{{too few arguments to function call, expected 1, have 0; did you mean 'A::f'?}} } diff --git a/test/FixIt/typo-using.cpp b/test/FixIt/typo-using.cpp index 3644dea3b2..e676b1074f 100644 --- a/test/FixIt/typo-using.cpp +++ b/test/FixIt/typo-using.cpp @@ -23,21 +23,21 @@ using N::FFG; // expected-error {{no member named 'FFG' in namespace 'using_sugg } namespace using_suggestion_ty_dropped_specifier { -class AAA {}; // expected-note {{'::using_suggestion_ty_dropped_specifier::AAA' declared here}} +class ABC {}; // expected-note {{'::using_suggestion_ty_dropped_specifier::ABC' 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'?}} +using N::ABC; // expected-error {{no member named 'ABC' in namespace 'using_suggestion_ty_dropped_specifier::N'; did you mean '::using_suggestion_ty_dropped_specifier::ABC'?}} } namespace using_suggestion_tyname_ty_dropped_specifier { -class AAA {}; // expected-note {{'::using_suggestion_tyname_ty_dropped_specifier::AAA' declared here}} +class BCD {}; // expected-note {{'::using_suggestion_tyname_ty_dropped_specifier::BCD' 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'?}} +using typename N::BCD; // expected-error {{no member named 'BCD' in namespace 'using_suggestion_tyname_ty_dropped_specifier::N'; did you mean '::using_suggestion_tyname_ty_dropped_specifier::BCD'?}} } namespace using_suggestion_val_dropped_specifier { -void FFF() {} // expected-note {{'::using_suggestion_val_dropped_specifier::FFF' declared here}} +void EFG() {} // expected-note {{'::using_suggestion_val_dropped_specifier::EFG' 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'?}} +using N::EFG; // expected-error {{no member named 'EFG' in namespace 'using_suggestion_val_dropped_specifier::N'; did you mean '::using_suggestion_val_dropped_specifier::EFG'?}} } namespace using_suggestion_member_ty { diff --git a/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp b/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp index 40bcf45bca..6195e03955 100644 --- a/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp +++ b/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp @@ -19,7 +19,7 @@ namespace fizbin { // expected-note{{'fizbin::nested::lessFoobar' declared here}} class dummy { // expected-note 2 {{'fizbin::dummy' declared here}} public: - static bool moreFoobar() { return false; } // expected-note{{'moreFoobar' declared here}} + static bool morebar() { return false; } // expected-note{{'morebar' declared here}} }; } void Check() { // expected-note{{'Check' declared here}} @@ -29,9 +29,9 @@ void Check() { // expected-note{{'Check' declared here}} if (lessFoobar()) Double(7); // expected-error{{use of undeclared identifier 'lessFoobar'; did you mean 'fizbin::nested::lessFoobar'?}} if (baztool::toFoobar()) Double(7); // expected-error{{use of undeclared identifier 'baztool'; did you mean 'fizbin::baztool'?}} if (nested::moreFoobar()) Double(7); // expected-error{{use of undeclared identifier 'nested'; did you mean 'fizbin::nested'?}} - if (dummy::moreFoobar()) Double(7); // expected-error{{use of undeclared identifier 'dummy'; did you mean 'fizbin::dummy'?}} - if (dummy::mreFoobar()) Double(7); // expected-error{{use of undeclared identifier 'dummy'; did you mean 'fizbin::dummy'?}} \ - // expected-error{{no member named 'mreFoobar' in 'fizbin::dummy'; did you mean 'moreFoobar'?}} + if (dummy::morebar()) Double(7); // expected-error{{use of undeclared identifier 'dummy'; did you mean 'fizbin::dummy'?}} + if (dummy::mrebar()) Double(7); // expected-error{{use of undeclared identifier 'dummy'; did you mean 'fizbin::dummy'?}} \ + // expected-error{{no member named 'mrebar' in 'fizbin::dummy'; did you mean 'morebar'?}} if (moFoobin()) Double(7); // expected-error{{use of undeclared identifier 'moFoobin'}} } diff --git a/test/SemaCXX/typo-correction-pt2.cpp b/test/SemaCXX/typo-correction-pt2.cpp index 06f69d7186..f475b41a9f 100644 --- a/test/SemaCXX/typo-correction-pt2.cpp +++ b/test/SemaCXX/typo-correction-pt2.cpp @@ -33,3 +33,64 @@ void test(Foo F, int num) { F.B(num); // expected-error {{too many arguments to function call, expected 0, have 1; did you mean '::TemplateFunction::B'?}} } } +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 class_member_typo_corrections { +class Outer { +public: + class Inner {}; // expected-note {{'Outer::Inner' declared here}} + Inner MyMethod(Inner arg); +}; + +Inner Outer::MyMethod(Inner arg) { // expected-error {{unknown type name 'Inner'; did you mean 'Outer::Inner'?}} + // TODO: Recovery needs to be fixed/added for the typo-correction of the + // return type so the below error isn't still generated. + return Inner(); // expected-error {{no viable conversion from 'class_member_typo_corrections::Outer::Inner' to 'int'}} +} + +class Result { +public: + enum ResultType { + ENTITY, // expected-note {{'Result::ENTITY' declared here}} + PREDICATE, // expected-note {{'Result::PREDICATE' declared here}} + LITERAL // expected-note {{'Result::LITERAL' declared here}} + }; + + ResultType type(); +}; + +void test() { + Result result_cell; + switch (result_cell.type()) { + case ENTITY: // expected-error {{use of undeclared identifier 'ENTITY'; did you mean 'Result::ENTITY'?}} + case LITERAL: // expected-error {{use of undeclared identifier 'LITERAL'; did you mean 'Result::LITERAL'?}} + case PREDICAT: // expected-error {{use of undeclared identifier 'PREDICAT'; did you mean 'Result::PREDICATE'?}} + break; + } +} + +class Figure { + enum ResultType { + SQUARE, + TRIANGLE, + CIRCLE + }; + +public: + ResultType type(); +}; + +void testAccess() { + Figure obj; + switch (obj.type()) { // expected-warning {{enumeration values 'SQUARE', 'TRIANGLE', and 'CIRCLE' not handled in switch}} + case SQUARE: // expected-error-re {{use of undeclared identifier 'SQUARE'$}} + case TRIANGLE: // expected-error-re {{use of undeclared identifier 'TRIANGLE'$}} + case CIRCE: // expected-error-re {{use of undeclared identifier 'CIRCE'$}} + break; + } +} +}