void LookupVisibleDecls(DeclContext *Ctx, LookupNameKind Kind,
VisibleDeclConsumer &Consumer);
+ /// \brief The context in which typo-correction occurs.
+ ///
+ /// The typo-correction context affects which keywords (if any) are
+ /// considered when trying to correct for typos.
+ enum CorrectTypoContext {
+ /// \brief An unknown context, where any keyword might be valid.
+ CTC_Unknown,
+ /// \brief A context where no keywords are used (e.g. we expect an actual
+ /// name).
+ CTC_NoKeywords,
+ /// \brief A context where we're correcting a type name.
+ CTC_Type,
+ /// \brief An expression context.
+ CTC_Expression,
+ /// \brief A type cast, or anything else that can be followed by a '<'.
+ CTC_CXXCasts,
+ /// \brief A member lookup context.
+ CTC_MemberLookup,
+ /// \brief The receiver of an Objective-C message send within an
+ /// Objective-C method where 'super' is a valid keyword.
+ CTC_ObjCMessageReceiver
+ };
+
DeclarationName CorrectTypo(LookupResult &R, Scope *S, CXXScopeSpec *SS,
DeclContext *MemberContext = 0,
bool EnteringContext = false,
+ CorrectTypoContext CTC = CTC_Unknown,
const ObjCObjectPointerType *OPT = 0);
void FindAssociatedClassesAndNamespaces(Expr **Args, unsigned NumArgs,
// We haven't found anything, and we're not recovering from a
// different kind of error, so look for typos.
DeclarationName Name = Found.getLookupName();
- if (CorrectTypo(Found, S, &SS, LookupCtx, EnteringContext) &&
+ if (CorrectTypo(Found, S, &SS, LookupCtx, EnteringContext,
+ CTC_NoKeywords) &&
Found.isSingleResult() &&
isAcceptableNestedNameSpecifier(Found.getAsSingle<NamedDecl>())) {
if (LookupCtx)
LookupResult Lookup(*this, &II, IILoc, LookupOrdinaryName,
NotForRedeclaration);
- // FIXME: It would be nice if we could correct for typos in built-in
- // names, such as "itn" for "int".
-
- if (CorrectTypo(Lookup, S, SS) && Lookup.isSingleResult()) {
- NamedDecl *Result = Lookup.getAsSingle<NamedDecl>();
- if ((isa<TypeDecl>(Result) || isa<ObjCInterfaceDecl>(Result)) &&
- !Result->isInvalidDecl()) {
- // We found a similarly-named type or interface; suggest that.
- if (!SS || !SS->isSet())
- Diag(IILoc, diag::err_unknown_typename_suggest)
- << &II << Lookup.getLookupName()
- << FixItHint::CreateReplacement(SourceRange(IILoc),
- Result->getNameAsString());
- else if (DeclContext *DC = computeDeclContext(*SS, false))
- Diag(IILoc, diag::err_unknown_nested_typename_suggest)
- << &II << DC << Lookup.getLookupName() << SS->getRange()
- << FixItHint::CreateReplacement(SourceRange(IILoc),
- Result->getNameAsString());
- else
- llvm_unreachable("could not have corrected a typo here");
+ if (DeclarationName Corrected = CorrectTypo(Lookup, S, SS, 0, 0, CTC_Type)) {
+ if (NamedDecl *Result = Lookup.getAsSingle<NamedDecl>()) {
+ if ((isa<TypeDecl>(Result) || isa<ObjCInterfaceDecl>(Result)) &&
+ !Result->isInvalidDecl()) {
+ // We found a similarly-named type or interface; suggest that.
+ if (!SS || !SS->isSet())
+ Diag(IILoc, diag::err_unknown_typename_suggest)
+ << &II << Lookup.getLookupName()
+ << FixItHint::CreateReplacement(SourceRange(IILoc),
+ Result->getNameAsString());
+ else if (DeclContext *DC = computeDeclContext(*SS, false))
+ Diag(IILoc, diag::err_unknown_nested_typename_suggest)
+ << &II << DC << Lookup.getLookupName() << SS->getRange()
+ << FixItHint::CreateReplacement(SourceRange(IILoc),
+ Result->getNameAsString());
+ else
+ llvm_unreachable("could not have corrected a typo here");
- Diag(Result->getLocation(), diag::note_previous_decl)
- << Result->getDeclName();
-
- SuggestedType = getTypeName(*Result->getIdentifier(), IILoc, S, SS);
- return true;
+ Diag(Result->getLocation(), diag::note_previous_decl)
+ << Result->getDeclName();
+
+ SuggestedType = getTypeName(*Result->getIdentifier(), IILoc, S, SS);
+ return true;
+ }
+ } else if (Lookup.empty()) {
+ // We corrected to a keyword.
+ // FIXME: Actually recover with the keyword we suggest, and emit a fix-it.
+ Diag(IILoc, diag::err_unknown_typename_suggest)
+ << &II << Corrected;
+ return true;
}
}
// Perform typo correction at the given location, but only if we
// find an Objective-C class name.
LookupResult R(*this, Id, RecoverLoc, LookupOrdinaryName);
- if (CorrectTypo(R, TUScope, 0) &&
+ if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) &&
(IDecl = R.getAsSingle<ObjCInterfaceDecl>())) {
Diag(RecoverLoc, diag::err_undef_interface_suggest)
<< Id << IDecl->getDeclName()
// If no results were found, try to correct typos.
if (R.empty() && BaseType.isNull() &&
- CorrectTypo(R, S, &SS, ClassDecl) && R.isSingleResult()) {
+ CorrectTypo(R, S, &SS, ClassDecl, 0, CTC_NoKeywords) &&
+ R.isSingleResult()) {
if (FieldDecl *Member = R.getAsSingle<FieldDecl>()) {
if (Member->getDeclContext()->getLookupContext()->Equals(ClassDecl)) {
// We have found a non-static data member with a similar
if (!PrevDecl) {
// Try to correct for a typo in the superclass name.
LookupResult R(*this, SuperName, SuperLoc, LookupOrdinaryName);
- if (CorrectTypo(R, TUScope, 0) &&
+ if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) &&
(PrevDecl = R.getAsSingle<ObjCInterfaceDecl>())) {
Diag(SuperLoc, diag::err_undef_superclass_suggest)
<< SuperName << ClassName << PrevDecl->getDeclName();
if (!PDecl) {
LookupResult R(*this, ProtocolId[i].first, ProtocolId[i].second,
LookupObjCProtocolName);
- if (CorrectTypo(R, TUScope, 0) &&
+ if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) &&
(PDecl = R.getAsSingle<ObjCProtocolDecl>())) {
Diag(ProtocolId[i].second, diag::err_undeclared_protocol_suggest)
<< ProtocolId[i].first << R.getLookupName();
// We did not find anything with the name ClassName; try to correct for
// typos in the class name.
LookupResult R(*this, ClassName, ClassLoc, LookupOrdinaryName);
- if (CorrectTypo(R, TUScope, 0) &&
+ if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) &&
(IDecl = R.getAsSingle<ObjCInterfaceDecl>())) {
// Suggest the (potentially) correct interface name. However, put the
// fix-it hint itself in a separate note, since changing the name in
}
// We didn't find anything, so try to correct for a typo.
- if (S && CorrectTypo(R, S, &SS)) {
- if (isa<ValueDecl>(*R.begin()) || isa<FunctionTemplateDecl>(*R.begin())) {
- if (SS.isEmpty())
- Diag(R.getNameLoc(), diagnostic_suggest) << Name << R.getLookupName()
- << FixItHint::CreateReplacement(R.getNameLoc(),
- R.getLookupName().getAsString());
- else
- Diag(R.getNameLoc(), diag::err_no_member_suggest)
- << Name << computeDeclContext(SS, false) << R.getLookupName()
- << SS.getRange()
- << FixItHint::CreateReplacement(R.getNameLoc(),
- R.getLookupName().getAsString());
- if (NamedDecl *ND = R.getAsSingle<NamedDecl>())
- Diag(ND->getLocation(), diag::note_previous_decl)
- << ND->getDeclName();
-
- // Tell the callee to try to recover.
- return false;
- }
+ DeclarationName Corrected;
+ if (S && (Corrected = CorrectTypo(R, S, &SS))) {
+ if (!R.empty()) {
+ if (isa<ValueDecl>(*R.begin()) || isa<FunctionTemplateDecl>(*R.begin())) {
+ if (SS.isEmpty())
+ Diag(R.getNameLoc(), diagnostic_suggest) << Name << R.getLookupName()
+ << FixItHint::CreateReplacement(R.getNameLoc(),
+ R.getLookupName().getAsString());
+ else
+ Diag(R.getNameLoc(), diag::err_no_member_suggest)
+ << Name << computeDeclContext(SS, false) << R.getLookupName()
+ << SS.getRange()
+ << FixItHint::CreateReplacement(R.getNameLoc(),
+ R.getLookupName().getAsString());
+ if (NamedDecl *ND = R.getAsSingle<NamedDecl>())
+ Diag(ND->getLocation(), diag::note_previous_decl)
+ << ND->getDeclName();
+
+ // Tell the callee to try to recover.
+ return false;
+ }
+
+ if (isa<TypeDecl>(*R.begin()) || isa<ObjCInterfaceDecl>(*R.begin())) {
+ // FIXME: If we ended up with a typo for a type name or
+ // Objective-C class name, we're in trouble because the parser
+ // is in the wrong place to recover. Suggest the typo
+ // correction, but don't make it a fix-it since we're not going
+ // to recover well anyway.
+ if (SS.isEmpty())
+ Diag(R.getNameLoc(), diagnostic_suggest) << Name << R.getLookupName();
+ else
+ Diag(R.getNameLoc(), diag::err_no_member_suggest)
+ << Name << computeDeclContext(SS, false) << R.getLookupName()
+ << SS.getRange();
- if (isa<TypeDecl>(*R.begin()) || isa<ObjCInterfaceDecl>(*R.begin())) {
- // FIXME: If we ended up with a typo for a type name or
- // Objective-C class name, we're in trouble because the parser
- // is in the wrong place to recover. Suggest the typo
- // correction, but don't make it a fix-it since we're not going
- // to recover well anyway.
+ // Don't try to recover; it won't work.
+ return true;
+ }
+ } else {
+ // FIXME: We found a keyword. Suggest it, but don't provide a fix-it
+ // because we aren't able to recover.
if (SS.isEmpty())
- Diag(R.getNameLoc(), diagnostic_suggest) << Name << R.getLookupName();
+ Diag(R.getNameLoc(), diagnostic_suggest) << Name << Corrected;
else
Diag(R.getNameLoc(), diag::err_no_member_suggest)
- << Name << computeDeclContext(SS, false) << R.getLookupName()
- << SS.getRange();
-
- // Don't try to recover; it won't work.
+ << Name << computeDeclContext(SS, false) << Corrected
+ << SS.getRange();
return true;
}
-
R.clear();
}
// We didn't find anything with the given name, so try to correct
// for typos.
DeclarationName Name = R.getLookupName();
- if (SemaRef.CorrectTypo(R, 0, &SS, DC) &&
+ if (SemaRef.CorrectTypo(R, 0, &SS, DC, false, Sema::CTC_MemberLookup) &&
+ !R.empty() &&
(isa<ValueDecl>(*R.begin()) || isa<FunctionTemplateDecl>(*R.begin()))) {
SemaRef.Diag(R.getNameLoc(), diag::err_no_member_suggest)
<< Name << DC << R.getLookupName() << SS.getRange()
// Attempt to correct for typos in ivar names.
LookupResult Res(*this, R.getLookupName(), R.getNameLoc(),
LookupMemberName);
- if (CorrectTypo(Res, 0, 0, IDecl) &&
+ if (CorrectTypo(Res, 0, 0, IDecl, false, CTC_MemberLookup) &&
(IV = Res.getAsSingle<ObjCIvarDecl>())) {
Diag(R.getNameLoc(),
diag::err_typecheck_member_reference_ivar_suggest)
// Attempt to correct for typos in property names.
LookupResult Res(*this, MemberName, MemberLoc, LookupOrdinaryName);
- if (CorrectTypo(Res, 0, 0, IFace, false, OPT) &&
+ if (CorrectTypo(Res, 0, 0, IFace, false, CTC_NoKeywords, OPT) &&
Res.getAsSingle<ObjCPropertyDecl>()) {
DeclarationName TypoResult = Res.getLookupName();
Diag(MemberLoc, diag::err_property_not_found_suggest)
}
}
- if (CorrectTypo(Result, S, 0) && Result.isSingleResult()) {
- NamedDecl *ND = Result.getFoundDecl();
- if (isa<ObjCInterfaceDecl>(ND)) {
+ // Determine our typo-correction context.
+ CorrectTypoContext CTC = CTC_Expression;
+ if (ObjCMethodDecl *Method = getCurMethodDecl())
+ if (Method->getClassInterface() &&
+ Method->getClassInterface()->getSuperClass())
+ CTC = CTC_ObjCMessageReceiver;
+
+ if (DeclarationName Corrected = CorrectTypo(Result, S, 0, 0, false, CTC)) {
+ if (Result.isSingleResult()) {
+ // If we found a declaration, correct when it refers to an Objective-C
+ // class.
+ NamedDecl *ND = Result.getFoundDecl();
+ if (isa<ObjCInterfaceDecl>(ND)) {
+ Diag(NameLoc, diag::err_unknown_receiver_suggest)
+ << Name << Result.getLookupName()
+ << FixItHint::CreateReplacement(SourceRange(NameLoc),
+ ND->getNameAsString());
+ Diag(ND->getLocation(), diag::note_previous_decl)
+ << Corrected;
+
+ Name = ND->getIdentifier();
+ return ObjCClassMessage;
+ }
+ } else if (Result.empty() && Corrected.getAsIdentifierInfo() &&
+ Corrected.getAsIdentifierInfo()->isStr("super")) {
+ // If we've found the keyword "super", this is a send to super.
Diag(NameLoc, diag::err_unknown_receiver_suggest)
- << Name << Result.getLookupName()
- << FixItHint::CreateReplacement(SourceRange(NameLoc),
- ND->getNameAsString());
- Diag(ND->getLocation(), diag::note_previous_decl)
- << ND->getDeclName();
-
- Name = ND->getIdentifier();
- return ObjCClassMessage;
+ << Name << Corrected
+ << FixItHint::CreateReplacement(SourceRange(NameLoc), "super");
+ Name = Corrected.getAsIdentifierInfo();
+ return ObjCSuperMessage;
}
}
// was a typo for another field name.
LookupResult R(SemaRef, FieldName, D->getFieldLoc(),
Sema::LookupMemberName);
- if (SemaRef.CorrectTypo(R, /*Scope=*/0, /*SS=*/0, RT->getDecl()) &&
+ if (SemaRef.CorrectTypo(R, /*Scope=*/0, /*SS=*/0, RT->getDecl(), false,
+ Sema::CTC_NoKeywords) &&
(ReplacementField = R.getAsSingle<FieldDecl>()) &&
ReplacementField->getDeclContext()->getLookupContext()
->Equals(RT->getDecl())) {
/// 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;
: 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; }
};
}
// 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;
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.
/// \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.
///
/// 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,
- const ObjCObjectPointerType *OPT) {
+ DeclContext *MemberContext,
+ bool EnteringContext,
+ CorrectTypoContext CTC,
+ const ObjCObjectPointerType *OPT) {
if (Diags.hasFatalErrorOccurred())
return DeclarationName();
// instantiation.
if (!ActiveTemplateInstantiations.empty())
return DeclarationName();
-
+
TypoCorrectionConsumer Consumer(Typo);
+
+ // Perform name lookup to find visible, similarly-named entities.
if (MemberContext) {
LookupVisibleDecls(MemberContext, Res.getLookupKind(), Consumer);
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 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())
+ 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();
- // 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;
+ // \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)
+ if (ED == 0 || !BestName.getAsIdentifierInfo() ||
+ (BestName.getAsIdentifierInfo()->getName().size() / ED) < 3)
return DeclarationName();
// Perform name lookup again with the name we chose, and declare
// 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
if (Found.empty() && !isDependent) {
// If we did not find any names, attempt to correct any typos.
DeclarationName Name = Found.getLookupName();
- if (CorrectTypo(Found, S, &SS, LookupCtx)) {
+ if (DeclarationName Corrected = CorrectTypo(Found, S, &SS, LookupCtx,
+ false, CTC_CXXCasts)) {
FilterAcceptableTemplateNames(Context, Found);
if (!Found.empty() && isa<TemplateDecl>(*Found.begin())) {
if (LookupCtx)
--- /dev/null
+/* FIXME: This is a file containing various typos for which we can
+ suggest corrections but are unable to actually recover from
+ them. Ideally, we would eliminate all such cases and move these
+ tests elsewhere. */
+
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// FIXME: Sadly, the following doesn't work within a function.
+
+unsinged x = 17; // expected-error{{unknown type name 'unsinged'; did you mean 'unsigned'?}}
+
+
--- /dev/null
+/* FIXME: This is a file containing various typos for which we can
+ suggest corrections but are unable to actually recover from
+ them. Ideally, we would eliminate all such cases and move these
+ tests elsewhere. */
+
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+float f(int y) {
+ return static_cst<float>(y); // expected-error{{use of undeclared identifier 'static_cst'; did you mean 'static_cast'?}}
+}
+
@interface IPv6 <Network_Socket> // expected-error{{cannot find protocol declaration for 'Network_Socket'; did you mean 'NetworkSocket'?}}
@end
+
+@interface Super
+- (int)method;
+@end
+
+@interface Sub : Super
+- (int)method;
+@end
+
+@implementation Sub
+- (int)method {
+ return [supper method]; // expected-error{{unknown receiver 'supper'; did you mean 'super'?}}
+}
+
+@end