diff options
-rw-r--r-- | clang/lib/Sema/Sema.h | 24 | ||||
-rw-r--r-- | clang/lib/Sema/SemaCXXScopeSpec.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 56 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclObjC.cpp | 6 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 79 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprObjC.cpp | 43 | ||||
-rw-r--r-- | clang/lib/Sema/SemaInit.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Sema/SemaLookup.cpp | 294 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 3 | ||||
-rw-r--r-- | clang/test/FixIt/fixit-unrecoverable.c | 12 | ||||
-rw-r--r-- | clang/test/FixIt/fixit-unrecoverable.cpp | 11 | ||||
-rw-r--r-- | clang/test/FixIt/typo.m | 15 |
13 files changed, 447 insertions, 105 deletions
diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index d198cf9d2b8..eea0a2bf9e6 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -1431,9 +1431,33 @@ public: 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, diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index 81b3e3116ae..e9d206e53be 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -483,7 +483,8 @@ Sema::CXXScopeTy *Sema::BuildCXXNestedNameSpecifier(Scope *S, // 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) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index a2d0e187285..8e46b9b6576 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -246,32 +246,36 @@ bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II, 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; } } @@ -605,7 +609,7 @@ ObjCInterfaceDecl *Sema::getObjCInterfaceDecl(IdentifierInfo *&Id, // 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() diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 214d57c7dce..24695f35cc2 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -1099,7 +1099,8 @@ Sema::ActOnMemInitializer(DeclPtrTy ConstructorD, // 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 diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index eb3f4222b69..f56a0b196ef 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -120,7 +120,7 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc, 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(); @@ -317,7 +317,7 @@ Sema::FindProtocolDeclaration(bool WarnOnDeclarations, 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(); @@ -554,7 +554,7 @@ Sema::DeclPtrTy Sema::ActOnStartClassImplementation( // 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 diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index d10ea6d4ab9..2a9af994933 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -946,43 +946,55 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, } // 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(); } @@ -2575,7 +2587,8 @@ LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, // 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() @@ -3032,7 +3045,7 @@ Sema::LookupMemberExpr(LookupResult &R, Expr *&BaseExpr, // 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) diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index f7d76b8b9b6..faa1ffc31e2 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -372,7 +372,7 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, // 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) @@ -523,18 +523,37 @@ Sema::ObjCMessageKind Sema::getObjCMessageKind(Scope *S, } } - 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; } } diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 52a5cb13724..dae3d3ae7e6 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -1354,7 +1354,8 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, // 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())) { diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 0e2a10aeb0d..18b6e720506 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -2421,6 +2421,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 +2432,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 +2470,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 +2488,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,6 +2530,9 @@ 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. /// @@ -2502,8 +2541,10 @@ void TypoCorrectionConsumer::FoundDecl(NamedDecl *ND, NamedDecl *Hiding, /// 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(); @@ -2529,8 +2570,10 @@ DeclarationName Sema::CorrectTypo(LookupResult &Res, Scope *S, CXXScopeSpec *SS, // 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); @@ -2551,37 +2594,231 @@ DeclarationName Sema::CorrectTypo(LookupResult &Res, Scope *S, CXXScopeSpec *SS, 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 @@ -2591,11 +2828,14 @@ DeclarationName Sema::CorrectTypo(LookupResult &Res, Scope *S, 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 diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 9816e76530b..28d6f0a40aa 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -250,7 +250,8 @@ void Sema::LookupTemplateName(LookupResult &Found, 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) diff --git a/clang/test/FixIt/fixit-unrecoverable.c b/clang/test/FixIt/fixit-unrecoverable.c new file mode 100644 index 00000000000..5c825f23cfb --- /dev/null +++ b/clang/test/FixIt/fixit-unrecoverable.c @@ -0,0 +1,12 @@ +/* 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'?}} + + diff --git a/clang/test/FixIt/fixit-unrecoverable.cpp b/clang/test/FixIt/fixit-unrecoverable.cpp new file mode 100644 index 00000000000..00ed8978c61 --- /dev/null +++ b/clang/test/FixIt/fixit-unrecoverable.cpp @@ -0,0 +1,11 @@ +/* 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'?}} +} + diff --git a/clang/test/FixIt/typo.m b/clang/test/FixIt/typo.m index 19602fcc8e9..ccd94b78c58 100644 --- a/clang/test/FixIt/typo.m +++ b/clang/test/FixIt/typo.m @@ -87,3 +87,18 @@ void test2(Collide *a) { @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 |