summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaelyn Uhrain <rikka@google.com>2013-09-26 19:10:29 +0000
committerKaelyn Uhrain <rikka@google.com>2013-09-26 19:10:29 +0000
commit3d9559b91c00757b296354cc6ca93e899266c7d2 (patch)
tree5a6868ce41f3fe5e7e51139528724beac04102c7
parent65ea687b8abbfc892e8d91bba477999b0379b07b (diff)
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
-rw-r--r--include/clang/Sema/TypoCorrection.h5
-rw-r--r--lib/Sema/SemaAccess.cpp2
-rw-r--r--lib/Sema/SemaLookup.cpp107
-rw-r--r--test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp8
-rw-r--r--test/CXX/class/class.nested.type/p1.cpp8
-rw-r--r--test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p10.cpp6
-rw-r--r--test/FixIt/typo-using.cpp12
-rw-r--r--test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp8
-rw-r--r--test/SemaCXX/typo-correction-pt2.cpp61
9 files changed, 191 insertions, 26 deletions
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<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();
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<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 DeclarationNameInfo &TypoName,
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:
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;
+ }
+}
+}