diff options
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 6 | ||||
-rw-r--r-- | include/clang/Sema/DeclSpec.h | 12 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 3 | ||||
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 5 | ||||
-rw-r--r-- | lib/Parse/ParseDeclCXX.cpp | 1 | ||||
-rw-r--r-- | lib/Parse/Parser.cpp | 5 | ||||
-rw-r--r-- | lib/Sema/AnalysisBasedWarnings.cpp | 39 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 35 | ||||
-rw-r--r-- | lib/Sema/SemaFixItUtils.cpp | 28 | ||||
-rw-r--r-- | test/CXX/basic/basic.link/p9.cpp | 2 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p11.cpp | 16 | ||||
-rw-r--r-- | test/FixIt/fixit-vexing-parse.cpp | 80 | ||||
-rw-r--r-- | test/SemaCXX/condition.cpp | 2 | ||||
-rw-r--r-- | test/SemaCXX/conditional-expr.cpp | 4 | ||||
-rw-r--r-- | test/SemaCXX/decl-expr-ambiguity.cpp | 20 |
15 files changed, 204 insertions, 54 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 4341dbbfa9..fbd8098a40 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -115,6 +115,12 @@ def warn_decl_in_param_list : Warning< def warn_empty_parens_are_function_decl : Warning< "empty parentheses interpreted as a function declaration">, InGroup<VexingParse>; +def note_empty_parens_function_call : Note< + "change this ',' to a ';' to call %0">; +def note_empty_parens_default_ctor : Note< + "remove parentheses to declare a variable">; +def note_empty_parens_zero_initialize : Note< + "replace parentheses with an initializer to declare a variable">; def warn_unused_function : Warning<"unused function %0">, InGroup<UnusedFunction>, DefaultIgnore; def warn_unused_member_function : Warning<"unused member function %0">, diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index a601210bf4..edc6fd3bc9 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -1468,6 +1468,10 @@ private: /// Extension - true if the declaration is preceded by __extension__. bool Extension : 1; + /// \brief If this is the second or subsequent declarator in this declaration, + /// the location of the comma before this declarator. + SourceLocation CommaLoc; + /// \brief If provided, the source location of the ellipsis used to describe /// this declarator as a parameter pack. SourceLocation EllipsisLoc; @@ -1557,6 +1561,8 @@ public: Attrs.clear(); AsmLabel = 0; InlineParamsUsed = false; + CommaLoc = SourceLocation(); + EllipsisLoc = SourceLocation(); } /// mayOmitIdentifier - Return true if the identifier is either optional or @@ -1846,7 +1852,11 @@ public: void setGroupingParens(bool flag) { GroupingParens = flag; } bool hasGroupingParens() const { return GroupingParens; } - + + bool isFirstDeclarator() const { return !CommaLoc.isValid(); } + SourceLocation getCommaLoc() const { return CommaLoc; } + void setCommaLoc(SourceLocation CL) { CommaLoc = CL; } + bool hasEllipsis() const { return EllipsisLoc.isValid(); } SourceLocation getEllipsisLoc() const { return EllipsisLoc; } void setEllipsisLoc(SourceLocation EL) { EllipsisLoc = EL; } diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index aa0a7ef957..d570a77936 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -756,6 +756,9 @@ public: bool findMacroSpelling(SourceLocation &loc, StringRef name); + /// \brief Get a string to suggest for zero-initialization of a type. + const char *getFixItZeroInitializerForType(QualType T) const; + ExprResult Owned(Expr* E) { return E; } ExprResult Owned(ExprResult R) { return R; } StmtResult Owned(Stmt* S) { return S; } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index be5d5ae54c..032326cea7 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1138,6 +1138,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, // Parse the next declarator. D.clear(); + D.setCommaLoc(CommaLoc); // Accept attributes in an init-declarator. In the first declarator in a // declaration, these would be part of the declspec. In subsequent @@ -2665,9 +2666,11 @@ ParseStructDeclaration(DeclSpec &DS, FieldCallback &Fields) { // Read struct-declarators until we find the semicolon. bool FirstDeclarator = true; + SourceLocation CommaLoc; while (1) { ParsingDeclRAIIObject PD(*this); FieldDeclarator DeclaratorInfo(DS); + DeclaratorInfo.D.setCommaLoc(CommaLoc); // Attributes are only allowed here on successive declarators. if (!FirstDeclarator) @@ -2703,7 +2706,7 @@ ParseStructDeclaration(DeclSpec &DS, FieldCallback &Fields) { return; // Consume the comma. - ConsumeToken(); + CommaLoc = ConsumeToken(); FirstDeclarator = false; } diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 285dbbbe07..c31c55ba5b 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -2044,6 +2044,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, BitfieldSize = true; Init = true; HasInitializer = false; + DeclaratorInfo.setCommaLoc(CommaLoc); // Attributes are only allowed on the second declarator. MaybeParseGNUAttributes(DeclaratorInfo); diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index d14b9adce2..e09ee79c8c 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -1067,11 +1067,12 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) { if (Tok.isNot(tok::comma)) break; + ParmDeclarator.clear(); + // Consume the comma. - ConsumeToken(); + ParmDeclarator.setCommaLoc(ConsumeToken()); // Parse the next declarator. - ParmDeclarator.clear(); ParseDeclarator(ParmDeclarator); } diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp index 3567b6bbe0..9b3191b027 100644 --- a/lib/Sema/AnalysisBasedWarnings.cpp +++ b/lib/Sema/AnalysisBasedWarnings.cpp @@ -415,42 +415,15 @@ static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) { return false; // Suggest possible initialization (if any). - const char *initialization = 0; QualType VariableTy = VD->getType().getCanonicalType(); - - if (VariableTy->isObjCObjectPointerType() || - VariableTy->isBlockPointerType()) { - // Check if 'nil' is defined. - if (S.PP.getMacroInfo(&S.getASTContext().Idents.get("nil"))) - initialization = " = nil"; - else - initialization = " = 0"; - } - else if (VariableTy->isRealFloatingType()) - initialization = " = 0.0"; - else if (VariableTy->isBooleanType() && S.Context.getLangOptions().CPlusPlus) - initialization = " = false"; - else if (VariableTy->isEnumeralType()) + const char *Init = S.getFixItZeroInitializerForType(VariableTy); + if (!Init) return false; - else if (VariableTy->isPointerType() || VariableTy->isMemberPointerType()) { - if (S.Context.getLangOptions().CPlusPlus0x) - initialization = " = nullptr"; - // Check if 'NULL' is defined. - else if (S.PP.getMacroInfo(&S.getASTContext().Idents.get("NULL"))) - initialization = " = NULL"; - else - initialization = " = 0"; - } - else if (VariableTy->isScalarType()) - initialization = " = 0"; - if (initialization) { - SourceLocation loc = S.PP.getLocForEndOfToken(VD->getLocEnd()); - S.Diag(loc, diag::note_var_fixit_add_initialization) << VD->getDeclName() - << FixItHint::CreateInsertion(loc, initialization); - return true; - } - return false; + SourceLocation Loc = S.PP.getLocForEndOfToken(VD->getLocEnd()); + S.Diag(Loc, diag::note_var_fixit_add_initialization) << VD->getDeclName() + << FixItHint::CreateInsertion(Loc, Init); + return true; } /// DiagnoseUninitializedUse -- Helper function for diagnosing uses of an diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index dc8d6ec8cf..4dcf1db2cf 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -4921,8 +4921,39 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, if (!T->isVoidType() && 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); + SourceRange ParenRange(C.Loc, C.EndLoc); + Diag(C.Loc, diag::warn_empty_parens_are_function_decl) << ParenRange; + + // If the declaration looks like: + // T var1, + // f(); + // and name lookup finds a function named 'f', then the ',' was + // probably intended to be a ';'. + if (!D.isFirstDeclarator() && D.getIdentifier()) { + FullSourceLoc Comma(D.getCommaLoc(), SourceMgr); + FullSourceLoc Name(D.getIdentifierLoc(), SourceMgr); + if (Comma.getFileID() != Name.getFileID() || + Comma.getSpellingLineNumber() != Name.getSpellingLineNumber()) { + LookupResult Result(*this, D.getIdentifier(), SourceLocation(), + LookupOrdinaryName); + if (LookupName(Result, S)) + Diag(D.getCommaLoc(), diag::note_empty_parens_function_call) + << FixItHint::CreateReplacement(D.getCommaLoc(), ";") << NewFD; + } + } + const CXXRecordDecl *RD = T->getAsCXXRecordDecl(); + // Empty parens mean value-initialization, and no parens mean default + // initialization. These are equivalent if the default constructor is + // user-provided, or if zero-initialization is a no-op. + if (RD && (RD->isEmpty() || RD->hasUserProvidedDefaultConstructor())) + Diag(C.Loc, diag::note_empty_parens_default_ctor) + << FixItHint::CreateRemoval(ParenRange); + else if (const char *Init = getFixItZeroInitializerForType(T)) + Diag(C.Loc, diag::note_empty_parens_zero_initialize) + << FixItHint::CreateReplacement(ParenRange, Init); + else if (LangOpts.CPlusPlus0x) + Diag(C.Loc, diag::note_empty_parens_zero_initialize) + << FixItHint::CreateReplacement(ParenRange, "{}"); } } diff --git a/lib/Sema/SemaFixItUtils.cpp b/lib/Sema/SemaFixItUtils.cpp index 8e8a46da73..1f17a9e83e 100644 --- a/lib/Sema/SemaFixItUtils.cpp +++ b/lib/Sema/SemaFixItUtils.cpp @@ -158,3 +158,31 @@ bool ConversionFixItGenerator::tryToFixConversion(const Expr *FullExpr, return false; } + +const char *Sema::getFixItZeroInitializerForType(QualType T) const { + // Suggest 'nil' if it's defined and appropriate. + if ((T->isObjCObjectPointerType() || T->isBlockPointerType()) && + PP.getMacroInfo(&getASTContext().Idents.get("nil"))) + return " = nil"; + if (T->isRealFloatingType()) + return " = 0.0"; + if (T->isBooleanType() && LangOpts.CPlusPlus) + return " = false"; + if (T->isPointerType() || T->isMemberPointerType()) { + if (LangOpts.CPlusPlus0x) + return " = nullptr"; + // Check if 'NULL' is defined. + else if (PP.getMacroInfo(&getASTContext().Idents.get("NULL"))) + return " = NULL"; + } + if (T->isEnumeralType()) + return 0; + if (T->isScalarType()) + return " = 0"; + const CXXRecordDecl *RD = T->getAsCXXRecordDecl(); + if (LangOpts.CPlusPlus0x && RD && !RD->hasUserProvidedDefaultConstructor()) + return "{}"; + if (T->isAggregateType()) + return " = {}"; + return 0; +} diff --git a/test/CXX/basic/basic.link/p9.cpp b/test/CXX/basic/basic.link/p9.cpp index c895253c68..680c93db2e 100644 --- a/test/CXX/basic/basic.link/p9.cpp +++ b/test/CXX/basic/basic.link/p9.cpp @@ -6,5 +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}} expected-warning{{interpreted as a function declaration}} + int N(); // expected-error{{redefinition}} expected-warning{{interpreted as a function declaration}} expected-note {{replace parentheses with an initializer}} } diff --git a/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p11.cpp b/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p11.cpp index b1dcf4d015..99903b57bf 100644 --- a/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p11.cpp +++ b/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p11.cpp @@ -12,26 +12,26 @@ namespace test0 { namespace ns { void foo(); } // expected-note {{target of using declaration}} - int foo(); // expected-note {{conflicting declaration}} + int foo(void); // expected-note {{conflicting declaration}} using ns::foo; // expected-error {{target of using declaration conflicts with declaration already in scope}} } namespace test1 { namespace ns { void foo(); } // expected-note {{target of using declaration}} using ns::foo; //expected-note {{using declaration}} - int foo(); // expected-error {{declaration conflicts with target of using declaration already in scope}} + int foo(void); // expected-error {{declaration conflicts with target of using declaration already in scope}} } namespace test2 { namespace ns { void foo(); } // expected-note 2 {{target of using declaration}} void test0() { - int foo(); // expected-note {{conflicting declaration}} expected-warning{{function declaration}} + int foo(void); // expected-note {{conflicting 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}} expected-warning{{function declaration}} + int foo(void); // expected-error {{declaration conflicts with target of using declaration already in scope}} } } @@ -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}} expected-warning{{function declaration}} + int foo(void); // expected-note {{conflicting 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}} expected-warning{{function declaration}} + int foo(void); // expected-error {{declaration conflicts with target of using declaration already in scope}} } }; } @@ -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}} expected-warning{{function declaration}} + int foo(void); // expected-note {{conflicting 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}} expected-warning{{function declaration}} + int foo(void); // expected-error {{declaration conflicts with target of using declaration already in scope}} } }; } diff --git a/test/FixIt/fixit-vexing-parse.cpp b/test/FixIt/fixit-vexing-parse.cpp new file mode 100644 index 0000000000..0f0505f983 --- /dev/null +++ b/test/FixIt/fixit-vexing-parse.cpp @@ -0,0 +1,80 @@ +// RUN: %clang_cc1 -verify -x c++ %s +// RUN: %clang_cc1 -fdiagnostics-parseable-fixits -x c++ %s 2>&1 | FileCheck %s + +struct S { + int n; +}; + +struct T { + T(); + int n; +}; + +struct U { + ~U(); + int n; +}; + +struct V { + ~V(); +}; + +struct W : V { +}; + +struct X : U { +}; + +int F1(); +S F2(); + +namespace N { + void test() { + // CHECK: fix-it:"{{.*}}":{34:9-34:11}:" = {}" + S s1(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} + + // CHECK: fix-it:"{{.*}}":{38:9-38:10}:";" + // CHECK: fix-it:"{{.*}}":{39:7-39:9}:" = {}" + S s2, // expected-note {{change this ',' to a ';' to call 'F2'}} + F2(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} + + // CHECK: fix-it:"{{.*}}":{43:9-43:11}:"" + // CHECK: fix-it:"{{.*}}":{44:9-44:11}:"" + T t1(), // expected-warning {{function declaration}} expected-note {{remove parentheses}} + t2(); // expected-warning {{function declaration}} expected-note {{remove parentheses}} + + // CHECK: fix-it:"{{.*}}":{47:8-47:10}:" = {}" + U u(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} + + // CHECK: fix-it:"{{.*}}":{50:8-50:10}:"" + V v(); // expected-warning {{function declaration}} expected-note {{remove parentheses}} + + // CHECK: fix-it:"{{.*}}":{53:8-53:10}:"" + W w(); // expected-warning {{function declaration}} expected-note {{remove parentheses}} + + // TODO: Removing the parens here would not initialize U::n. + // Maybe suggest an " = X()" initializer for this case? + // Maybe suggest removing the parens anyway? + X x(); // expected-warning {{function declaration}} + + // CHECK: fix-it:"{{.*}}":{61:11-61:13}:" = 0" + int n1(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} + + // CHECK: fix-it:"{{.*}}":{65:11-65:12}:";" + // CHECK: fix-it:"{{.*}}":{66:7-66:9}:" = 0" + int n2, // expected-note {{change this ',' to a ';' to call 'F1'}} + F1(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} + + // CHECK: fix-it:"{{.*}}":{69:13-69:15}:" = 0.0" + double d(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} + + typedef void *Ptr; + + // CHECK: fix-it:"{{.*}}":{74:10-74:12}:" = 0" + Ptr p(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} + +#define NULL 0 + // CHECK: fix-it:"{{.*}}":{78:10-78:12}:" = NULL" + Ptr p(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} + } +} diff --git a/test/SemaCXX/condition.cpp b/test/SemaCXX/condition.cpp index 3d17dc3937..43ee640e0f 100644 --- a/test/SemaCXX/condition.cpp +++ b/test/SemaCXX/condition.cpp @@ -7,7 +7,7 @@ void test() { typedef int arr[10]; while (arr x=0) ; // expected-error {{an array type is not allowed here}} expected-error {{array initializer must be an initializer list}} - while (int f()=0) ; // expected-warning {{interpreted as a function declaration}} expected-error {{a function type is not allowed here}} + while (int f()=0) ; // expected-warning {{interpreted as a function declaration}} expected-note {{initializer}} expected-error {{a function type is not allowed here}} struct S {} s; if (s) ++x; // expected-error {{value of type 'struct S' is not contextually convertible to 'bool'}} diff --git a/test/SemaCXX/conditional-expr.cpp b/test/SemaCXX/conditional-expr.cpp index 3cfddb3a41..4aee913277 100644 --- a/test/SemaCXX/conditional-expr.cpp +++ b/test/SemaCXX/conditional-expr.cpp @@ -96,8 +96,8 @@ void test() (void)(i1 ? BadDerived() : BadBase()); // b2.1 (hierarchy stuff) - const Base constret(); // expected-warning {{interpreted as a function declaration}} - const Derived constder(); // expected-warning {{interpreted as a function declaration}} + extern const Base constret(); + extern const Derived constder(); // should use const overload A a1((i1 ? constret() : Base()).trick()); A a2((i1 ? Base() : constret()).trick()); diff --git a/test/SemaCXX/decl-expr-ambiguity.cpp b/test/SemaCXX/decl-expr-ambiguity.cpp index 3a946c0ffb..f6dbe2f339 100644 --- a/test/SemaCXX/decl-expr-ambiguity.cpp +++ b/test/SemaCXX/decl-expr-ambiguity.cpp @@ -26,15 +26,15 @@ void f() { 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}} + T d3(); // expected-warning {{empty parentheses interpreted as a function declaration}} expected-note {{replace parentheses with an initializer}} T d3v(void); typedef T d3t(); extern T f3(); - __typeof(*T()) f4(); // expected-warning {{empty parentheses interpreted as a function declaration}} + __typeof(*T()) f4(); // expected-warning {{empty parentheses interpreted as a function declaration}} expected-note {{replace parentheses with an initializer}} typedef void *V; __typeof(*V()) f5(); T multi1, - multi2(); // expected-warning {{empty parentheses interpreted as a function declaration}} + multi2(); // expected-warning {{empty parentheses interpreted as a function declaration}} expected-note {{replace parentheses with an initializer}} T(d)[5]; // expected-error {{redefinition of 'd'}} typeof(int[])(f) = { 1, 2 }; // expected-error {{extension used}} void(b)(int); @@ -43,6 +43,20 @@ void f() { int(d3(int())); } +struct RAII { + RAII(); + ~RAII(); +}; + +void func(); +namespace N { + void emptyParens() { + RAII raii(); // expected-warning {{function declaration}} expected-note {{remove parentheses to declare a variable}} + int a, b, c, d, e, // expected-note {{change this ',' to a ';' to call 'func'}} + func(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}} + } +} + class C { }; void fn(int(C)) { } // void fn(int(*fp)(C c)) { } expected-note{{candidate function}} // not: void fn(int C); |