зеркало из https://github.com/microsoft/clang-1.git
Add -Wstatic-local-in-inline, which warns about using a static local
variable in a C99 inline (but not static-inline or extern-inline) function definition. The standard doesn't actually say that this doesn't apply to "extern inline" definitions, but that seems like a useful extension, and it at least doesn't have the obvious flaw that a static mutable variable in an externally-available definition does. rdar://13535367 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@178520 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
658a28479d
Коммит
b421d926cd
|
@ -218,6 +218,7 @@ def SizeofArrayArgument : DiagGroup<"sizeof-array-argument">;
|
|||
def SizeofArrayDecay : DiagGroup<"sizeof-array-decay">;
|
||||
def SizeofPointerMemaccess : DiagGroup<"sizeof-pointer-memaccess">;
|
||||
def StaticInInline : DiagGroup<"static-in-inline">;
|
||||
def StaticLocalInInline : DiagGroup<"static-local-in-inline">;
|
||||
def GNUStaticFloatInit : DiagGroup<"gnu-static-float-init">;
|
||||
def StaticFloatInit : DiagGroup<"static-float-init", [GNUStaticFloatInit]>;
|
||||
def StringPlusInt : DiagGroup<"string-plus-int">;
|
||||
|
|
|
@ -3328,6 +3328,9 @@ def warn_internal_in_extern_inline : ExtWarn<
|
|||
def ext_internal_in_extern_inline : Extension<
|
||||
"static %select{function|variable}0 %1 is used in an inline function with "
|
||||
"external linkage">, InGroup<StaticInInline>;
|
||||
def warn_static_local_in_extern_inline : Warning<
|
||||
"non-constant static local variable in inline function may be different "
|
||||
"in different files">, InGroup<StaticLocalInInline>;
|
||||
def note_convert_inline_to_static : Note<
|
||||
"use 'static' to give inline function %0 internal linkage">;
|
||||
def note_internal_decl_declared_here : Note<
|
||||
|
|
|
@ -1375,6 +1375,7 @@ public:
|
|||
// Returns true if the variable declaration is a redeclaration
|
||||
bool CheckVariableDeclaration(VarDecl *NewVD, LookupResult &Previous);
|
||||
void CheckCompleteVariableDeclaration(VarDecl *var);
|
||||
void MaybeSuggestAddingStaticToDecl(const FunctionDecl *D);
|
||||
void ActOnStartFunctionDeclarator();
|
||||
void ActOnEndFunctionDeclarator();
|
||||
NamedDecl* ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
|
||||
|
|
|
@ -4644,6 +4644,38 @@ static void checkAttributesAfterMerging(Sema &S, NamedDecl &ND) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Given that we are within the definition of the given function,
|
||||
/// will that definition behave like C99's 'inline', where the
|
||||
/// definition is discarded except for optimization purposes?
|
||||
static bool isFunctionDefinitionDiscarded(Sema &S, FunctionDecl *FD) {
|
||||
// Try to avoid calling GetGVALinkageForFunction.
|
||||
|
||||
// All cases of this require the 'inline' keyword.
|
||||
if (!FD->isInlined()) return false;
|
||||
|
||||
// This is only possible in C++ with the gnu_inline attribute.
|
||||
if (S.getLangOpts().CPlusPlus && !FD->hasAttr<GNUInlineAttr>())
|
||||
return false;
|
||||
|
||||
// Okay, go ahead and call the relatively-more-expensive function.
|
||||
|
||||
#ifndef NDEBUG
|
||||
// AST quite reasonably asserts that it's working on a function
|
||||
// definition. We don't really have a way to tell it that we're
|
||||
// currently defining the function, so just lie to it in +Asserts
|
||||
// builds. This is an awful hack.
|
||||
FD->setLazyBody(1);
|
||||
#endif
|
||||
|
||||
bool isC99Inline = (S.Context.GetGVALinkageForFunction(FD) == GVA_C99Inline);
|
||||
|
||||
#ifndef NDEBUG
|
||||
FD->setLazyBody(0);
|
||||
#endif
|
||||
|
||||
return isC99Inline;
|
||||
}
|
||||
|
||||
static bool shouldConsiderLinkage(const VarDecl *VD) {
|
||||
const DeclContext *DC = VD->getDeclContext()->getRedeclContext();
|
||||
if (DC->isFunctionOrMethod())
|
||||
|
@ -4693,6 +4725,7 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
|
|||
D.setInvalidType();
|
||||
SC = SC_None;
|
||||
}
|
||||
|
||||
SCSpec = D.getDeclSpec().getStorageClassSpecAsWritten();
|
||||
VarDecl::StorageClass SCAsWritten
|
||||
= StorageClassSpecToVarDeclStorageClass(SCSpec);
|
||||
|
@ -4865,6 +4898,25 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
|
|||
NewVD->setThreadSpecified(true);
|
||||
}
|
||||
|
||||
// C99 6.7.4p3
|
||||
// An inline definition of a function with external linkage shall
|
||||
// not contain a definition of a modifiable object with static or
|
||||
// thread storage duration...
|
||||
// We only apply this when the function is required to be defined
|
||||
// elsewhere, i.e. when the function is not 'extern inline'. Note
|
||||
// that a local variable with thread storage duration still has to
|
||||
// be marked 'static'. Also note that it's possible to get these
|
||||
// semantics in C++ using __attribute__((gnu_inline)).
|
||||
if (SC == SC_Static && S->getFnParent() != 0 &&
|
||||
!NewVD->getType().isConstQualified()) {
|
||||
FunctionDecl *CurFD = getCurFunctionDecl();
|
||||
if (CurFD && isFunctionDefinitionDiscarded(*this, CurFD)) {
|
||||
Diag(D.getDeclSpec().getStorageClassSpecLoc(),
|
||||
diag::warn_static_local_in_extern_inline);
|
||||
MaybeSuggestAddingStaticToDecl(CurFD);
|
||||
}
|
||||
}
|
||||
|
||||
if (D.getDeclSpec().isModulePrivateSpecified()) {
|
||||
if (isExplicitSpecialization)
|
||||
Diag(NewVD->getLocation(), diag::err_module_private_specialization)
|
||||
|
|
|
@ -214,19 +214,24 @@ static void diagnoseUseOfInternalDeclInInlineFunction(Sema &S,
|
|||
: diag::warn_internal_in_extern_inline)
|
||||
<< /*IsVar=*/!UsedFn << D;
|
||||
|
||||
// Suggest "static" on the inline function, if possible.
|
||||
if (!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.MaybeSuggestAddingStaticToDecl(Current);
|
||||
|
||||
S.Diag(D->getCanonicalDecl()->getLocation(),
|
||||
diag::note_internal_decl_declared_here)
|
||||
<< D;
|
||||
}
|
||||
|
||||
void Sema::MaybeSuggestAddingStaticToDecl(const FunctionDecl *Cur) {
|
||||
const FunctionDecl *First = Cur->getFirstDeclaration();
|
||||
|
||||
// Suggest "static" on the function, if possible.
|
||||
if (!hasAnyExplicitStorageClass(First)) {
|
||||
SourceLocation DeclBegin = First->getSourceRange().getBegin();
|
||||
Diag(DeclBegin, diag::note_convert_inline_to_static)
|
||||
<< Cur << FixItHint::CreateInsertion(DeclBegin, "static ");
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Determine whether the use of this declaration is valid, and
|
||||
/// emit any corresponding diagnostics.
|
||||
///
|
||||
|
|
|
@ -73,6 +73,16 @@ inline int useStaticAgain () { // expected-note 2 {{use 'static' to give inline
|
|||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
inline void defineStaticVar() { // expected-note {{use 'static' to give inline function 'defineStaticVar' internal linkage}}
|
||||
static const int x = 0; // ok
|
||||
static int y = 0; // expected-warning {{non-constant static local variable in inline function may be different in different files}}
|
||||
}
|
||||
|
||||
extern inline void defineStaticVarInExtern() {
|
||||
static const int x = 0; // ok
|
||||
static int y = 0; // ok
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче