[cfe-commits] PATCH: Add typo correction to DiagnoseInvalidRedeclaration (issue 4908044)

rikka at google.com rikka at google.com
Mon Aug 15 15:12:52 PDT 2011


Reviewers: cfe-commits_cs.uiuc.edu,

Description:
Reworks DiagnoseInvalidRedeclaration to add the ability to correct typos
when diagnosing invalid function redeclarations

Please review this at http://codereview.appspot.com/4908044/

Affected files:
   M include/clang/Basic/DiagnosticSemaKinds.td
   M lib/Sema/SemaDecl.cpp
   A test/SemaCXX/function-redecl-typo-correction.cpp


-------------- next part --------------
Index: include/clang/Basic/DiagnosticSemaKinds.td
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 2430372c3df4b280bb4c44f8e76dd2ee37b9870b..61a0bf1d3bcb493c94406730998a26e7816a6146 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -610,6 +610,8 @@ def err_tagless_friend_type_template : Error<
   "friend type templates must use an elaborated type">;
 def err_no_matching_local_friend : Error<
   "no matching function found in local scope">;
+def err_no_matching_local_friend_suggest : Error<
+  "no matching function %0 found in local scope; did you mean %2">;
 def err_partial_specialization_friend : Error<
   "partial specialization cannot be declared as a friend">;
 
@@ -3028,6 +3030,9 @@ def err_member_def_undefined_record : Error<
   "out-of-line definition of %0 from class %1 without definition">;
 def err_member_def_does_not_match : Error<
   "out-of-line definition of %0 does not match any declaration in %1">;
+def err_member_def_does_not_match_suggest : Error<
+  "out-of-line definition of %0 does not match any declaration in %1; "
+  "did you mean %2">;
 def err_member_def_does_not_match_ret_type : Error<
   "out-of-line definition of %q0 differs from the declaration in the return type">;
 def err_nonstatic_member_out_of_line : Error<
Index: lib/Sema/SemaDecl.cpp
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index c1a6c60d28c8f7f32a86169056229a42bea54727..31f86b3674038f9336877481fade4f9991179282 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -4182,28 +4182,77 @@ bool Sema::AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) {
   return AddedAny;
 }
 
