[cfe-commits] [Patch] Suggest typo corrections for implicit function decls

Richard Smith richard at metafoo.co.uk
Fri Dec 2 15:09:05 PST 2011


Hi Hans,

On Sat, November 26, 2011 19:29, Hans Wennborg wrote:
> I noticed that C programmers don't get to enjoy all of Clang's typo
> corrections: a mistyped function call becomes an implicit function
> declaration (with a warning). The attached patch adds typo correction to the
> already existing warnings.
>
> Please take a look and let me know if it's okay for me to land this.

Sorry for the delay. Some comments below.

diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index cd2f071..4d45e32 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -7262,13 +7262,49 @@ NamedDecl
*Sema::ImplicitlyDefineFunction(SourceLocation Loc,
     return Pos->second;
   }

+  // See if we can find a typo correction.
+  TypoCorrection Corrected;
+  FunctionDecl* Func = NULL;

Please put the space to the right of the *, not to the left. Also, the null
pointer constant is written as 0 more frequently than as NULL in Sema.

+  std::string CorrectedStr;
+  std::string CorrectedQuotedStr;
+  if (S && (Corrected = CorrectTypo(DeclarationNameInfo(&II, Loc),
+                                    LookupOrdinaryName, S, 0))) {
+    // Since this is an implicit function declaration, we are only
+    // interested in a potential typo for a function name.
+    if ((Func = dyn_cast_or_null<FunctionDecl>(
+            Corrected.getCorrectionDecl()))) {
+      CorrectedStr = Corrected.getAsString(getLangOptions());
+      CorrectedQuotedStr = Corrected.getQuoted(getLangOptions());
+    }
+  }
+
   // Extension in C99.  Legal in C90, but warn about it.
-  if (II.getName().startswith("__builtin_"))
-    Diag(Loc, diag::warn_builtin_unknown) << &II;
-  else if (getLangOptions().C99)
-    Diag(Loc, diag::ext_implicit_function_decl) << &II;
-  else
-    Diag(Loc, diag::warn_implicit_function_decl) << &II;
+  // If we found a typo correction, then suggest that.
+  if (Func) {
+    unsigned diagnostic_kind;
+    if (II.getName().startswith("__builtin_"))
+      diagnostic_kind = diag::warn_builtin_unknown_suggest;
+    else if (getLangOptions().C99)
+      diagnostic_kind = diag::ext_implicit_function_decl_suggest;
+    else
+      diagnostic_kind = diag::warn_implicit_function_decl_suggest;
+
+    Diag(Loc, diagnostic_kind) << &II << CorrectedQuotedStr
+      << FixItHint::CreateReplacement(Loc, CorrectedStr);

Please move this fix-it to a separate note (at least for the implicit function
declaration case -- see below). When a fix-it is issued on a warning or an
error, we must recover as if the fixed code had been parsed (and for a
warning, this essentially means the fix-it must not change the semantics).

+
+    if (Func->getLocation().isValid())
+      Diag(Func->getLocation(), diag::note_previous_decl) << CorrectedQuotedStr;
+  } else {
+    unsigned diagnostic_kind;
+    if (II.getName().startswith("__builtin_"))
+      diagnostic_kind = diag::warn_builtin_unknown;
+    else if (getLangOptions().C99)
+      diagnostic_kind = diag::ext_implicit_function_decl;
+    else
+      diagnostic_kind = diag::warn_implicit_function_decl;
+
+    Diag(Loc, diagnostic_kind) << &II;
+  }

   // Set a Declarator for the implicit definition: int foo();
   const char *Dummy;
diff --git a/test/Misc/warning-flags.c b/test/Misc/warning-flags.c
index 5fc48cd..87a982f 100644
--- a/test/Misc/warning-flags.c
+++ b/test/Misc/warning-flags.c
@@ -17,7 +17,7 @@ This test serves two purposes:

 The list of warnings below should NEVER grow.  It should gradually shrink to 0.

-CHECK: Warnings without flags (274):
+CHECK: Warnings without flags (275):

New warnings without flags should not be added (not even if you're just
splitting a warning in two -- please add a flag; we all have taxes to pay).
However, it strikes me that a Warning, DefaultError message with no -W flag,
like this one, is a strange construct indeed, and it should probably simply be
changed into an Error. With that done, you can keep the fix-it on the error
for the __builtin_ case, if you also recover as if the user had typed the
correct builtin name.

 CHECK-NEXT:   ext_anon_param_requires_type_specifier
 CHECK-NEXT:   ext_anonymous_struct_union_qualified
 CHECK-NEXT:   ext_array_init_copy
@@ -129,6 +129,7 @@ CHECK-NEXT:   warn_bitfield_width_exceeds_type_size
 CHECK-NEXT:   warn_bool_switch_condition
 CHECK-NEXT:   warn_braces_around_scalar_init
 CHECK-NEXT:   warn_builtin_unknown
+CHECK-NEXT:   warn_builtin_unknown_suggest
 CHECK-NEXT:   warn_c_kext
 CHECK-NEXT:   warn_call_to_pure_virtual_member_function_from_ctor_dtor
 CHECK-NEXT:   warn_call_wrong_number_of_arguments
diff --git a/test/Sema/builtins.c b/test/Sema/builtins.c
index 64efefc..92a49bd 100644
--- a/test/Sema/builtins.c
+++ b/test/Sema/builtins.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -fsyntax-only -verify -pedantic -triple=i686-apple-darwin9
+// RUN: %clang_cc1 %s -fsyntax-only -verify -pedantic -fno-spell-checking
-triple=i686-apple-darwin9

Rather than turning off spell-checking, it'd be better to update the test to
expect the new output (and add more cases here if we've lost some coverage).

 // This test needs to set the target because it uses __builtin_ia32_vec_ext_v4si

 int test1(float a, int b) {
diff --git a/test/Sema/implicit-decl.c b/test/Sema/implicit-decl.c
index f455977..f4b9016 100644
--- a/test/Sema/implicit-decl.c
+++ b/test/Sema/implicit-decl.c
@@ -3,6 +3,8 @@
 typedef int int32_t;
 typedef unsigned char Boolean;

+extern int printf(__const char *__restrict __format, ...); //
expected-note{{'printf' declared here}}
+
 void func() {
    int32_t *vector[16];
    const char compDesc[16 + 1];
@@ -10,6 +12,10 @@ void func() {
    if (_CFCalendarDecomposeAbsoluteTimeV(compDesc, vector, compCount)) { //
expected-note {{previous implicit declaration is here}} \
          expected-warning {{implicit declaration of function
'_CFCalendarDecomposeAbsoluteTimeV' is invalid in C99}}
    }
+
+   printg("Hello, World!\n"); // expected-warning{{implicit declaration of
function
'printg' is invalid in C99; did you mean 'printf'?}}
+
+  __builtin_is_les(1, 3); // expected-error{{use of unknown builtin
'__builtin_is_les'; did you mean '__builtin_is_less'?}}
 }
 Boolean _CFCalendarDecomposeAbsoluteTimeV(const char *componentDesc, int32_t
**vector, int32_t count) { // expected-error{{conflicting types for
'_CFCalendarDecomposeAbsoluteTimeV'}}
  return 0;

Could you add a test for the C89 case, please?

Thanks,
Richard




More information about the cfe-commits mailing list