aboutsummaryrefslogtreecommitdiff
path: root/lib/Sema/SemaLookup.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Sema/SemaLookup.cpp')
-rw-r--r--lib/Sema/SemaLookup.cpp501
1 files changed, 360 insertions, 141 deletions
diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp
index a29c4d452814..337a4a3ce3f7 100644
--- a/lib/Sema/SemaLookup.cpp
+++ b/lib/Sema/SemaLookup.cpp
@@ -193,62 +193,6 @@ namespace {
};
}
-static bool IsAcceptableIDNS(NamedDecl *D, unsigned IDNS) {
- return D->isInIdentifierNamespace(IDNS);
-}
-
-static bool IsAcceptableOperatorName(NamedDecl *D, unsigned IDNS) {
- return D->isInIdentifierNamespace(IDNS) &&
- !D->getDeclContext()->isRecord();
-}
-
-static bool IsAcceptableNestedNameSpecifierName(NamedDecl *D, unsigned IDNS) {
- // This lookup ignores everything that isn't a type.
-
- // This is a fast check for the far most common case.
- if (D->isInIdentifierNamespace(Decl::IDNS_Tag))
- return true;
-
- if (isa<UsingShadowDecl>(D))
- D = cast<UsingShadowDecl>(D)->getTargetDecl();
-
- return isa<TypeDecl>(D);
-}
-
-static bool IsAcceptableNamespaceName(NamedDecl *D, unsigned IDNS) {
- // We don't need to look through using decls here because
- // using decls aren't allowed to name namespaces.
-
- return isa<NamespaceDecl>(D) || isa<NamespaceAliasDecl>(D);
-}
-
-/// Gets the default result filter for the given lookup.
-static inline
-LookupResult::ResultFilter getResultFilter(Sema::LookupNameKind NameKind) {
- switch (NameKind) {
- case Sema::LookupOrdinaryName:
- case Sema::LookupTagName:
- case Sema::LookupMemberName:
- case Sema::LookupRedeclarationWithLinkage: // FIXME: check linkage, scoping
- case Sema::LookupUsingDeclName:
- case Sema::LookupObjCProtocolName:
- case Sema::LookupObjCImplementationName:
- return &IsAcceptableIDNS;
-
- case Sema::LookupOperatorName:
- return &IsAcceptableOperatorName;
-
- case Sema::LookupNestedNameSpecifierName:
- return &IsAcceptableNestedNameSpecifierName;
-
- case Sema::LookupNamespaceName:
- return &IsAcceptableNamespaceName;
- }
-
- llvm_unreachable("unkknown lookup kind");
- return 0;
-}
-
// Retrieve the set of identifier namespaces that correspond to a
// specific kind of name lookup.
static inline unsigned getIDNS(Sema::LookupNameKind NameKind,
@@ -257,19 +201,35 @@ static inline unsigned getIDNS(Sema::LookupNameKind NameKind,
unsigned IDNS = 0;
switch (NameKind) {
case Sema::LookupOrdinaryName:
- case Sema::LookupOperatorName:
case Sema::LookupRedeclarationWithLinkage:
IDNS = Decl::IDNS_Ordinary;
if (CPlusPlus) {
- IDNS |= Decl::IDNS_Tag | Decl::IDNS_Member;
+ IDNS |= Decl::IDNS_Tag | Decl::IDNS_Member | Decl::IDNS_Namespace;
if (Redeclaration) IDNS |= Decl::IDNS_TagFriend | Decl::IDNS_OrdinaryFriend;
}
break;
+ case Sema::LookupOperatorName:
+ // Operator lookup is its own crazy thing; it is not the same
+ // as (e.g.) looking up an operator name for redeclaration.
+ assert(!Redeclaration && "cannot do redeclaration operator lookup");
+ IDNS = Decl::IDNS_NonMemberOperator;
+ break;
+
case Sema::LookupTagName:
- IDNS = Decl::IDNS_Tag;
- if (CPlusPlus && Redeclaration)
- IDNS |= Decl::IDNS_TagFriend;
+ if (CPlusPlus) {
+ IDNS = Decl::IDNS_Type;
+
+ // When looking for a redeclaration of a tag name, we add:
+ // 1) TagFriend to find undeclared friend decls
+ // 2) Namespace because they can't "overload" with tag decls.
+ // 3) Tag because it includes class templates, which can't
+ // "overload" with tag decls.
+ if (Redeclaration)
+ IDNS |= Decl::IDNS_Tag | Decl::IDNS_TagFriend | Decl::IDNS_Namespace;
+ } else {
+ IDNS = Decl::IDNS_Tag;
+ }
break;
case Sema::LookupMemberName:
@@ -279,8 +239,11 @@ static inline unsigned getIDNS(Sema::LookupNameKind NameKind,
break;
case Sema::LookupNestedNameSpecifierName:
+ IDNS = Decl::IDNS_Type | Decl::IDNS_Namespace;
+ break;
+
case Sema::LookupNamespaceName:
- IDNS = Decl::IDNS_Ordinary | Decl::IDNS_Tag | Decl::IDNS_Member;
+ IDNS = Decl::IDNS_Namespace;
break;
case Sema::LookupUsingDeclName:
@@ -291,10 +254,6 @@ static inline unsigned getIDNS(Sema::LookupNameKind NameKind,
case Sema::LookupObjCProtocolName:
IDNS = Decl::IDNS_ObjCProtocol;
break;
-
- case Sema::LookupObjCImplementationName:
- IDNS = Decl::IDNS_ObjCImplementation;
- break;
}
return IDNS;
}
@@ -303,7 +262,6 @@ void LookupResult::configure() {
IDNS = getIDNS(LookupKind,
SemaRef.getLangOptions().CPlusPlus,
isForRedeclaration());
- IsAcceptableFn = getResultFilter(LookupKind);
// If we're looking for one of the allocation or deallocation
// operators, make sure that the implicitly-declared new and delete
@@ -341,9 +299,10 @@ void LookupResult::resolveKind() {
// If there's a single decl, we need to examine it to decide what
// kind of lookup this is.
if (N == 1) {
- if (isa<FunctionTemplateDecl>(*Decls.begin()))
+ NamedDecl *D = (*Decls.begin())->getUnderlyingDecl();
+ if (isa<FunctionTemplateDecl>(D))
ResultKind = FoundOverloaded;
- else if (isa<UnresolvedUsingValueDecl>(*Decls.begin()))
+ else if (isa<UnresolvedUsingValueDecl>(D))
ResultKind = FoundUnresolvedValue;
return;
}
@@ -1169,7 +1128,6 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
case LookupOperatorName:
case LookupNamespaceName:
case LookupObjCProtocolName:
- case LookupObjCImplementationName:
// These lookups will never find a member in a C++ class (or base class).
return false;
@@ -1291,7 +1249,7 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
/// context of the scope-specifier SS (if present).
///
/// @returns True if any decls were found (but possibly ambiguous)
-bool Sema::LookupParsedName(LookupResult &R, Scope *S, const CXXScopeSpec *SS,
+bool Sema::LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS,
bool AllowBuiltinCreation, bool EnteringContext) {
if (SS && SS->isInvalid()) {
// When the scope specifier is invalid, don't even look for
@@ -1303,7 +1261,7 @@ bool Sema::LookupParsedName(LookupResult &R, Scope *S, const CXXScopeSpec *SS,
if (DeclContext *DC = computeDeclContext(*SS, EnteringContext)) {
// We have resolved the scope specifier to a particular declaration
// contex, and will perform name lookup in that context.
- if (!DC->isDependentContext() && RequireCompleteDeclContext(*SS))
+ if (!DC->isDependentContext() && RequireCompleteDeclContext(*SS, DC))
return false;
R.setContextRange(SS->getRange());
@@ -1428,10 +1386,18 @@ addAssociatedClassesAndNamespaces(QualType T,
Sema::AssociatedNamespaceSet &AssociatedNamespaces,
Sema::AssociatedClassSet &AssociatedClasses);
-static void CollectNamespace(Sema::AssociatedNamespaceSet &Namespaces,
- DeclContext *Ctx) {
+static void CollectEnclosingNamespace(Sema::AssociatedNamespaceSet &Namespaces,
+ DeclContext *Ctx) {
+ // Add the associated namespace for this class.
+
+ // We don't use DeclContext::getEnclosingNamespaceContext() as this may
+ // be a locally scoped record.
+
+ while (Ctx->isRecord() || Ctx->isTransparentContext())
+ Ctx = Ctx->getParent();
+
if (Ctx->isFileContext())
- Namespaces.insert(Ctx);
+ Namespaces.insert(Ctx->getPrimaryContext());
}
// \brief Add the associated classes and namespaces for argument-dependent
@@ -1467,9 +1433,7 @@ addAssociatedClassesAndNamespaces(const TemplateArgument &Arg,
if (CXXRecordDecl *EnclosingClass = dyn_cast<CXXRecordDecl>(Ctx))
AssociatedClasses.insert(EnclosingClass);
// Add the associated namespace for this class.
- while (Ctx->isRecord())
- Ctx = Ctx->getParent();
- CollectNamespace(AssociatedNamespaces, Ctx);
+ CollectEnclosingNamespace(AssociatedNamespaces, Ctx);
}
break;
}
@@ -1513,9 +1477,7 @@ addAssociatedClassesAndNamespaces(CXXRecordDecl *Class,
if (CXXRecordDecl *EnclosingClass = dyn_cast<CXXRecordDecl>(Ctx))
AssociatedClasses.insert(EnclosingClass);
// Add the associated namespace for this class.
- while (Ctx->isRecord())
- Ctx = Ctx->getParent();
- CollectNamespace(AssociatedNamespaces, Ctx);
+ CollectEnclosingNamespace(AssociatedNamespaces, Ctx);
// Add the class itself. If we've already seen this class, we don't
// need to visit base classes.
@@ -1537,9 +1499,7 @@ addAssociatedClassesAndNamespaces(CXXRecordDecl *Class,
if (CXXRecordDecl *EnclosingClass = dyn_cast<CXXRecordDecl>(Ctx))
AssociatedClasses.insert(EnclosingClass);
// Add the associated namespace for this class.
- while (Ctx->isRecord())
- Ctx = Ctx->getParent();
- CollectNamespace(AssociatedNamespaces, Ctx);
+ CollectEnclosingNamespace(AssociatedNamespaces, Ctx);
const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs();
for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
@@ -1580,9 +1540,7 @@ addAssociatedClassesAndNamespaces(CXXRecordDecl *Class,
if (AssociatedClasses.insert(BaseDecl)) {
// Find the associated namespace for this base class.
DeclContext *BaseCtx = BaseDecl->getDeclContext();
- while (BaseCtx->isRecord())
- BaseCtx = BaseCtx->getParent();
- CollectNamespace(AssociatedNamespaces, BaseCtx);
+ CollectEnclosingNamespace(AssociatedNamespaces, BaseCtx);
// Make sure we visit the bases of this base class.
if (BaseDecl->bases_begin() != BaseDecl->bases_end())
@@ -1657,9 +1615,7 @@ addAssociatedClassesAndNamespaces(QualType T,
AssociatedClasses.insert(EnclosingClass);
// Add the associated namespace for this class.
- while (Ctx->isRecord())
- Ctx = Ctx->getParent();
- CollectNamespace(AssociatedNamespaces, Ctx);
+ CollectEnclosingNamespace(AssociatedNamespaces, Ctx);
return;
}
@@ -1825,16 +1781,19 @@ IsAcceptableNonMemberOperatorCandidate(FunctionDecl *Fn,
}
NamedDecl *Sema::LookupSingleName(Scope *S, DeclarationName Name,
+ SourceLocation Loc,
LookupNameKind NameKind,
RedeclarationKind Redecl) {
- LookupResult R(*this, Name, SourceLocation(), NameKind, Redecl);
+ LookupResult R(*this, Name, Loc, NameKind, Redecl);
LookupName(R, S);
return R.getAsSingle<NamedDecl>();
}
/// \brief Find the protocol with the given name, if any.
-ObjCProtocolDecl *Sema::LookupProtocol(IdentifierInfo *II) {
- Decl *D = LookupSingleName(TUScope, II, LookupObjCProtocolName);
+ObjCProtocolDecl *Sema::LookupProtocol(IdentifierInfo *II,
+ SourceLocation IdLoc) {
+ Decl *D = LookupSingleName(TUScope, II, IdLoc,
+ LookupObjCProtocolName);
return cast_or_null<ObjCProtocolDecl>(D);
}
@@ -1864,16 +1823,17 @@ void Sema::LookupOverloadedOperatorName(OverloadedOperatorKind Op, Scope *S,
for (LookupResult::iterator Op = Operators.begin(), OpEnd = Operators.end();
Op != OpEnd; ++Op) {
- if (FunctionDecl *FD = dyn_cast<FunctionDecl>(*Op)) {
+ NamedDecl *Found = (*Op)->getUnderlyingDecl();
+ if (FunctionDecl *FD = dyn_cast<FunctionDecl>(Found)) {
if (IsAcceptableNonMemberOperatorCandidate(FD, T1, T2, Context))
- Functions.addDecl(FD, Op.getAccess()); // FIXME: canonical FD
+ Functions.addDecl(*Op, Op.getAccess()); // FIXME: canonical FD
} else if (FunctionTemplateDecl *FunTmpl
- = dyn_cast<FunctionTemplateDecl>(*Op)) {
+ = dyn_cast<FunctionTemplateDecl>(Found)) {
// FIXME: friend operators?
// FIXME: do we need to check IsAcceptableNonMemberOperatorCandidate,
// later?
if (!FunTmpl->getDeclContext()->isRecord())
- Functions.addDecl(FunTmpl, Op.getAccess());
+ Functions.addDecl(*Op, Op.getAccess());
}
}
}
@@ -2137,7 +2097,7 @@ NamedDecl *VisibleDeclsRecord::checkHidden(NamedDecl *ND) {
IEnd = Pos->second.end();
I != IEnd; ++I) {
// A tag declaration does not hide a non-tag declaration.
- if ((*I)->getIdentifierNamespace() == Decl::IDNS_Tag &&
+ if ((*I)->hasTagIdentifierNamespace() &&
(IDNS & (Decl::IDNS_Member | Decl::IDNS_Ordinary |
Decl::IDNS_ObjCProtocol)))
continue;
@@ -2276,6 +2236,14 @@ static void LookupVisibleDecls(DeclContext *Ctx, LookupResult &Result,
LookupVisibleDecls(IFace->getSuperClass(), Result, QualifiedNameLookup,
true, Consumer, Visited);
}
+
+ // If there is an implementation, traverse it. We do this to find
+ // synthesized ivars.
+ if (IFace->getImplementation()) {
+ ShadowContextRAII Shadow(Visited);
+ LookupVisibleDecls(IFace->getImplementation(), Result,
+ QualifiedNameLookup, true, 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) {
@@ -2290,6 +2258,13 @@ static void LookupVisibleDecls(DeclContext *Ctx, LookupResult &Result,
LookupVisibleDecls(*I, Result, QualifiedNameLookup, false, Consumer,
Visited);
}
+
+ // If there is an implementation, traverse it.
+ if (Category->getImplementation()) {
+ ShadowContextRAII Shadow(Visited);
+ LookupVisibleDecls(Category->getImplementation(), Result,
+ QualifiedNameLookup, true, Consumer, Visited);
+ }
}
}
@@ -2421,6 +2396,9 @@ class TypoCorrectionConsumer : public VisibleDeclConsumer {
/// found (so far) with the typo name.
llvm::SmallVector<NamedDecl *, 4> BestResults;
+ /// \brief The keywords that have the smallest edit distance.
+ llvm::SmallVector<IdentifierInfo *, 4> BestKeywords;
+
/// \brief The best edit distance found so far.
unsigned BestEditDistance;
@@ -2429,13 +2407,23 @@ public:
: Typo(Typo->getName()) { }
virtual void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, bool InBaseClass);
+ void addKeywordResult(ASTContext &Context, llvm::StringRef Keyword);
typedef llvm::SmallVector<NamedDecl *, 4>::const_iterator iterator;
iterator begin() const { return BestResults.begin(); }
iterator end() const { return BestResults.end(); }
- bool empty() const { return BestResults.empty(); }
-
- unsigned getBestEditDistance() const { return BestEditDistance; }
+ void clear_decls() { BestResults.clear(); }
+
+ bool empty() const { return BestResults.empty() && BestKeywords.empty(); }
+
+ typedef llvm::SmallVector<IdentifierInfo *, 4>::const_iterator
+ keyword_iterator;
+ keyword_iterator keyword_begin() const { return BestKeywords.begin(); }
+ keyword_iterator keyword_end() const { return BestKeywords.end(); }
+ bool keyword_empty() const { return BestKeywords.empty(); }
+ unsigned keyword_size() const { return BestKeywords.size(); }
+
+ unsigned getBestEditDistance() const { return BestEditDistance; }
};
}
@@ -2457,11 +2445,12 @@ void TypoCorrectionConsumer::FoundDecl(NamedDecl *ND, NamedDecl *Hiding,
// entity. If this edit distance is not worse than the best edit
// distance we've seen so far, add it to the list of results.
unsigned ED = Typo.edit_distance(Name->getName());
- if (!BestResults.empty()) {
+ if (!BestResults.empty() || !BestKeywords.empty()) {
if (ED < BestEditDistance) {
// This result is better than any we've seen before; clear out
// the previous results.
BestResults.clear();
+ BestKeywords.clear();
BestEditDistance = ED;
} else if (ED > BestEditDistance) {
// This result is worse than the best results we've seen so far;
@@ -2474,6 +2463,28 @@ void TypoCorrectionConsumer::FoundDecl(NamedDecl *ND, NamedDecl *Hiding,
BestResults.push_back(ND);
}
+void TypoCorrectionConsumer::addKeywordResult(ASTContext &Context,
+ llvm::StringRef Keyword) {
+ // Compute the edit distance between the typo and this keyword.
+ // If this edit distance is not worse than the best edit
+ // distance we've seen so far, add it to the list of results.
+ unsigned ED = Typo.edit_distance(Keyword);
+ if (!BestResults.empty() || !BestKeywords.empty()) {
+ if (ED < BestEditDistance) {
+ BestResults.clear();
+ BestKeywords.clear();
+ BestEditDistance = ED;
+ } else if (ED > BestEditDistance) {
+ // This result is worse than the best results we've seen so far;
+ // ignore it.
+ return;
+ }
+ } else
+ BestEditDistance = ED;
+
+ BestKeywords.push_back(&Context.Idents.get(Keyword));
+}
+
/// \brief Try to "correct" a typo in the source code by finding
/// visible declarations whose names are similar to the name that was
/// present in the source code.
@@ -2494,42 +2505,50 @@ void TypoCorrectionConsumer::FoundDecl(NamedDecl *ND, NamedDecl *Hiding,
/// \param EnteringContext whether we're entering the context described by
/// the nested-name-specifier SS.
///
+/// \param CTC The context in which typo correction occurs, which impacts the
+/// set of keywords permitted.
+///
/// \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,
- const ObjCObjectPointerType *OPT) {
+/// \returns the corrected name if the typo was corrected, otherwise returns an
+/// empty \c DeclarationName. When a typo was corrected, the result structure
+/// may contain the results of name lookup for the correct name or it may be
+/// empty.
+DeclarationName Sema::CorrectTypo(LookupResult &Res, Scope *S, CXXScopeSpec *SS,
+ DeclContext *MemberContext,
+ bool EnteringContext,
+ CorrectTypoContext CTC,
+ const ObjCObjectPointerType *OPT) {
if (Diags.hasFatalErrorOccurred())
- return false;
+ return DeclarationName();
// Provide a stop gap for files that are just seriously broken. Trying
// to correct all typos can turn into a HUGE performance penalty, causing
// some files to take minutes to get rejected by the parser.
// FIXME: Is this the right solution?
if (TyposCorrected == 20)
- return false;
+ return DeclarationName();
++TyposCorrected;
// We only attempt to correct typos for identifiers.
IdentifierInfo *Typo = Res.getLookupName().getAsIdentifierInfo();
if (!Typo)
- return false;
+ return DeclarationName();
// If the scope specifier itself was invalid, don't try to correct
// typos.
if (SS && SS->isInvalid())
- return false;
+ return DeclarationName();
// Never try to correct typos during template deduction or
// instantiation.
if (!ActiveTemplateInstantiations.empty())
- return false;
-
+ return DeclarationName();
+
TypoCorrectionConsumer Consumer(Typo);
+
+ // Perform name lookup to find visible, similarly-named entities.
if (MemberContext) {
LookupVisibleDecls(MemberContext, Res.getLookupKind(), Consumer);
@@ -2543,45 +2562,239 @@ bool Sema::CorrectTypo(LookupResult &Res, Scope *S, const CXXScopeSpec *SS,
} else if (SS && SS->isSet()) {
DeclContext *DC = computeDeclContext(*SS, EnteringContext);
if (!DC)
- return false;
+ return DeclarationName();
LookupVisibleDecls(DC, Res.getLookupKind(), Consumer);
} else {
LookupVisibleDecls(S, Res.getLookupKind(), Consumer);
}
+ // Add context-dependent keywords.
+ bool WantTypeSpecifiers = false;
+ bool WantExpressionKeywords = false;
+ bool WantCXXNamedCasts = false;
+ bool WantRemainingKeywords = false;
+ switch (CTC) {
+ case CTC_Unknown:
+ WantTypeSpecifiers = true;
+ WantExpressionKeywords = true;
+ WantCXXNamedCasts = true;
+ WantRemainingKeywords = true;
+ break;
+
+ case CTC_NoKeywords:
+ break;
+
+ case CTC_Type:
+ WantTypeSpecifiers = true;
+ break;
+
+ case CTC_ObjCMessageReceiver:
+ Consumer.addKeywordResult(Context, "super");
+ // Fall through to handle message receivers like expressions.
+
+ case CTC_Expression:
+ if (getLangOptions().CPlusPlus)
+ WantTypeSpecifiers = true;
+ WantExpressionKeywords = true;
+ // Fall through to get C++ named casts.
+
+ case CTC_CXXCasts:
+ WantCXXNamedCasts = true;
+ break;
+
+ case CTC_MemberLookup:
+ if (getLangOptions().CPlusPlus)
+ Consumer.addKeywordResult(Context, "template");
+ break;
+ }
+
+ if (WantTypeSpecifiers) {
+ // Add type-specifier keywords to the set of results.
+ const char *CTypeSpecs[] = {
+ "char", "const", "double", "enum", "float", "int", "long", "short",
+ "signed", "struct", "union", "unsigned", "void", "volatile", "_Bool",
+ "_Complex", "_Imaginary",
+ // storage-specifiers as well
+ "extern", "inline", "static", "typedef"
+ };
+
+ const unsigned NumCTypeSpecs = sizeof(CTypeSpecs) / sizeof(CTypeSpecs[0]);
+ for (unsigned I = 0; I != NumCTypeSpecs; ++I)
+ Consumer.addKeywordResult(Context, CTypeSpecs[I]);
+
+ if (getLangOptions().C99)
+ Consumer.addKeywordResult(Context, "restrict");
+ if (getLangOptions().Bool || getLangOptions().CPlusPlus)
+ Consumer.addKeywordResult(Context, "bool");
+
+ if (getLangOptions().CPlusPlus) {
+ Consumer.addKeywordResult(Context, "class");
+ Consumer.addKeywordResult(Context, "typename");
+ Consumer.addKeywordResult(Context, "wchar_t");
+
+ if (getLangOptions().CPlusPlus0x) {
+ Consumer.addKeywordResult(Context, "char16_t");
+ Consumer.addKeywordResult(Context, "char32_t");
+ Consumer.addKeywordResult(Context, "constexpr");
+ Consumer.addKeywordResult(Context, "decltype");
+ Consumer.addKeywordResult(Context, "thread_local");
+ }
+ }
+
+ if (getLangOptions().GNUMode)
+ Consumer.addKeywordResult(Context, "typeof");
+ }
+
+ if (WantCXXNamedCasts) {
+ Consumer.addKeywordResult(Context, "const_cast");
+ Consumer.addKeywordResult(Context, "dynamic_cast");
+ Consumer.addKeywordResult(Context, "reinterpret_cast");
+ Consumer.addKeywordResult(Context, "static_cast");
+ }
+
+ if (WantExpressionKeywords) {
+ Consumer.addKeywordResult(Context, "sizeof");
+ if (getLangOptions().Bool || getLangOptions().CPlusPlus) {
+ Consumer.addKeywordResult(Context, "false");
+ Consumer.addKeywordResult(Context, "true");
+ }
+
+ if (getLangOptions().CPlusPlus) {
+ const char *CXXExprs[] = {
+ "delete", "new", "operator", "throw", "typeid"
+ };
+ const unsigned NumCXXExprs = sizeof(CXXExprs) / sizeof(CXXExprs[0]);
+ for (unsigned I = 0; I != NumCXXExprs; ++I)
+ Consumer.addKeywordResult(Context, CXXExprs[I]);
+
+ if (isa<CXXMethodDecl>(CurContext) &&
+ cast<CXXMethodDecl>(CurContext)->isInstance())
+ Consumer.addKeywordResult(Context, "this");
+
+ if (getLangOptions().CPlusPlus0x) {
+ Consumer.addKeywordResult(Context, "alignof");
+ Consumer.addKeywordResult(Context, "nullptr");
+ }
+ }
+ }
+
+ if (WantRemainingKeywords) {
+ if (getCurFunctionOrMethodDecl() || getCurBlock()) {
+ // Statements.
+ const char *CStmts[] = {
+ "do", "else", "for", "goto", "if", "return", "switch", "while" };
+ const unsigned NumCStmts = sizeof(CStmts) / sizeof(CStmts[0]);
+ for (unsigned I = 0; I != NumCStmts; ++I)
+ Consumer.addKeywordResult(Context, CStmts[I]);
+
+ if (getLangOptions().CPlusPlus) {
+ Consumer.addKeywordResult(Context, "catch");
+ Consumer.addKeywordResult(Context, "try");
+ }
+
+ if (S && S->getBreakParent())
+ Consumer.addKeywordResult(Context, "break");
+
+ if (S && S->getContinueParent())
+ Consumer.addKeywordResult(Context, "continue");
+
+ if (!getSwitchStack().empty()) {
+ Consumer.addKeywordResult(Context, "case");
+ Consumer.addKeywordResult(Context, "default");
+ }
+ } else {
+ if (getLangOptions().CPlusPlus) {
+ Consumer.addKeywordResult(Context, "namespace");
+ Consumer.addKeywordResult(Context, "template");
+ }
+
+ if (S && S->isClassScope()) {
+ Consumer.addKeywordResult(Context, "explicit");
+ Consumer.addKeywordResult(Context, "friend");
+ Consumer.addKeywordResult(Context, "mutable");
+ Consumer.addKeywordResult(Context, "private");
+ Consumer.addKeywordResult(Context, "protected");
+ Consumer.addKeywordResult(Context, "public");
+ Consumer.addKeywordResult(Context, "virtual");
+ }
+ }
+
+ if (getLangOptions().CPlusPlus) {
+ Consumer.addKeywordResult(Context, "using");
+
+ if (getLangOptions().CPlusPlus0x)
+ Consumer.addKeywordResult(Context, "static_assert");
+ }
+ }
+
+ // If we haven't found anything, we're done.
if (Consumer.empty())
- return false;
+ return DeclarationName();
// Only allow a single, closest name in the result set (it's okay to
// 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;
+ DeclarationName BestName;
+ NamedDecl *BestIvarOrPropertyDecl = 0;
+ bool FoundIvarOrPropertyDecl = false;
+
+ // Check all of the declaration results to find the best name so far.
+ for (TypoCorrectionConsumer::iterator I = Consumer.begin(),
+ IEnd = Consumer.end();
+ I != IEnd; ++I) {
+ if (!BestName)
+ BestName = (*I)->getDeclName();
+ else if (BestName != (*I)->getDeclName())
+ return DeclarationName();
+
+ // \brief Keep track of either an Objective-C ivar or a property, but not
+ // both.
+ if (isa<ObjCIvarDecl>(*I) || isa<ObjCPropertyDecl>(*I)) {
+ if (FoundIvarOrPropertyDecl)
+ BestIvarOrPropertyDecl = 0;
+ else {
+ BestIvarOrPropertyDecl = *I;
+ FoundIvarOrPropertyDecl = true;
+ }
+ }
}
+ // Now check all of the keyword results to find the best name.
+ switch (Consumer.keyword_size()) {
+ case 0:
+ // No keywords matched.
+ break;
+
+ case 1:
+ // If we already have a name
+ if (!BestName) {
+ // We did not have anything previously,
+ BestName = *Consumer.keyword_begin();
+ } else if (BestName.getAsIdentifierInfo() == *Consumer.keyword_begin()) {
+ // We have a declaration with the same name as a context-sensitive
+ // keyword. The keyword takes precedence.
+ BestIvarOrPropertyDecl = 0;
+ FoundIvarOrPropertyDecl = false;
+ Consumer.clear_decls();
+ } else {
+ // Name collision; we will not correct typos.
+ return DeclarationName();
+ }
+ break;
+
+ default:
+ // Name collision; we will not correct typos.
+ return DeclarationName();
+ }
+
// BestName is the closest viable name to what the user
// typed. However, to make sure that we don't pick something that's
// way off, make sure that the user typed at least 3 characters for
// each correction.
unsigned ED = Consumer.getBestEditDistance();
- if (ED == 0 || (BestName.getAsIdentifierInfo()->getName().size() / ED) < 3)
- return false;
+ if (ED == 0 || !BestName.getAsIdentifierInfo() ||
+ (BestName.getAsIdentifierInfo()->getName().size() / ED) < 3)
+ return DeclarationName();
// Perform name lookup again with the name we chose, and declare
// success if we found something that was not ambiguous.
@@ -2590,11 +2803,14 @@ bool Sema::CorrectTypo(LookupResult &Res, Scope *S, const CXXScopeSpec *SS,
// If we found an ivar or property, add that result; no further
// lookup is required.
- if (FoundBest)
- Res.addDecl(FoundBest);
+ if (BestIvarOrPropertyDecl)
+ Res.addDecl(BestIvarOrPropertyDecl);
// If we're looking into the context of a member, perform qualified
// name lookup on the best name.
- else if (MemberContext)
+ else if (!Consumer.keyword_empty()) {
+ // The best match was a keyword. Return it.
+ return BestName;
+ } else if (MemberContext)
LookupQualifiedName(Res, MemberContext);
// Perform lookup as if we had just parsed the best name.
else
@@ -2603,8 +2819,11 @@ bool Sema::CorrectTypo(LookupResult &Res, Scope *S, const CXXScopeSpec *SS,
if (Res.isAmbiguous()) {
Res.suppressDiagnostics();
- return false;
+ return DeclarationName();
}
- return Res.getResultKind() != LookupResult::NotFound;
+ if (Res.getResultKind() != LookupResult::NotFound)
+ return BestName;
+
+ return DeclarationName();
}