diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index c862c090c50a8..cbc71b3509e70 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -8248,7 +8248,9 @@ NamedDecl *Sema::ActOnVariableDeclarator( if (isDeclExternC(NewVD)) { NewVD->addAttr(I->second); ExtnameUndeclaredIdentifiers.erase(I); - } else + } else if (NewVD->getDeclContext() + ->getRedeclContext() + ->isTranslationUnit()) Diag(NewVD->getLocation(), diag::warn_redefine_extname_not_applied) << /*Variable*/ 1 << NewVD; } @@ -10551,7 +10553,9 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, if (isDeclExternC(NewFD)) { NewFD->addAttr(I->second); ExtnameUndeclaredIdentifiers.erase(I); - } else + } else if (NewFD->getDeclContext() + ->getRedeclContext() + ->isTranslationUnit()) Diag(NewFD->getLocation(), diag::warn_redefine_extname_not_applied) << /*Variable*/0 << NewFD; } diff --git a/clang/test/Sema/redefine_extname.cpp b/clang/test/Sema/redefine_extname.cpp new file mode 100644 index 0000000000000..c60ed202f076e --- /dev/null +++ b/clang/test/Sema/redefine_extname.cpp @@ -0,0 +1,108 @@ +// RUN: %clang_cc1 -triple=x86_64-unknown-linux -Wpragmas -verify -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK +// RUN: %clang_cc1 -triple=x86_64-unknown-linux -Wpragmas -verify -emit-llvm -o - %s | FileCheck %s --check-prefix=CONTAINS + +/// Ensure nothing ever creates any identifiers with check_not_ in them. +// CONTAINS-NOT: check_not_ + +/// Check that C functions are affected normally. +#pragma redefine_extname check_not_foo_cfunc bar_cfunc +#pragma redefine_extname check_not_foo_cvar bar_cvar +extern "C" int check_not_foo_cfunc() { return 1; } +// CHECK-DAG: @bar_cfunc +extern "C" int check_not_foo_cvar = 1; +// CHECK-DAG: @bar_cvar + +/// Check that there is a warning for C++ functions (which are not affected). +#pragma redefine_extname foo_cppfunc check_not_bar_cppfunc +int foo_cppfunc() { return 1; } // expected-warning {{#pragma redefine_extname is applicable to external C declarations only; not applied to function 'foo_cppfunc'}} +// CHECK-DAG: {{@[^ ]*foo_cppfunc}} + +/// Check that there is a warning for C++ variables (which are not affected). +#pragma redefine_extname foo_cppvar check_not_bar_cppvar +int foo_cppvar = 1; // expected-warning {{#pragma redefine_extname is applicable to external C declarations only; not applied to variable 'foo_cppvar'}} +// CHECK-DAG: {{@[^ ]*foo_cppvar}} + +/// Check that the warning goes away when doing it in a namespace. +/// Such uses are clearly scoped and need no warning (and often can be intentional). +#pragma redefine_extname foo_nsfunc check_not_bar_nsfunc +#pragma redefine_extname foo_nsvar check_not_bar_nsvar +namespace ns { +int foo_nsfunc() { return 1; } +// CHECK-DAG: {{@[^ ]*foo_nsfunc}} +int foo_nsvar = 1; +// CHECK-DAG: {{@[^ ]*foo_nsvar}} +} + +/// Check that the warning goes away when doing it in a class. +/// Such uses are clearly scoped and need no warning (and often can be intentional). +#pragma redefine_extname foo_classmethod check_not_bar_classmethod +#pragma redefine_extname foo_staticmethod check_not_bar_staticmethod +#pragma redefine_extname foo_classmember check_not_bar_classmember +#pragma redefine_extname foo_staticmember check_not_bar_staticmember +class C { +public: + int foo_classmethod(); + // CHECK-DAG: {{@[^ ]*foo_classmethod}} + static int foo_staticmethod(); + // CHECK-DAG: {{@[^ ]*foo_staticmethod}} + int foo_classmember = 1; + // CHECK-DAG: {{%[^ ]*foo_classmember}} + static int foo_staticmember; + // CHECK-DAG: {{@[^ ]*foo_staticmember}} +}; +int C::foo_classmethod() { return 1; } +int C::foo_staticmethod() { return 1; } +int C::foo_staticmember = 1; + +// Force C to be actually instantiated. Emits a reference to foo_classmember. +void instantiate_C(C *p) { p = new C; } + +/// Check that the warning remains when doing it in an extern "C++" block. +/// Such blocks do not affect scope. +extern "C++" { +#pragma redefine_extname foo_extcppfunc check_not_bar_extcppfunc +#pragma redefine_extname foo_extcppvar check_not_bar_extcppvar +int foo_extcppfunc() { return 1; } // expected-warning {{#pragma redefine_extname is applicable to external C declarations only; not applied to function 'foo_extcppfunc'}} +// CHECK-DAG: {{@[^ ]*foo_extcppfunc}} +int foo_extcppvar = 1; // expected-warning {{#pragma redefine_extname is applicable to external C declarations only; not applied to variable 'foo_extcppvar'}} +// CHECK-DAG: {{@[^ ]*foo_extcppvar}} +} + +/// Check that the warning remains when doing it in C++ class friends. +/// These are actually in global scope. +#pragma redefine_extname foo_friendcppfunc check_not_bar_friendcppfunc +class F { +public: + friend int foo_friendcppfunc(F f) { return 1; } // expected-warning {{#pragma redefine_extname is applicable to external C declarations only; not applied to function 'foo_friendcppfunc'}} + // CHECK-DAG: {{@[^ ]*foo_friendcppfunc}} +}; +// Force foo_friendcppfunc to be actually instantiated. +int instantiate_friendcppfunc() { return foo_friendcppfunc(F{}); } + +/// Check that extern "C" friends can be renamed. +#pragma redefine_extname check_not_foo_friendcfunc bar_friendcfunc +extern "C" { +class CF { +public: + friend int check_not_foo_friendcfunc(CF cf) { return 1; } + // CHECK-DAG: @bar_friendcfunc +}; +// Force foo_friendcfunc to be actually instantiated. +int instantiate_friendcfunc() { return check_not_foo_friendcfunc(CF{}); } +} + +#pragma redefine_extname foo_arg check_not_bar_arg +#pragma redefine_extname foo_local check_not_bar_local +#pragma redefine_extname foo_staticlocal check_not_bar_staticlocal +int func(int foo_arg = 1) { + // CHECK-DAG: %foo_arg + int foo_local = 2; + // CHECK-DAG: %foo_local + static int foo_staticlocal = 3; + // CHECK-DAG: {{@[^ ]*foo_staticlocal}} + /// A hard enough nonsense computation to force foo_local to exist. + for (int i = 0; i < foo_arg; ++i) { + foo_local += foo_staticlocal++; + } + return foo_arg + foo_local + foo_staticlocal; +}