Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -1583,3 +1583,6 @@ def ExtractAPIMisuse : DiagGroup<"extractapi-misuse">;
// Warnings about using the non-standard extension having an explicit specialization
// with a storage class specifier.
def ExplicitSpecializationStorageClass : DiagGroup<"explicit-specialization-storage-class">;

// Warnings about extern functions not in header files.
def ExternalDeclaration : DiagGroup<"external-declaration">;
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -12686,4 +12686,10 @@ def err_acc_intervening_code
// AMDGCN builtins diagnostics
def err_amdgcn_global_load_lds_size_invalid_value : Error<"invalid size value">;
def note_amdgcn_global_load_lds_size_valid_value : Note<"size must be 1, 2, or 4">;

// SemaExternWarning diagnostics
def warn_extern_func_not_in_header : Warning<
"extern function '%0' declared in main file '%1' instead of a header">,
InGroup<ExternalDeclaration>, DefaultIgnore;

} // end of sema component.
9 changes: 9 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -4324,6 +4324,15 @@ class Sema final : public SemaBase {
// linkage or not.
static bool mightHaveNonExternalLinkage(const DeclaratorDecl *FD);

///@}
/// \name CheckExternFunction
///@{
public:
void CheckExternDecl(Decl *D);
void CheckDeferredExternDecls();

private:
std::vector<FunctionDecl *> ExternFuncDecls;
///@}

//
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,8 @@ void Sema::ActOnEndOfTranslationUnit() {
if (PP.isCodeCompletionEnabled())
return;

CheckDeferredExternDecls();

// Complete translation units and modules define vtables and perform implicit
// instantiations. PCH files do not.
if (TUKind != TU_Prefix) {
Expand Down
90 changes: 90 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6066,6 +6066,10 @@ Decl *Sema::ActOnDeclarator(Scope *S, Declarator &D) {
Dcl && Dcl->getDeclContext()->isFileContext())
Dcl->setTopLevelDeclInObjCContainer();

if (Dcl) {
CheckExternDecl(Dcl);
}

if (!Bases.empty())
OpenMP().ActOnFinishedFunctionDefinitionInOpenMPDeclareVariantScope(Dcl,
Bases);
Expand Down Expand Up @@ -20321,3 +20325,89 @@ bool Sema::shouldIgnoreInHostDeviceCheck(FunctionDecl *Callee) {
return LangOpts.CUDA && !LangOpts.CUDAIsDevice &&
CUDA().IdentifyTarget(Callee) == CUDAFunctionTarget::Global;
}

void Sema::diagnoseFunctionEffectMergeConflicts(
const FunctionEffectSet::Conflicts &Errs, SourceLocation NewLoc,
SourceLocation OldLoc) {
for (const FunctionEffectSet::Conflict &Conflict : Errs) {
Diag(NewLoc, diag::warn_conflicting_func_effects)
<< Conflict.Kept.description() << Conflict.Rejected.description();
Diag(OldLoc, diag::note_previous_declaration);
}
}

bool Sema::diagnoseConflictingFunctionEffect(
const FunctionEffectsRef &FX, const FunctionEffectWithCondition &NewEC,
SourceLocation NewAttrLoc) {
// If the new effect has a condition, we can't detect conflicts until the
// condition is resolved.
if (NewEC.Cond.getCondition() != nullptr)
return false;

// Diagnose the new attribute as incompatible with a previous one.
auto Incompatible = [&](const FunctionEffectWithCondition &PrevEC) {
Diag(NewAttrLoc, diag::err_attributes_are_not_compatible)
<< ("'" + NewEC.description() + "'")
<< ("'" + PrevEC.description() + "'") << false;
// We don't necessarily have the location of the previous attribute,
// so no note.
return true;
};

// Compare against previous attributes.
FunctionEffect::Kind NewKind = NewEC.Effect.kind();

for (const FunctionEffectWithCondition &PrevEC : FX) {
// Again, can't check yet when the effect is conditional.
if (PrevEC.Cond.getCondition() != nullptr)
continue;

FunctionEffect::Kind PrevKind = PrevEC.Effect.kind();
// Note that we allow PrevKind == NewKind; it's redundant and ignored.

if (PrevEC.Effect.oppositeKind() == NewKind)
return Incompatible(PrevEC);

// A new allocating is incompatible with a previous nonblocking.
if (PrevKind == FunctionEffect::Kind::NonBlocking &&
NewKind == FunctionEffect::Kind::Allocating)
return Incompatible(PrevEC);

// A new nonblocking is incompatible with a previous allocating.
if (PrevKind == FunctionEffect::Kind::Allocating &&
NewKind == FunctionEffect::Kind::NonBlocking)
return Incompatible(PrevEC);
}

return false;
}

void Sema::CheckExternDecl(Decl *D) {
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
SourceLocation Loc = FD->getLocation();
SourceManager &SM = Context.getSourceManager();

// Only consider extern function declarations (not definitions) in the main
// file
if (FD->isExternC() && !FD->isImplicit() && !FD->getBuiltinID() &&
!FD->hasBody() && !FD->isThisDeclarationADefinition() &&
FD->isFirstDecl() && SM.isInMainFile(Loc)) {
// Defer the warning check until the end of the translation unit
ExternFuncDecls.push_back(FD);
}
}
}

void Sema::CheckDeferredExternDecls() {
SourceManager &SM = Context.getSourceManager();
for (FunctionDecl *FD : ExternFuncDecls) {
// Check if there's a definition in the same file
const FunctionDecl *Definition = FD->getDefinition();
if (!Definition || !SM.isInMainFile(Definition->getLocation())) {
SourceLocation Loc = FD->getLocation();
Diag(Loc, diag::warn_extern_func_not_in_header)
<< FD->getName() << SM.getFilename(Loc);
}
}
ExternFuncDecls.clear();
}
13 changes: 13 additions & 0 deletions clang/test/Sema/warn-extern-func-not-in-header.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wexternal-declaration %s

extern int
foo(int); // expected-warning{{extern function 'foo' declared in main file}}

int bar(int);
int bar(int x) {
return x + 1;
}

int main() {
return foo(42) + bar(10);
}
Loading