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.

llvm-svn: 191449
This commit is contained in:
Kaelyn Uhrain 2013-09-26 19:10:29 +00:00
parent e228d10709
commit 95995be7a3
9 changed files with 191 additions and 26 deletions

View File

@ -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();

View File

@ -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?");

View File

@ -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:

View File

@ -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 {

View File

@ -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;

View File

@ -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'?}}
}

View File

@ -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 {

View File

@ -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'}}
}

View File

@ -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;
}
}
}