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