r185881 - Attempt typo correction for function calls with the wrong number of arguments.
Kaelyn Uhrain
rikka at google.com
Mon Jul 8 16:13:44 PDT 2013
Author: rikka
Date: Mon Jul 8 18:13:44 2013
New Revision: 185881
URL: http://llvm.org/viewvc/llvm-project?rev=185881&view=rev
Log:
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.
Added:
cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp
Modified:
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/lib/Sema/SemaExpr.cpp
cfe/trunk/test/FixIt/typo.cpp
cfe/trunk/test/SemaCXX/default1.cpp
cfe/trunk/test/SemaCXX/typo-correction.cpp
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=185881&r1=185880&r2=185881&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Jul 8 18:13:44 2013
@@ -5339,6 +5339,14 @@ def err_typecheck_call_too_few_args_at_l
"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_
"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">;
Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=185881&r1=185880&r2=185881&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Mon Jul 8 18:13:44 2013
@@ -3807,6 +3807,64 @@ Sema::getVariadicCallType(FunctionDecl *
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<Expr *> 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<FunctionDecl>(*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<ValueDecl>(ND) || isa<FunctionTemplateDecl>(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 *
// 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<unsigned>(Args.size())
+ << Fn->getSourceRange() << CorrectedQuotedStr
+ << FixItHint::CreateReplacement(TC.getCorrectionRange(),
+ CorrectedStr);
+ Diag(TC.getCorrectionDeclAs<FunctionDecl>()->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 *
<< 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 *
// 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<unsigned>(Args.size())
+ << Fn->getSourceRange() << CorrectedQuotedStr
+ << FixItHint::CreateReplacement(TC.getCorrectionRange(),
+ CorrectedStr);
+ Diag(TC.getCorrectionDeclAs<FunctionDecl>()->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 *
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;
Modified: cfe/trunk/test/FixIt/typo.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/FixIt/typo.cpp?rev=185881&r1=185880&r2=185881&view=diff
==============================================================================
--- cfe/trunk/test/FixIt/typo.cpp (original)
+++ cfe/trunk/test/FixIt/typo.cpp Mon Jul 8 18:13:44 2013
@@ -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'?}}
+}
Modified: cfe/trunk/test/SemaCXX/default1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/default1.cpp?rev=185881&r1=185880&r2=185881&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/default1.cpp (original)
+++ cfe/trunk/test/SemaCXX/default1.cpp Mon Jul 8 18:13:44 2013
@@ -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}}
}
}
Added: cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp?rev=185881&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp (added)
+++ cfe/trunk/test/SemaCXX/typo-correction-pt2.cpp Mon Jul 8 18:13:44 2013
@@ -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'?}}
+}
+}
Modified: cfe/trunk/test/SemaCXX/typo-correction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/typo-correction.cpp?rev=185881&r1=185880&r2=185881&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/typo-correction.cpp (original)
+++ cfe/trunk/test/SemaCXX/typo-correction.cpp Mon Jul 8 18:13:44 2013
@@ -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'$}}
+};
+}
More information about the cfe-commits
mailing list