aboutsummaryrefslogtreecommitdiff
path: root/lib/Sema/SemaLookup.cpp
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2010-01-03 18:01:57 +0000
committerDouglas Gregor <dgregor@apple.com>2010-01-03 18:01:57 +0000
commitf06cdae9c68dfc4191fbf6b9e5ea0fd748488d88 (patch)
tree2354bfa622035a1dd2cf4d18161aee9acd92e1f0 /lib/Sema/SemaLookup.cpp
parent368a55d3ce5d66c6d0502c6f8bf061c06961042c (diff)
Implement typo correction for a variety of Objective-C-specific
constructs: - Instance variable lookup ("foo->ivar" and, in instance methods, "ivar") - Property name lookup ("foo.prop") - Superclasses - Various places where a class name is required - Protocol names (e.g., id<proto>) This seems to cover many of the common places where typos could occur. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@92449 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaLookup.cpp')
-rw-r--r--lib/Sema/SemaLookup.cpp95
1 files changed, 89 insertions, 6 deletions
diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp
index b86b31896c..1f2943cb1f 100644
--- a/lib/Sema/SemaLookup.cpp
+++ b/lib/Sema/SemaLookup.cpp
@@ -1913,7 +1913,7 @@ static void LookupVisibleDecls(DeclContext *Ctx, LookupResult &Result,
}
}
- // Traverse the contexts of inherited classes.
+ // Traverse the contexts of inherited C++ classes.
if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Ctx)) {
for (CXXRecordDecl::base_class_iterator B = Record->bases_begin(),
BEnd = Record->bases_end();
@@ -1955,7 +1955,42 @@ static void LookupVisibleDecls(DeclContext *Ctx, LookupResult &Result,
}
}
- // FIXME: Look into base classes in Objective-C!
+ // Traverse the contexts of Objective-C classes.
+ if (ObjCInterfaceDecl *IFace = dyn_cast<ObjCInterfaceDecl>(Ctx)) {
+ // Traverse categories.
+ for (ObjCCategoryDecl *Category = IFace->getCategoryList();
+ Category; Category = Category->getNextClassCategory()) {
+ ShadowContextRAII Shadow(Visited);
+ LookupVisibleDecls(Category, Result, QualifiedNameLookup, Consumer,
+ Visited);
+ }
+
+ // Traverse protocols.
+ for (ObjCInterfaceDecl::protocol_iterator I = IFace->protocol_begin(),
+ E = IFace->protocol_end(); I != E; ++I) {
+ ShadowContextRAII Shadow(Visited);
+ LookupVisibleDecls(*I, Result, QualifiedNameLookup, Consumer, Visited);
+ }
+
+ // Traverse the superclass.
+ if (IFace->getSuperClass()) {
+ ShadowContextRAII Shadow(Visited);
+ LookupVisibleDecls(IFace->getSuperClass(), Result, QualifiedNameLookup,
+ Consumer, Visited);
+ }
+ } else if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(Ctx)) {
+ for (ObjCProtocolDecl::protocol_iterator I = Protocol->protocol_begin(),
+ E = Protocol->protocol_end(); I != E; ++I) {
+ ShadowContextRAII Shadow(Visited);
+ LookupVisibleDecls(*I, Result, QualifiedNameLookup, Consumer, Visited);
+ }
+ } else if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(Ctx)) {
+ for (ObjCCategoryDecl::protocol_iterator I = Category->protocol_begin(),
+ E = Category->protocol_end(); I != E; ++I) {
+ ShadowContextRAII Shadow(Visited);
+ LookupVisibleDecls(*I, Result, QualifiedNameLookup, Consumer, Visited);
+ }
+ }
}
static void LookupVisibleDecls(Scope *S, LookupResult &Result,
@@ -1975,6 +2010,22 @@ static void LookupVisibleDecls(Scope *S, LookupResult &Result,
for (DeclContext *Ctx = Entity; Ctx && Ctx->getPrimaryContext() != OuterCtx;
Ctx = Ctx->getLookupParent()) {
+ if (ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(Ctx)) {
+ if (Method->isInstanceMethod()) {
+ // For instance methods, look for ivars in the method's interface.
+ LookupResult IvarResult(Result.getSema(), Result.getLookupName(),
+ Result.getNameLoc(), Sema::LookupMemberName);
+ ObjCInterfaceDecl *IFace = Method->getClassInterface();
+ LookupVisibleDecls(IFace, IvarResult, /*QualifiedNameLookup=*/false,
+ Consumer, Visited);
+ }
+
+ // We've already performed all of the name lookup that we need
+ // to for Objective-C methods; the next context will be the
+ // outer scope.
+ break;
+ }
+
if (Ctx->isFunctionOrMethod())
continue;
@@ -2139,11 +2190,15 @@ void TypoCorrectionConsumer::FoundDecl(NamedDecl *ND, NamedDecl *Hiding) {
/// \param EnteringContext whether we're entering the context described by
/// the nested-name-specifier SS.
///
+/// \param OPT when non-NULL, the search for visible declarations will
+/// also walk the protocols in the qualified interfaces of \p OPT.
+///
/// \returns true if the typo was corrected, in which case the \p Res
/// structure will contain the results of name lookup for the
/// corrected name. Otherwise, returns false.
bool Sema::CorrectTypo(LookupResult &Res, Scope *S, const CXXScopeSpec *SS,
- DeclContext *MemberContext, bool EnteringContext) {
+ DeclContext *MemberContext, bool EnteringContext,
+ const ObjCObjectPointerType *OPT) {
// We only attempt to correct typos for identifiers.
IdentifierInfo *Typo = Res.getLookupName().getAsIdentifierInfo();
if (!Typo)
@@ -2160,9 +2215,17 @@ bool Sema::CorrectTypo(LookupResult &Res, Scope *S, const CXXScopeSpec *SS,
return false;
TypoCorrectionConsumer Consumer(Typo);
- if (MemberContext)
+ if (MemberContext) {
LookupVisibleDecls(MemberContext, Res.getLookupKind(), Consumer);
- else if (SS && SS->isSet()) {
+
+ // Look in qualified interfaces.
+ if (OPT) {
+ for (ObjCObjectPointerType::qual_iterator
+ I = OPT->qual_begin(), E = OPT->qual_end();
+ I != E; ++I)
+ LookupVisibleDecls(*I, Res.getLookupKind(), Consumer);
+ }
+ } else if (SS && SS->isSet()) {
DeclContext *DC = computeDeclContext(*SS, EnteringContext);
if (!DC)
return false;
@@ -2179,10 +2242,22 @@ bool Sema::CorrectTypo(LookupResult &Res, Scope *S, const CXXScopeSpec *SS,
// have overloads of that name, though).
TypoCorrectionConsumer::iterator I = Consumer.begin();
DeclarationName BestName = (*I)->getDeclName();
+
+ // If we've found an Objective-C ivar or property, don't perform
+ // name lookup again; we'll just return the result directly.
+ NamedDecl *FoundBest = 0;
+ if (isa<ObjCIvarDecl>(*I) || isa<ObjCPropertyDecl>(*I))
+ FoundBest = *I;
++I;
for(TypoCorrectionConsumer::iterator IEnd = Consumer.end(); I != IEnd; ++I) {
if (BestName != (*I)->getDeclName())
return false;
+
+ // FIXME: If there are both ivars and properties of the same name,
+ // don't return both because the callee can't handle two
+ // results. We really need to separate ivar lookup from property
+ // lookup to avoid this problem.
+ FoundBest = 0;
}
// BestName is the closest viable name to what the user
@@ -2197,8 +2272,16 @@ bool Sema::CorrectTypo(LookupResult &Res, Scope *S, const CXXScopeSpec *SS,
// success if we found something that was not ambiguous.
Res.clear();
Res.setLookupName(BestName);
- if (MemberContext)
+
+ // If we found an ivar or property, add that result; no further
+ // lookup is required.
+ if (FoundBest)
+ Res.addDecl(FoundBest);
+ // If we're looking into the context of a member, perform qualified
+ // name lookup on the best name.
+ else if (MemberContext)
LookupQualifiedName(Res, MemberContext);
+ // Perform lookup as if we had just parsed the best name.
else
LookupParsedName(Res, S, SS, /*AllowBuiltinCreation=*/false,
EnteringContext);