PR10828: Produce a warning when a no-arguments function is declared in block
authorRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 5 Jan 2012 04:12:21 +0000 (04:12 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 5 Jan 2012 04:12:21 +0000 (04:12 +0000)
scope, when no other indication is provided that the user intended to declare a
function rather than a variable.

Remove some false positives from the existing 'parentheses disambiguated as a
function' warning by suppressing it when the declaration is marked as 'typedef'
or 'extern'.

Add a new warning group -Wvexing-parse containing both of these warnings.

The new warning is enabled by default; despite a number of false positives (and
one bug) in clang's test-suite, I have only found genuine bugs with it when
running it over a significant quantity of real C++ code.

llvm-svn: 147599

clang/include/clang/Basic/DiagnosticGroups.td
clang/include/clang/Basic/DiagnosticParseKinds.td
clang/include/clang/Sema/DeclSpec.h
clang/lib/Parse/ParseDecl.cpp
clang/test/CXX/basic/basic.link/p9.cpp
clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p11.cpp
clang/test/Misc/warning-flags.c
clang/test/SemaCXX/conditional-expr.cpp
clang/test/SemaCXX/decl-expr-ambiguity.cpp
clang/test/SemaTemplate/class-template-ctor-initializer.cpp

index c54abaa..9c03580 100644 (file)
@@ -231,6 +231,7 @@ def OverridingMethodMismatch : DiagGroup<"overriding-method-mismatch">;
 def : DiagGroup<"variadic-macros">;
 def VariadicMacros : DiagGroup<"variadic-macros">;
 def VectorConversions : DiagGroup<"vector-conversions">;      // clang specific
+def VexingParse : DiagGroup<"vexing-parse">;
 def VLA : DiagGroup<"vla">;
 def VolatileRegisterVar : DiagGroup<"volatile-register-var">;
 
index 90d4721..bd30f48 100644 (file)
@@ -387,7 +387,11 @@ def err_expected_lparen_after_type : Error<
 def err_expected_equal_after_declarator : Error<
   "expected '=' after declarator">;
 def warn_parens_disambiguated_as_function_decl : Warning<
-  "parentheses were disambiguated as a function declarator">;
+  "parentheses were disambiguated as a function declarator">,
+  InGroup<VexingParse>;
+def warn_empty_parens_are_function_decl : Warning<
+  "empty parentheses interpreted as a function declaration">,
+  InGroup<VexingParse>;
 def warn_dangling_else : Warning<
   "add explicit braces to avoid dangling else">,
   InGroup<DanglingElse>;
index 44823c8..a601210 100644 (file)
@@ -1626,6 +1626,13 @@ public:
   bool mayBeFollowedByCXXDirectInit() const {
     if (hasGroupingParens()) return false;
 
+    if (getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_typedef)
+      return false;
+
+    if (getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_extern &&
+        Context != FileContext)
+      return false;
+
     switch (Context) {
     case FileContext:
     case BlockContext:
index ee4a51e..d3c8211 100644 (file)
@@ -1111,6 +1111,21 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
   if (FirstDecl)
     DeclsInGroup.push_back(FirstDecl);
 
+  // In C++, "T var();" at block scope is probably an attempt to initialize a
+  // variable, not a function declaration. We don't catch this case earlier,
+  // since there is no ambiguity here.
+  if (getLang().CPlusPlus && Context != Declarator::FileContext &&
+      D.getNumTypeObjects() == 1 && D.isFunctionDeclarator() &&
+      D.getDeclSpec().getStorageClassSpecAsWritten()
+        == DeclSpec::SCS_unspecified &&
+      D.getDeclSpec().getTypeSpecType() != DeclSpec::TST_void) {
+    DeclaratorChunk &C = D.getTypeObject(0);
+    if (C.Fun.NumArgs == 0 && !C.Fun.isVariadic && !C.Fun.TrailingReturnType &&
+        C.Fun.getExceptionSpecType() == EST_None)
+      Diag(C.Loc, diag::warn_empty_parens_are_function_decl)
+        << SourceRange(C.Loc, C.EndLoc);
+  }
+
   bool ExpectSemi = Context != Declarator::ForContext;
 
   // If we don't have a comma, it is either the end of the list (a ';') or an
index bd16b02..c895253 100644 (file)
@@ -6,6 +6,5 @@ namespace N { } // expected-note{{here}}
 // First bullet: two names with external linkage that refer to
 // different kinds of entities.
 void f() {
-  int N(); // expected-error{{redefinition}}
+  int N(); // expected-error{{redefinition}} expected-warning{{interpreted as a function declaration}}
 }
-
index 63b3022..b1dcf4d 100644 (file)
@@ -25,13 +25,13 @@ namespace test1 {
 namespace test2 {
   namespace ns { void foo(); } // expected-note 2 {{target of using declaration}}
   void test0() {
-    int foo(); // expected-note {{conflicting declaration}}
+    int foo(); // expected-note {{conflicting declaration}} expected-warning{{function declaration}}
     using ns::foo; // expected-error {{target of using declaration conflicts with declaration already in scope}}
   }
 
   void test1() {
     using ns::foo; //expected-note {{using declaration}}
-    int foo(); // expected-error {{declaration conflicts with target of using declaration already in scope}}
+    int foo(); // expected-error {{declaration conflicts with target of using declaration already in scope}} expected-warning{{function declaration}}
   }
 }
 
@@ -39,7 +39,7 @@ namespace test3 {
   namespace ns { void foo(); } // expected-note 2 {{target of using declaration}}
   class Test0 {
     void test() {
-      int foo(); // expected-note {{conflicting declaration}}
+      int foo(); // expected-note {{conflicting declaration}} expected-warning{{function declaration}}
       using ns::foo; // expected-error {{target of using declaration conflicts with declaration already in scope}}
     }
   };
@@ -47,7 +47,7 @@ namespace test3 {
   class Test1 {
     void test() {
       using ns::foo; //expected-note {{using declaration}}
-      int foo(); // expected-error {{declaration conflicts with target of using declaration already in scope}}
+      int foo(); // expected-error {{declaration conflicts with target of using declaration already in scope}} expected-warning{{function declaration}}
     }
   };
 }
@@ -56,7 +56,7 @@ namespace test4 {
   namespace ns { void foo(); } // expected-note 2 {{target of using declaration}}
   template <typename> class Test0 {
     void test() {
-      int foo(); // expected-note {{conflicting declaration}}
+      int foo(); // expected-note {{conflicting declaration}} expected-warning{{function declaration}}
       using ns::foo; // expected-error {{target of using declaration conflicts with declaration already in scope}}
     }
   };
@@ -64,7 +64,7 @@ namespace test4 {
   template <typename> class Test1 {
     void test() {
       using ns::foo; //expected-note {{using declaration}}
-      int foo(); // expected-error {{declaration conflicts with target of using declaration already in scope}}
+      int foo(); // expected-error {{declaration conflicts with target of using declaration already in scope}} expected-warning{{function declaration}}
     }
   };
 }
@@ -91,4 +91,3 @@ namespace test5 {
   template class Test0<int>;
   template class Test1<int>; // expected-note {{in instantiation of member function}}
 }
-
index 5e05e0b..42c09cb 100644 (file)
@@ -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 (268):
+CHECK: Warnings without flags (267):
 CHECK-NEXT:   ext_anon_param_requires_type_specifier
 CHECK-NEXT:   ext_anonymous_struct_union_qualified
 CHECK-NEXT:   ext_array_init_copy
@@ -211,7 +211,6 @@ CHECK-NEXT:   warn_octal_escape_too_large
 CHECK-NEXT:   warn_odr_tag_type_inconsistent
 CHECK-NEXT:   warn_on_superclass_use
 CHECK-NEXT:   warn_param_default_argument_redefinition
-CHECK-NEXT:   warn_parens_disambiguated_as_function_decl
 CHECK-NEXT:   warn_partial_specs_not_deducible
 CHECK-NEXT:   warn_pointer_attribute_wrong_type
 CHECK-NEXT:   warn_pp_convert_lhs_to_positive
index 5648d02..3cfddb3 100644 (file)
@@ -96,8 +96,8 @@ void test()
   (void)(i1 ? BadDerived() : BadBase());
 
   // b2.1 (hierarchy stuff)
-  const Base constret();
-  const Derived constder();
+  const Base constret(); // expected-warning {{interpreted as a function declaration}}
+  const Derived constder(); // expected-warning {{interpreted as a function declaration}}
   // should use const overload
   A a1((i1 ? constret() : Base()).trick());
   A a2((i1 ? Base() : constret()).trick());
index 1ddff80..555e89c 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -pedantic-errors %s 
+// RUN: %clang_cc1 -fsyntax-only -verify -pedantic-errors %s
 
 void f() {
   int a;
@@ -24,10 +24,15 @@ void f() {
   // Declarations.
   int fd(T(a)); // expected-warning {{parentheses were disambiguated as a function declarator}}
   T(*d)(int(p)); // expected-warning {{parentheses were disambiguated as a function declarator}} expected-note {{previous definition is here}}
+  typedef T(*td)(int(p));
+  extern T(*tp)(int(p));
+  T d3(); // expected-warning {{empty parentheses interpreted as a function declaration}}
+  typedef T d3t();
+  extern T f3();
   T(d)[5]; // expected-error {{redefinition of 'd'}}
   typeof(int[])(f) = { 1, 2 }; // expected-error {{extension used}}
   void(b)(int);
-  int(d2) __attribute__(()); 
+  int(d2) __attribute__(());
   if (int(a)=1) {}
   int(d3(int()));
 }
index 81a5e2b..44bb4bd 100644 (file)
@@ -49,7 +49,7 @@ namespace PR7259 {
   int
   main (void)
   {
-    Final final();
+    Final final;
     return 0;
   }
 }