r191449 - Teach typo correction to look inside of classes like it does namespaces.
Kaelyn Uhrain
rikka at google.com
Thu Sep 26 12:10:30 PDT 2013
Author: rikka
Date: Thu Sep 26 14:10:29 2013
New Revision: 191449
URL: http://llvm.org/viewvc/llvm-project?rev=191449&view=rev
Log:
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.
Modified:
cfe/trunk/include/clang/Sema/TypoCorrection.h
cfe/trunk/lib/Sema/SemaAccess.cpp
cfe/trunk/lib/Sema/SemaLookup.cpp
cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp
cfe/trunk/test/CXX/class/class.nested.type/p1.cpp
cfe/trunk/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp
cfe/trunk/test/FixIt/typo-using.cpp
cfe/trunk/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp
cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp
Modified: cfe/trunk/include/clang/Sema/TypoCorrection.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/TypoCorrection.h?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/TypoCorrection.h (original)
+++ cfe/trunk/include/clang/Sema/TypoCorrection.h Thu Sep 26 14:10:29 2013
@@ -137,6 +137,11 @@ public:
return dyn_cast_or_null<DeclClass>(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();
Modified: cfe/trunk/lib/Sema/SemaAccess.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaAccess.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaAccess.cpp (original)
+++ cfe/trunk/lib/Sema/SemaAccess.cpp Thu Sep 26 14:10:29 2013
@@ -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?");
Modified: cfe/trunk/lib/Sema/SemaLookup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLookup.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLookup.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLookup.cpp Thu Sep 26 14:10:29 2013
@@ -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
DistanceMap[NumSpecifiers].push_back(SpecifierInfo(Ctx, NNS, NumSpecifiers));
}
+void NamespaceSpecifierSet::AddRecord(RecordDecl *RD) {
+ if (!RD->isBeingDefined() && !RD->isCompleteDefinition())
+ return;
+
+ DeclContext *Ctx = cast<DeclContext>(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<NamespaceDecl>(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<NamespaceDecl>(*C)) {
+ NNS = NestedNameSpecifier::Create(Context, NNS, ND);
+ ++NumSpecifiers;
+ } else if (RecordDecl *RD = dyn_cast_or_null<RecordDecl>(*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<const IdentifierInfo*, 4> NewNameSpecifierIdentifiers;
+ getNestedNameSpecifierIdentifiers(NNS, NewNameSpecifierIdentifiers);
+ NumSpecifiers = llvm::ComputeEditDistance(
+ ArrayRef<const IdentifierInfo *>(CurNameSpecifierIdentifiers),
+ ArrayRef<const IdentifierInfo *>(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 D
for (unsigned I = 0, N = ExternalKnownNamespaces.size(); I != N; ++I)
KnownNamespaces[ExternalKnownNamespaces[I]] = true;
}
-
- for (llvm::MapVector<NamespaceDecl*, bool>::iterator
+
+ for (llvm::MapVector<NamespaceDecl*, bool>::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:
Modified: cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp (original)
+++ cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp Thu Sep 26 14:10:29 2013
@@ -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 {
Modified: cfe/trunk/test/CXX/class/class.nested.type/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class/class.nested.type/p1.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/test/CXX/class/class.nested.type/p1.cpp (original)
+++ cfe/trunk/test/CXX/class/class.nested.type/p1.cpp Thu Sep 26 14:10:29 2013
@@ -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;
Modified: cfe/trunk/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp (original)
+++ cfe/trunk/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp Thu Sep 26 14:10:29 2013
@@ -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'?}}
}
Modified: cfe/trunk/test/FixIt/typo-using.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/FixIt/typo-using.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/test/FixIt/typo-using.cpp (original)
+++ cfe/trunk/test/FixIt/typo-using.cpp Thu Sep 26 14:10:29 2013
@@ -23,21 +23,21 @@ using N::FFG; // expected-error {{no mem
}
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 {
Modified: cfe/trunk/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp (original)
+++ cfe/trunk/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp Thu Sep 26 14:10:29 2013
@@ -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'
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'}}
}
Modified: cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp?rev=191449&r1=191448&r2=191449&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp (original)
+++ cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp Thu Sep 26 14:10:29 2013
@@ -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;
+ }
+}
+}
More information about the cfe-commits
mailing list