-static void DiagnoseInvalidRedeclaration(Sema &S, FunctionDecl *NewFD) {
-  LookupResult Prev(S, NewFD->getDeclName(), NewFD->getLocation(),
+static void DiagnoseInvalidRedeclaration(Sema &S, FunctionDecl *NewFD,
+                                         bool isFriendDecl) {
+  DeclarationName Name = NewFD->getDeclName();
+  DeclContext *DC = NewFD->getDeclContext();
+  LookupResult Prev(S, Name, NewFD->getLocation(),
                     Sema::LookupOrdinaryName, Sema::ForRedeclaration);
   llvm::SmallVector<unsigned, 1> MismatchedParams;
-  S.LookupQualifiedName(Prev, NewFD->getDeclContext());
+  llvm::SmallVector<std::pair<FunctionDecl*, unsigned>, 1> NearMatches;
+  TypoCorrection Correction;
+  unsigned DiagMsg = isFriendDecl ? diag::err_no_matching_local_friend
+                                  : diag::err_member_def_does_not_match;
+
+  NewFD->setInvalidDecl();
+  S.LookupQualifiedName(Prev, DC);
   assert(!Prev.isAmbiguous() &&
          "Cannot have an ambiguity in previous-declaration lookup");
-  for (LookupResult::iterator Func = Prev.begin(), FuncEnd = Prev.end();
-       Func != FuncEnd; ++Func) {
-    FunctionDecl *FD = dyn_cast<FunctionDecl>(*Func);
-    if (FD && isNearlyMatchingFunction(S.Context, FD, NewFD,
-                                       MismatchedParams)) {
-      if (MismatchedParams.size() > 0) {
-        unsigned Idx = MismatchedParams.front();
-        ParmVarDecl *FDParam = FD->getParamDecl(Idx);
-        S.Diag(FDParam->getTypeSpecStartLoc(),
-               diag::note_member_def_close_param_match)
-            << Idx+1 << FDParam->getType() << NewFD->getParamDecl(Idx)->getType();
-      } else
-        S.Diag(FD->getLocation(), diag::note_member_def_close_match);
+  // If the qualified name lookup yielded nothing, try typo correction
+  if (!Prev.empty()) {
+    for (LookupResult::iterator Func = Prev.begin(), FuncEnd = Prev.end();
+         Func != FuncEnd; ++Func) {
+      FunctionDecl *FD = dyn_cast<FunctionDecl>(*Func);
+      if (FD && isNearlyMatchingFunction(S.Context, FD, NewFD,
+                                         MismatchedParams)) {
+        NearMatches.push_back(std::pair<FunctionDecl*, unsigned>(FD,
+            // Add 1 to the index so that 0 can mean the mismatch didn't
+            // involve a parameter
+            MismatchedParams.size() > 0 ? MismatchedParams.front() + 1 : 0));
+      }
+    }
+  } else if ((Correction = S.CorrectTypo(Prev.getLookupNameInfo(),
+                                         Prev.getLookupKind(), NULL, NULL, DC))) {
+    DiagMsg = isFriendDecl ? diag::err_no_matching_local_friend_suggest
+                           : diag::err_member_def_does_not_match_suggest;
+    for (TypoCorrection::decl_iterator CDecl = Correction.begin(),
+                                    CDeclEnd = Correction.end();
+         CDecl != CDeclEnd; ++CDecl) {
+      FunctionDecl *FD = dyn_cast<FunctionDecl>(*CDecl);
+      if (FD && isNearlyMatchingFunction(S.Context, FD, NewFD,
+                                         MismatchedParams)) {
+        NearMatches.push_back(std::pair<FunctionDecl*, unsigned>(FD,
+            // Add 1 to the index so that 0 can mean the mismatch didn't
+            // involve a parameter
+            MismatchedParams.size() > 0 ? MismatchedParams.front() + 1 : 0));
+      }
     }
   }
+
+  if (Correction)
+    S.Diag(NewFD->getLocation(), DiagMsg)
+        << Name << DC << Correction.getQuoted(S.getLangOptions())
+        << FixItHint::CreateReplacement(
+            NewFD->getLocation(), Correction.getAsString(S.getLangOptions()));
+  else
+    S.Diag(NewFD->getLocation(), DiagMsg) << Name << DC << NewFD->getLocation();
+
+  for (llvm::SmallVector<std::pair<FunctionDecl*, unsigned>, 1>::iterator
+       NearMatch = NearMatches.begin(), NearMatchEnd = NearMatches.end();
+       NearMatch != NearMatchEnd; ++NearMatch) {
+    FunctionDecl *FD = NearMatch->first;
+
+    if (unsigned Idx = NearMatch->second) {
+      ParmVarDecl *FDParam = FD->getParamDecl(Idx-1);
+      S.Diag(FDParam->getTypeSpecStartLoc(),
+             diag::note_member_def_close_param_match)
+          << Idx << FDParam->getType() << NewFD->getParamDecl(Idx-1)->getType();
+    } else if (Correction) {
+      S.Diag(FD->getLocation(), diag::note_previous_decl)
+        << Correction.getQuoted(S.getLangOptions());
+    } else
+      S.Diag(FD->getLocation(), diag::note_member_def_close_match);
+  }
 }
 
 NamedDecl*
@@ -4897,19 +4946,14 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
               // Complain about this problem, and attempt to suggest close
               // matches (e.g., those that differ only in cv-qualifiers and
               // whether the parameter types are references).
-              Diag(D.getIdentifierLoc(), diag::err_member_def_does_not_match)
-              << Name << DC << D.getCXXScopeSpec().getRange();
-              NewFD->setInvalidDecl();
 
-              DiagnoseInvalidRedeclaration(*this, NewFD);
+              DiagnoseInvalidRedeclaration(*this, NewFD, false);
             }
 
         // Unqualified local friend declarations are required to resolve
         // to something.
         } else if (isFriend && cast<CXXRecordDecl>(CurContext)->isLocalClass()) {
-          Diag(D.getIdentifierLoc(), diag::err_no_matching_local_friend);
-          NewFD->setInvalidDecl();
-          DiagnoseInvalidRedeclaration(*this, NewFD);
+          DiagnoseInvalidRedeclaration(*this, NewFD, true);
         }
 
     } else if (!IsFunctionDefinition && D.getCXXScopeSpec().isSet() &&
Index: test/SemaCXX/function-redecl-typo-correction.cpp
diff --git a/test/SemaCXX/function-redecl-typo-correction.cpp b/test/SemaCXX/function-redecl-typo-correction.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b1e929932f1147928e57d3029f4e5cd382c23225
--- /dev/null
+++ b/test/SemaCXX/function-redecl-typo-correction.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+class A {
+ void typocorrection(); // expected-note {{'typocorrection' declared here}}
+};
+
+void A::Notypocorrection() { // expected-error {{out-of-line definition of 'Notypocorrection' does not match any declaration in 'A'; did you mean 'typocorrection'}}
+}
+
+
+namespace test0 {
+  void foo() {
+    void Bar(); // expected-note {{'Bar' declared here}}
+    class A {
+      friend void bar(); // expected-error {{no matching function 'bar' found in local scope; did you mean 'Bar'}}
+    };
+  }
+}
+
+
+class B {
+ void typocorrection(const int); // expected-note {{type of 1st parameter of member declaration does not match definition}}
+ void typocorrection(double);
+};
+
+void B::Notypocorrection(int) { // expected-error {{out-of-line definition of 'Notypocorrection' does not match any declaration in 'B'; did you mean 'typocorrection'}}
+}


More information about the cfe-commits mailing list