diff --git a/clang/include/clang/Sema/TypoCorrection.h b/clang/include/clang/Sema/TypoCorrection.h index c0c7790bb028..24d7d5f387fb 100644 --- a/clang/include/clang/Sema/TypoCorrection.h +++ b/clang/include/clang/Sema/TypoCorrection.h @@ -128,6 +128,11 @@ public: return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); } decl_iterator end() { return CorrectionDecls.end(); } + typedef llvm::SmallVector::const_iterator const_decl_iterator; + const_decl_iterator begin() const { + return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); + } + const_decl_iterator end() const { return CorrectionDecls.end(); } private: bool hasCorrectionDecl() const { diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index fac3c26d0e3a..3333b4eea922 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -9169,6 +9169,54 @@ DiagnoseTwoPhaseOperatorLookup(Sema &SemaRef, OverloadedOperatorKind Op, /*ExplicitTemplateArgs=*/0, Args, NumArgs); } +namespace { +// Callback to limit the allowed keywords and to only accept typo corrections +// that are keywords or whose decls refer to functions (or template functions) +// that accept the given number of arguments. +class RecoveryCallCCC : public CorrectionCandidateCallback { + public: + RecoveryCallCCC(Sema &SemaRef, unsigned NumArgs, bool HasExplicitTemplateArgs) + : NumArgs(NumArgs), HasExplicitTemplateArgs(HasExplicitTemplateArgs) { + WantTypeSpecifiers = SemaRef.getLangOptions().CPlusPlus; + WantRemainingKeywords = false; + } + + virtual bool ValidateCandidate(const TypoCorrection &candidate) { + if (!candidate.getCorrectionDecl()) + return candidate.isKeyword(); + + for (TypoCorrection::const_decl_iterator DI = candidate.begin(), + DIEnd = candidate.end(); DI != DIEnd; ++DI) { + FunctionDecl *FD = 0; + NamedDecl *ND = (*DI)->getUnderlyingDecl(); + if (FunctionTemplateDecl *FTD = dyn_cast(ND)) + FD = FTD->getTemplatedDecl(); + if (!HasExplicitTemplateArgs && !FD) { + if (!(FD = dyn_cast(ND)) && isa(ND)) { + // If the Decl is neither a function nor a template function, + // determine if it is a pointer or reference to a function. If so, + // check against the number of arguments expected for the pointee. + QualType ValType = cast(ND)->getType(); + if (ValType->isAnyPointerType() || ValType->isReferenceType()) + ValType = ValType->getPointeeType(); + if (const FunctionProtoType *FPT = ValType->getAs()) + if (FPT->getNumArgs() == NumArgs) + return true; + } + } + if (FD && FD->getNumParams() >= NumArgs && + FD->getMinRequiredArguments() <= NumArgs) + return true; + } + return false; + } + + private: + unsigned NumArgs; + bool HasExplicitTemplateArgs; +}; +} + /// Attempts to recover from a call where no functions were found. /// /// Returns true if new candidates were found. @@ -9192,9 +9240,7 @@ BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn, LookupResult R(SemaRef, ULE->getName(), ULE->getNameLoc(), Sema::LookupOrdinaryName); - CorrectionCandidateCallback Validator; - Validator.WantTypeSpecifiers = SemaRef.getLangOptions().CPlusPlus; - Validator.WantRemainingKeywords = false; + RecoveryCallCCC Validator(SemaRef, NumArgs, ExplicitTemplateArgs != 0); if (!DiagnoseTwoPhaseLookup(SemaRef, Fn->getExprLoc(), SS, R, ExplicitTemplateArgs, Args, NumArgs) && (!EmptyLookup || diff --git a/clang/test/SemaCXX/typo-correction.cpp b/clang/test/SemaCXX/typo-correction.cpp index 56417cd87dc5..b8795e7d006c 100644 --- a/clang/test/SemaCXX/typo-correction.cpp +++ b/clang/test/SemaCXX/typo-correction.cpp @@ -113,3 +113,27 @@ struct TestRedecl : public BaseDecl { void add_it(int i); // expected-note{{'add_it' declared here}} }; void TestRedecl::add_in(int i) {} // expected-error{{out-of-line definition of 'add_in' does not match any declaration in 'TestRedecl'; did you mean 'add_it'?}} + +// Test the typo-correction callback in BuildRecoveryCallExpr. +// Solves the main issue in PR 9320 of suggesting corrections that take the +// wrong number of arguments. +void revoke(const char*); // expected-note 2{{'revoke' declared here}} +void Test() { + Invoke(); // expected-error{{use of undeclared identifier 'Invoke'}} + Invoke("foo"); // expected-error{{use of undeclared identifier 'Invoke'; did you mean 'revoke'?}} + Invoke("foo", "bar"); // expected-error{{use of undeclared identifier 'Invoke'}} +} +void Test2(void (*invoke)(const char *, int)) { // expected-note{{'invoke' declared here}} + Invoke(); // expected-error{{use of undeclared identifier 'Invoke'}} + Invoke("foo"); // expected-error{{use of undeclared identifier 'Invoke'; did you mean 'revoke'?}} + Invoke("foo", 7); // expected-error{{use of undeclared identifier 'Invoke'; did you mean 'invoke'?}} + Invoke("foo", 7, 22); // expected-error{{use of undeclared identifier 'Invoke'}} +} + +void provoke(const char *x, bool y=false) {} // expected-note 2{{'provoke' declared here}} +void Test3() { + Provoke(); // expected-error{{use of undeclared identifier 'Provoke'}} + Provoke("foo"); // expected-error{{use of undeclared identifier 'Provoke'; did you mean 'provoke'?}} + Provoke("foo", true); // expected-error{{use of undeclared identifier 'Provoke'; did you mean 'provoke'?}} + Provoke("foo", 7, 22); // expected-error{{use of undeclared identifier 'Provoke'}} +}