Support -Winternal-linkage-in-inline in C++ code.
This includes treating anonymous namespaces like internal linkage, and allowing const variables to be used even if internal. The whole thing's been broken out into a separate function to avoid nested ifs. llvm-svn: 158683
This commit is contained in:
parent
2fa69ef934
commit
28cd12f265
|
@ -2997,17 +2997,19 @@ def warn_undefined_internal : Warning<
|
|||
def note_used_here : Note<"used here">;
|
||||
|
||||
def warn_internal_in_extern_inline : ExtWarn<
|
||||
"%select{function|variable}0 %1 has internal linkage but is used in an "
|
||||
"inline %select{function|method}2 with external linkage">,
|
||||
"%select{function|variable}0 %1 %select{has internal linkage|is in an anonymous"
|
||||
" namespace}2 but is used in an inline %select{function|method}3 with"
|
||||
" external linkage">,
|
||||
InGroup<DiagGroup<"internal-linkage-in-inline"> >;
|
||||
def ext_internal_in_extern_inline : Extension<
|
||||
"%select{function|variable}0 %1 has internal linkage but is used in an "
|
||||
"inline %select{function|method}2 with external linkage">,
|
||||
"%select{function|variable}0 %1 %select{has internal linkage|is in an anonymous"
|
||||
" namespace}2 but is used in an inline %select{function|method}3 with"
|
||||
" external linkage">,
|
||||
InGroup<DiagGroup<"internal-linkage-in-inline"> >;
|
||||
def note_internal_decl_declared_here : Note<
|
||||
"%0 declared here">;
|
||||
def note_convert_inline_to_static : Note<
|
||||
"use 'static' to give inline function %0 internal linkage">;
|
||||
def note_internal_decl_declared_here : Note<
|
||||
"%0 declared here">;
|
||||
|
||||
def warn_redefinition_of_typedef : ExtWarn<
|
||||
"redefinition of typedef %0 is a C11 feature">,
|
||||
|
|
|
@ -130,6 +130,103 @@ void Sema::NoteDeletedFunction(FunctionDecl *Decl) {
|
|||
<< 1 << Decl->isDeleted();
|
||||
}
|
||||
|
||||
/// \brief Determine whether a FunctionDecl was ever declared with an
|
||||
/// explicit storage class.
|
||||
static bool hasAnyExplicitStorageClass(const FunctionDecl *D) {
|
||||
for (FunctionDecl::redecl_iterator I = D->redecls_begin(),
|
||||
E = D->redecls_end();
|
||||
I != E; ++I) {
|
||||
if (I->getStorageClassAsWritten() != SC_None)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Check whether we're in an extern inline function and referring to a
|
||||
/// variable or function with internal linkage.
|
||||
///
|
||||
/// This also applies to anonymous-namespaced objects, which are effectively
|
||||
/// internal.
|
||||
/// This is only a warning because we used to silently accept this code, but
|
||||
/// most likely it will not do what the user intends.
|
||||
static void diagnoseUseOfInternalDeclInInlineFunction(Sema &S,
|
||||
const NamedDecl *D,
|
||||
SourceLocation Loc) {
|
||||
// C11 6.7.4p3: An inline definition of a function with external linkage...
|
||||
// shall not contain a reference to an identifier with internal linkage.
|
||||
// C++11 [basic.def.odr]p6: ...in each definition of D, corresponding names,
|
||||
// looked up according to 3.4, shall refer to an entity defined within the
|
||||
// definition of D, or shall refer to the same entity, after overload
|
||||
// resolution (13.3) and after matching of partial template specialization
|
||||
// (14.8.3), except that a name can refer to a const object with internal or
|
||||
// no linkage if the object has the same literal type in all definitions of
|
||||
// D, and the object is initialized with a constant expression (5.19), and
|
||||
// the value (but not the address) of the object is used, and the object has
|
||||
// the same value in all definitions of D; ...
|
||||
|
||||
// Check if this is an inlined function or method.
|
||||
FunctionDecl *Current = S.getCurFunctionDecl();
|
||||
if (!Current)
|
||||
return;
|
||||
if (!Current->isInlined())
|
||||
return;
|
||||
if (Current->getLinkage() != ExternalLinkage)
|
||||
return;
|
||||
|
||||
// Check if the decl has internal linkage.
|
||||
Linkage UsedLinkage = D->getLinkage();
|
||||
switch (UsedLinkage) {
|
||||
case NoLinkage:
|
||||
return;
|
||||
case InternalLinkage:
|
||||
case UniqueExternalLinkage:
|
||||
break;
|
||||
case ExternalLinkage:
|
||||
return;
|
||||
}
|
||||
|
||||
// Check C++'s exception for const variables. This is in the standard
|
||||
// because in C++ const variables have internal linkage unless
|
||||
// explicitly declared extern.
|
||||
// Note that we don't do any of the cross-TU checks, and this code isn't
|
||||
// even particularly careful about checking if the variable "has the
|
||||
// same value in all definitions" of the inline function. It just does a
|
||||
// sanity check to make sure there is an initializer at all.
|
||||
// FIXME: We should still be checking to see if we're using a constant
|
||||
// as a glvalue anywhere, but we don't have the necessary information to
|
||||
// do that here, and as long as this is a warning and not a hard error
|
||||
// it's not appropriate to change the semantics of the program (i.e.
|
||||
// by having BuildDeclRefExpr use VK_RValue for constants like these).
|
||||
const VarDecl *VD = dyn_cast<VarDecl>(D);
|
||||
if (VD && S.getLangOpts().CPlusPlus)
|
||||
if (VD->getType().isConstant(S.getASTContext()) && VD->getAnyInitializer())
|
||||
return;
|
||||
|
||||
// Don't warn unless -pedantic is on if the inline function is in the main
|
||||
// source file. These functions will most likely not be inlined into another
|
||||
// translation unit, so they're effectively internal.
|
||||
bool IsInMainFile = S.getSourceManager().isFromMainFile(Loc);
|
||||
S.Diag(Loc, IsInMainFile ? diag::ext_internal_in_extern_inline
|
||||
: diag::warn_internal_in_extern_inline)
|
||||
<< (bool)VD
|
||||
<< D
|
||||
<< (UsedLinkage == UniqueExternalLinkage)
|
||||
<< isa<CXXMethodDecl>(Current);
|
||||
|
||||
// Suggest "static" on the inline function, if possible.
|
||||
if (!isa<CXXMethodDecl>(Current) &&
|
||||
!hasAnyExplicitStorageClass(Current)) {
|
||||
const FunctionDecl *FirstDecl = Current->getCanonicalDecl();
|
||||
SourceLocation DeclBegin = FirstDecl->getSourceRange().getBegin();
|
||||
S.Diag(DeclBegin, diag::note_convert_inline_to_static)
|
||||
<< Current << FixItHint::CreateInsertion(DeclBegin, "static ");
|
||||
}
|
||||
|
||||
S.Diag(D->getCanonicalDecl()->getLocation(),
|
||||
diag::note_internal_decl_declared_here)
|
||||
<< D;
|
||||
}
|
||||
|
||||
/// \brief Determine whether the use of this declaration is valid, and
|
||||
/// emit any corresponding diagnostics.
|
||||
///
|
||||
|
@ -183,42 +280,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc,
|
|||
if (D->hasAttr<UnusedAttr>())
|
||||
Diag(Loc, diag::warn_used_but_marked_unused) << D->getDeclName();
|
||||
|
||||
// Warn if we're in an extern inline function referring to a decl
|
||||
// with internal linkage. (C99 6.7.4p3)
|
||||
// FIXME: This is not explicitly forbidden in C++, but it's not clear
|
||||
// what the correct behavior is. We should probably still have a warning.
|
||||
// (However, in C++ const variables have internal linkage by default, while
|
||||
// functions still have external linkage by default, so this warning becomes
|
||||
// very noisy.)
|
||||
if (!getLangOpts().CPlusPlus) {
|
||||
if (FunctionDecl *Current = getCurFunctionDecl()) {
|
||||
if (Current->isInlined() && Current->getLinkage() > InternalLinkage) {
|
||||
if (D->getLinkage() == InternalLinkage) {
|
||||
// We won't warn by default if the inline function is in the main
|
||||
// source file; in these cases it is almost certain that the inlining
|
||||
// will only occur in this file, even if there is an external
|
||||
// declaration as well.
|
||||
bool IsFromMainFile = getSourceManager().isFromMainFile(Loc);
|
||||
Diag(Loc, IsFromMainFile ? diag::ext_internal_in_extern_inline
|
||||
: diag::warn_internal_in_extern_inline)
|
||||
<< !isa<FunctionDecl>(D) << D << isa<CXXMethodDecl>(Current);
|
||||
|
||||
// If the user didn't explicitly specify a storage class,
|
||||
// suggest adding "static" to fix the problem.
|
||||
const FunctionDecl *FirstDecl = Current->getCanonicalDecl();
|
||||
if (FirstDecl->getStorageClassAsWritten() == SC_None) {
|
||||
SourceLocation DeclBegin = FirstDecl->getSourceRange().getBegin();
|
||||
Diag(DeclBegin, diag::note_convert_inline_to_static)
|
||||
<< Current << FixItHint::CreateInsertion(DeclBegin, "static ");
|
||||
}
|
||||
|
||||
Diag(D->getCanonicalDecl()->getLocation(),
|
||||
diag::note_internal_decl_declared_here)
|
||||
<< D;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
diagnoseUseOfInternalDeclInInlineFunction(*this, D, Loc);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify -x c++ %s
|
||||
|
||||
#if defined(INCLUDE)
|
||||
// -------
|
||||
|
@ -40,7 +41,7 @@ int d(inline int a); // expected-error{{'inline' can only appear on functions}}
|
|||
// Check that the warnings from the "header file" aren't on by default in
|
||||
// the main source file.
|
||||
|
||||
inline int useStaticMain () {
|
||||
inline int useStaticMainFile () {
|
||||
staticFunction(); // no-warning
|
||||
return staticVar; // no-warning
|
||||
}
|
||||
|
|
|
@ -1,5 +1,110 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
|
||||
#if defined(INCLUDE)
|
||||
// -------
|
||||
// This section acts like a header file.
|
||||
// -------
|
||||
|
||||
// Check the use of static variables in non-static inline functions.
|
||||
static int staticVar; // expected-note + {{'staticVar' declared here}}
|
||||
static int staticFunction(); // expected-note + {{'staticFunction' declared here}}
|
||||
const int constVar = 0; // no-warning
|
||||
|
||||
namespace {
|
||||
int anonVar; // expected-note + {{'anonVar' declared here}}
|
||||
int anonFunction(); // expected-note + {{'anonFunction' declared here}}
|
||||
const int anonConstVar = 0; // no-warning
|
||||
|
||||
class Anon {
|
||||
public:
|
||||
static int var; // expected-note + {{'var' declared here}}
|
||||
static const int constVar = 0; // no-warning
|
||||
};
|
||||
}
|
||||
|
||||
inline void useStatic() { // expected-note + {{use 'static' to give inline function 'useStatic' internal linkage}}
|
||||
staticFunction(); // expected-warning{{function 'staticFunction' has internal linkage but is used in an inline function with external linkage}}
|
||||
(void)staticVar; // expected-warning{{variable 'staticVar' has internal linkage but is used in an inline function with external linkage}}
|
||||
anonFunction(); // expected-warning{{function 'anonFunction' is in an anonymous namespace but is used in an inline function with external linkage}}
|
||||
(void)anonVar; // expected-warning{{variable 'anonVar' is in an anonymous namespace but is used in an inline function with external linkage}}
|
||||
(void)Anon::var; // expected-warning{{variable 'var' is in an anonymous namespace but is used in an inline function with external linkage}}
|
||||
|
||||
(void)constVar; // no-warning
|
||||
(void)anonConstVar; // no-warning
|
||||
(void)Anon::constVar; // no-warning
|
||||
}
|
||||
|
||||
extern inline int useStaticFromExtern() { // no suggestions
|
||||
staticFunction(); // expected-warning{{function 'staticFunction' has internal linkage but is used in an inline function with external linkage}}
|
||||
return staticVar; // expected-warning{{variable 'staticVar' has internal linkage but is used in an inline function with external linkage}}
|
||||
}
|
||||
|
||||
class A {
|
||||
public:
|
||||
static inline int useInClass() { // no suggestions
|
||||
return staticFunction(); // expected-warning{{function 'staticFunction' has internal linkage but is used in an inline method with external linkage}}
|
||||
}
|
||||
inline int useInInstance() { // no suggestions
|
||||
return staticFunction(); // expected-warning{{function 'staticFunction' has internal linkage but is used in an inline method with external linkage}}
|
||||
}
|
||||
};
|
||||
|
||||
static inline void useStaticFromStatic () {
|
||||
// No warnings.
|
||||
staticFunction();
|
||||
(void)staticVar;
|
||||
(void)constVar;
|
||||
anonFunction();
|
||||
(void)anonVar;
|
||||
(void)anonConstVar;
|
||||
(void)Anon::var;
|
||||
(void)Anon::constVar;
|
||||
}
|
||||
|
||||
namespace {
|
||||
inline void useStaticFromAnon() {
|
||||
// No warnings.
|
||||
staticFunction();
|
||||
(void)staticVar;
|
||||
(void)constVar;
|
||||
anonFunction();
|
||||
(void)anonVar;
|
||||
(void)anonConstVar;
|
||||
(void)Anon::var;
|
||||
(void)Anon::constVar;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
// -------
|
||||
// This is the main source file.
|
||||
// -------
|
||||
|
||||
#define INCLUDE
|
||||
#include "inline.cpp"
|
||||
|
||||
// Check that we don't allow illegal uses of inline
|
||||
// (checking C++-only constructs here)
|
||||
struct c {inline int a;}; // expected-error{{'inline' can only appear on functions}}
|
||||
|
||||
// Check that the warnings from the "header file" aren't on by default in
|
||||
// the main source file.
|
||||
|
||||
inline int useStaticMainFile () {
|
||||
anonFunction(); // no-warning
|
||||
return staticVar; // no-warning
|
||||
}
|
||||
|
||||
// Check that the warnings show up when explicitly requested.
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic warning "-Winternal-linkage-in-inline"
|
||||
|
||||
inline int useStaticAgain () { // expected-note 2 {{use 'static' to give inline function 'useStaticAgain' internal linkage}}
|
||||
anonFunction(); // expected-warning{{function 'anonFunction' is in an anonymous namespace but is used in an inline function with external linkage}}
|
||||
return staticVar; // expected-warning{{variable 'staticVar' has internal linkage but is used in an inline function with external linkage}}
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue