diff options
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 6 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 29 | ||||
-rw-r--r-- | test/SemaCXX/access.cpp | 15 |
3 files changed, 47 insertions, 3 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index fb194fe4c3..5dbc3d0ce8 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -234,6 +234,12 @@ def err_deleted_non_function : Error< def err_deleted_decl_not_first : Error< "deleted definition must be first declaration">; +// C++ access checking +def err_class_redeclared_with_different_access : Error< + "%0 redeclared with '%1' access">; +def note_previous_access_declaration : Note< + "previously declared '%1' here">; + // C++ name lookup def err_incomplete_nested_name_spec : Error< "incomplete type %0 named in nested name specifier">; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 88c08b0f75..d974301785 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3083,6 +3083,21 @@ TypedefDecl *Sema::ParseTypedefDecl(Scope *S, Declarator &D, QualType T, return NewTD; } +static const char *getAccessName(AccessSpecifier AS) { + switch (AS) { + default: + case AS_none: + assert("Invalid access specifier!"); + return 0; + case AS_public: + return "public"; + case AS_private: + return "private"; + case AS_protected: + return "protected"; + } +} + /// ActOnTag - This is invoked when we see 'struct foo' or 'struct {'. In the /// former case, Name will be non-null. In the later case, Name will be null. /// TagSpec indicates what kind of tag this is. TK indicates whether this is a @@ -3384,9 +3399,17 @@ CreateNewDecl: // lexical context will be different from the semantic context. New->setLexicalDeclContext(CurContext); - if (PrevDecl) - New->setAccess(PrevDecl->getAccess()); - else + if (PrevDecl) { + // C++ [class.access.spec]p3: When a member is redeclared its access + // specifier must be same as its initial declaration. + if (AS != AS_none && AS != PrevDecl->getAccess()) { + Diag(Loc, diag::err_class_redeclared_with_different_access) + << New << getAccessName(AS); + Diag(PrevDecl->getLocation(), diag::note_previous_access_declaration) + << PrevDecl << getAccessName(PrevDecl->getAccess()); + } else + New->setAccess(PrevDecl->getAccess()); + } else New->setAccess(AS); if (TK == TK_Definition) diff --git a/test/SemaCXX/access.cpp b/test/SemaCXX/access.cpp new file mode 100644 index 0000000000..d95781c1b7 --- /dev/null +++ b/test/SemaCXX/access.cpp @@ -0,0 +1,15 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +class C { + struct S; // expected-note {{previously declared 'private' here}} + +public: + struct S {}; // expected-error {{'S' redeclared with 'public' access}} +}; + +struct S { + class C; // expected-note {{previously declared 'public' here}} + +private: + class C { }; // expected-error {{'C' redeclared with 'private' access}} +};
\ No newline at end of file |