mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-25 10:48:14 +00:00
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:
parent
e228d10709
commit
95995be7a3
@ -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();
|
||||
|
@ -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?");
|
||||
|
@ -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:
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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'?}}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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'}}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user