From: Kaelyn Uhrain Date: Mon, 8 Jul 2013 23:13:44 +0000 (+0000) Subject: Attempt typo correction for function calls with the wrong number of arguments. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6c4898b6ff23950cddca6948ef3fa0dd1848f6f1;p=clang Attempt typo correction for function calls with the wrong number of arguments. Combined with typo correction's new ability to apply global/absolute nested name specifiers to possible corrections, cases such as in PR12287 where the desired function is being shadowed by a lexically closer function with the same name but a different number of parameters will now include a FixIt. On a side note, since the test for this change caused test/SemaCXX/typo-correction.cpp to exceed the typo correction limit for a single file, I've included a test case for exceeding the limit and added some comments to both the original and part two of typo-correction.cpp warning future editors of the files about the limit. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@185881 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 6574518044..9a075fe8c8 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -5339,6 +5339,14 @@ def err_typecheck_call_too_few_args_at_least_one : Error< "too few %select{|||execution configuration }0arguments to " "%select{function|block|method|kernel function}0 call, " "at least argument %1 must be specified">; +def err_typecheck_call_too_few_args_suggest : Error< + "too few %select{|||execution configuration }0arguments to " + "%select{function|block|method|kernel function}0 call, " + "expected %1, have %2; did you mean %3?">; +def err_typecheck_call_too_few_args_at_least_suggest : Error< + "too few %select{|||execution configuration }0arguments to " + "%select{function|block|method|kernel function}0 call, " + "expected at least %1, have %2; did you mean %3?">; def err_typecheck_call_too_many_args : Error< "too many %select{|||execution configuration }0arguments to " "%select{function|block|method|kernel function}0 call, " @@ -5355,6 +5363,14 @@ def err_typecheck_call_too_many_args_at_most_one : Error< "too many %select{|||execution configuration }0arguments to " "%select{function|block|method|kernel function}0 call, " "expected at most single argument %1, have %2 arguments">; +def err_typecheck_call_too_many_args_suggest : Error< + "too many %select{|||execution configuration }0arguments to " + "%select{function|block|method|kernel function}0 call, " + "expected %1, have %2; did you mean %3?">; +def err_typecheck_call_too_many_args_at_most_suggest : Error< + "too many %select{|||execution configuration }0arguments to " + "%select{function|block|method|kernel function}0 call, " + "expected at most %1, have %2; did you mean %3?">; def note_callee_decl : Note< "%0 declared here">; def note_defined_here : Note<"%0 defined here">; diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index b4dbcd2c9e..51afa31cd7 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -3807,6 +3807,64 @@ Sema::getVariadicCallType(FunctionDecl *FDecl, const FunctionProtoType *Proto, return VariadicDoesNotApply; } +namespace { +class FunctionCallCCC : public FunctionCallFilterCCC { +public: + FunctionCallCCC(Sema &SemaRef, const IdentifierInfo *FuncName, + unsigned NumArgs, bool HasExplicitTemplateArgs) + : FunctionCallFilterCCC(SemaRef, NumArgs, HasExplicitTemplateArgs), + FunctionName(FuncName) {} + + virtual bool ValidateCandidate(const TypoCorrection &candidate) { + if (!candidate.getCorrectionSpecifier() || + candidate.getCorrectionAsIdentifierInfo() != FunctionName) { + return false; + } + + return FunctionCallFilterCCC::ValidateCandidate(candidate); + } + +private: + const IdentifierInfo *const FunctionName; +}; +} + +static TypoCorrection TryTypoCorrectionForCall(Sema &S, + DeclarationNameInfo FuncName, + ArrayRef Args) { + FunctionCallCCC CCC(S, FuncName.getName().getAsIdentifierInfo(), + Args.size(), false); + if (TypoCorrection Corrected = + S.CorrectTypo(FuncName, Sema::LookupOrdinaryName, + S.getScopeForContext(S.CurContext), NULL, CCC)) { + if (NamedDecl *ND = Corrected.getCorrectionDecl()) { + if (Corrected.isOverloaded()) { + OverloadCandidateSet OCS(FuncName.getLoc()); + OverloadCandidateSet::iterator Best; + for (TypoCorrection::decl_iterator CD = Corrected.begin(), + CDEnd = Corrected.end(); + CD != CDEnd; ++CD) { + if (FunctionDecl *FD = dyn_cast(*CD)) + S.AddOverloadCandidate(FD, DeclAccessPair::make(FD, AS_none), Args, + OCS); + } + switch (OCS.BestViableFunction(S, FuncName.getLoc(), Best)) { + case OR_Success: + ND = Best->Function; + Corrected.setCorrectionDecl(ND); + break; + default: + break; + } + } + if (isa(ND) || isa(ND)) { + return Corrected; + } + } + } + return TypoCorrection(); +} + /// ConvertArgumentsForCall - Converts the arguments specified in /// Args/NumArgs to the parameter types of the function FDecl with /// function prototype Proto. Call is the call expression itself, and @@ -3841,7 +3899,25 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn, // arguments for the remaining parameters), don't make the call. if (Args.size() < NumArgsInProto) { if (Args.size() < MinArgs) { - if (MinArgs == 1 && FDecl && FDecl->getParamDecl(0)->getDeclName()) + TypoCorrection TC; + if (FDecl && (TC = TryTypoCorrectionForCall( + *this, DeclarationNameInfo(FDecl->getDeclName(), + Fn->getLocStart()), + Args))) { + std::string CorrectedStr(TC.getAsString(getLangOpts())); + std::string CorrectedQuotedStr(TC.getQuoted(getLangOpts())); + unsigned diag_id = + MinArgs == NumArgsInProto && !Proto->isVariadic() + ? diag::err_typecheck_call_too_few_args_suggest + : diag::err_typecheck_call_too_few_args_at_least_suggest; + Diag(RParenLoc, diag_id) + << FnKind << MinArgs << static_cast(Args.size()) + << Fn->getSourceRange() << CorrectedQuotedStr + << FixItHint::CreateReplacement(TC.getCorrectionRange(), + CorrectedStr); + Diag(TC.getCorrectionDeclAs()->getLocStart(), + diag::note_previous_decl) << CorrectedQuotedStr; + } else if (MinArgs == 1 && FDecl && FDecl->getParamDecl(0)->getDeclName()) Diag(RParenLoc, MinArgs == NumArgsInProto && !Proto->isVariadic() ? diag::err_typecheck_call_too_few_args_one : diag::err_typecheck_call_too_few_args_at_least_one) @@ -3856,7 +3932,7 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn, << Fn->getSourceRange(); // Emit the location of the prototype. - if (FDecl && !FDecl->getBuiltinID() && !IsExecConfig) + if (!TC && FDecl && !FDecl->getBuiltinID() && !IsExecConfig) Diag(FDecl->getLocStart(), diag::note_callee_decl) << FDecl; @@ -3869,7 +3945,25 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn, // them. if (Args.size() > NumArgsInProto) { if (!Proto->isVariadic()) { - if (NumArgsInProto == 1 && FDecl && FDecl->getParamDecl(0)->getDeclName()) + TypoCorrection TC; + if (FDecl && (TC = TryTypoCorrectionForCall( + *this, DeclarationNameInfo(FDecl->getDeclName(), + Fn->getLocStart()), + Args))) { + std::string CorrectedStr(TC.getAsString(getLangOpts())); + std::string CorrectedQuotedStr(TC.getQuoted(getLangOpts())); + unsigned diag_id = + MinArgs == NumArgsInProto && !Proto->isVariadic() + ? diag::err_typecheck_call_too_many_args_suggest + : diag::err_typecheck_call_too_many_args_at_most_suggest; + Diag(Args[NumArgsInProto]->getLocStart(), diag_id) + << FnKind << NumArgsInProto << static_cast(Args.size()) + << Fn->getSourceRange() << CorrectedQuotedStr + << FixItHint::CreateReplacement(TC.getCorrectionRange(), + CorrectedStr); + Diag(TC.getCorrectionDeclAs()->getLocStart(), + diag::note_previous_decl) << CorrectedQuotedStr; + } else if (NumArgsInProto == 1 && FDecl && FDecl->getParamDecl(0)->getDeclName()) Diag(Args[NumArgsInProto]->getLocStart(), MinArgs == NumArgsInProto ? diag::err_typecheck_call_too_many_args_one @@ -3891,7 +3985,7 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn, Args.back()->getLocEnd()); // Emit the location of the prototype. - if (FDecl && !FDecl->getBuiltinID() && !IsExecConfig) + if (!TC && FDecl && !FDecl->getBuiltinID() && !IsExecConfig) Diag(FDecl->getLocStart(), diag::note_callee_decl) << FDecl; diff --git a/test/FixIt/typo.cpp b/test/FixIt/typo.cpp index 87df1c07f3..2a743991be 100644 --- a/test/FixIt/typo.cpp +++ b/test/FixIt/typo.cpp @@ -127,3 +127,11 @@ void func2() { // to replace base::i with derived::i as we would for other qualified name misspellings. // d.base::i = 3; } + +class A { + void bar(int); +}; +void bar(int, int); // expected-note{{'::bar' declared here}} +void A::bar(int x) { + bar(x, 5); // expected-error{{too many arguments to function call, expected 1, have 2; did you mean '::bar'?}} +} diff --git a/test/SemaCXX/default1.cpp b/test/SemaCXX/default1.cpp index c8c197e153..b661776b6e 100644 --- a/test/SemaCXX/default1.cpp +++ b/test/SemaCXX/default1.cpp @@ -21,7 +21,7 @@ struct X { X(int); }; -void j(X x = 17); +void j(X x = 17); // expected-note{{'::j' declared here}} struct Y { // expected-note 2{{candidate}} explicit Y(int); @@ -46,8 +46,13 @@ int l () { int i () { void j (int f = 4); { - void j (int f); // expected-note{{'j' declared here}} - j(); // expected-error{{too few arguments to function call, single argument 'f' was not specified}} + void j (int f); + j(); // expected-error{{too few arguments to function call, expected 1, have 0; did you mean '::j'?}} + } + void jj (int f = 4); + { + void jj (int f); // expected-note{{'jj' declared here}} + jj(); // expected-error{{too few arguments to function call, single argument 'f' was not specified}} } } diff --git a/test/SemaCXX/typo-correction-pt2.cpp b/test/SemaCXX/typo-correction-pt2.cpp new file mode 100644 index 0000000000..ee4971709f --- /dev/null +++ b/test/SemaCXX/typo-correction-pt2.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c++11-extensions %s +// +// FIXME: This file is overflow from test/SemaCXX/typo-correction.cpp due to a +// hard-coded limit of 20 different typo corrections Sema::CorrectTypo will +// attempt within a single file (which is to avoid having very broken files take +// minutes to finally be rejected by the parser). + +namespace PR12287 { +class zif { + void nab(int); +}; +void nab(); // expected-note{{'::PR12287::nab' declared here}} +void zif::nab(int) { + nab(); // expected-error{{too few arguments to function call, expected 1, have 0; did you mean '::PR12287::nab'?}} +} +} diff --git a/test/SemaCXX/typo-correction.cpp b/test/SemaCXX/typo-correction.cpp index 62d870c826..c2c79d2203 100644 --- a/test/SemaCXX/typo-correction.cpp +++ b/test/SemaCXX/typo-correction.cpp @@ -1,4 +1,8 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wno-c++11-extensions %s +// +// WARNING: Do not add more typo correction test cases to this file lest you run +// afoul the hard-coded limit (escape hatch) of 20 different typos whose +// correction was attempted by Sema::CorrectTypo struct errc { int v_; @@ -314,3 +318,10 @@ namespace b6956809_test2 { int k = s.methodd((void*)0); // expected-error{{no member named 'methodd' in 'b6956809_test2::S'; did you mean 'method'?}} } } + +namespace CorrectTypo_has_reached_its_limit { +int flibberdy(); // no note here +int no_correction() { + return gibberdy(); // expected-error-re{{use of undeclared identifier 'gibberdy'$}} +}; +}