Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit d73ef13

Browse files
committed
Add pedantic warning -Wempty-translation-unit (C11 6.9p1).
In standard C since C89, a 'translation-unit' is syntactically defined to have at least one "external-declaration", which is either a decl or a function definition. In Clang the latter gives us a declaration as well. The tricky bit about this warning is that our predefines can contain external declarations (__builtin_va_list and the 128-bit integer types). Therefore our AST parser now makes sure we have at least one declaration that doesn't come from the predefines buffer. Also, remove bogus warning about empty source files. This doesn't catch source files that only contain comments, and never fired anyway because of our predefines. PR12665 and <rdar://problem/9165548> git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@158085 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent a08e7bc commit d73ef13

14 files changed

+108
-26
lines changed

include/clang/Basic/DiagnosticParseKinds.td

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ def warn_file_asm_volatile : Warning<
2020

2121
let CategoryName = "Parse Issue" in {
2222

23-
def ext_empty_source_file : Extension<"ISO C forbids an empty source file">;
23+
def ext_empty_translation_unit : Extension<
24+
"ISO C requires a translation unit to contain at least one declaration.">,
25+
InGroup<DiagGroup<"empty-translation-unit">>;
2426
def warn_cxx98_compat_top_level_semi : Warning<
2527
"extra ';' outside of a function is incompatible with C++98">,
2628
InGroup<CXX98CompatPedantic>, DefaultIgnore;

include/clang/Basic/SourceManager.h

+20
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,9 @@ class SourceManager : public RefCountedBase<SourceManager> {
584584
/// \brief The file ID for the precompiled preamble there is one.
585585
FileID PreambleFileID;
586586

587+
/// \brief The file ID for the preprocessor's predefines.
588+
FileID PredefinesFileID;
589+
587590
// Statistics for -print-stats.
588591
mutable unsigned NumLinearScans, NumBinaryProbes;
589592

@@ -628,6 +631,14 @@ class SourceManager : public RefCountedBase<SourceManager> {
628631
MainFileID = createFileIDForMemBuffer(Buffer);
629632
return MainFileID;
630633
}
634+
635+
/// \brief Create the FileID for a memory buffer that contains the
636+
/// preprocessor's predefines.
637+
FileID createPredefinesFileIDForMemBuffer(const llvm::MemoryBuffer *Buffer) {
638+
assert(PredefinesFileID.isInvalid() && "PredefinesFileID already set!");
639+
PredefinesFileID = createFileIDForMemBuffer(Buffer);
640+
return PredefinesFileID;
641+
}
631642

632643
//===--------------------------------------------------------------------===//
633644
// MainFileID creation and querying methods.
@@ -636,6 +647,9 @@ class SourceManager : public RefCountedBase<SourceManager> {
636647
/// getMainFileID - Returns the FileID of the main source file.
637648
FileID getMainFileID() const { return MainFileID; }
638649

650+
/// \brief Returns the FileID of the preprocessor predefines buffer.
651+
FileID getPredefinesFileID() const { return PredefinesFileID; }
652+
639653
/// createMainFileID - Create the FileID for the main source file.
640654
FileID createMainFileID(const FileEntry *SourceFile,
641655
SrcMgr::CharacteristicKind Kind = SrcMgr::C_User) {
@@ -1113,6 +1127,12 @@ class SourceManager : public RefCountedBase<SourceManager> {
11131127
return getFileID(Loc) == getMainFileID();
11141128
}
11151129

1130+
/// isFromPredefines - Returns true if the provided SourceLocation is
1131+
/// within the processor's predefines buffer.
1132+
bool isFromPredefines(SourceLocation Loc) const {
1133+
return getFileID(Loc) == getPredefinesFileID();
1134+
}
1135+
11161136
/// isInSystemHeader - Returns if a SourceLocation is in a system header.
11171137
bool isInSystemHeader(SourceLocation Loc) const {
11181138
return getFileCharacteristic(Loc) != SrcMgr::C_User;

include/clang/Parse/Parser.h

+3
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,9 @@ class Parser : public CodeCompletionHandler {
730730
public:
731731
DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID);
732732
DiagnosticBuilder Diag(const Token &Tok, unsigned DiagID);
733+
DiagnosticBuilder Diag(unsigned DiagID) {
734+
return Diag(Tok, DiagID);
735+
}
733736

734737
private:
735738
void SuggestParentheses(SourceLocation Loc, unsigned DK,

lib/Basic/SourceManager.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ SourceManager::~SourceManager() {
407407

408408
void SourceManager::clearIDTables() {
409409
MainFileID = FileID();
410+
PredefinesFileID = FileID();
410411
LocalSLocEntryTable.clear();
411412
LoadedSLocEntryTable.clear();
412413
SLocEntryLoaded.clear();

lib/Lex/Preprocessor.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ void Preprocessor::EnterMainSourceFile() {
425425
llvm::MemoryBuffer *SB =
426426
llvm::MemoryBuffer::getMemBufferCopy(Predefines, "<built-in>");
427427
assert(SB && "Cannot create predefined source buffer");
428-
FileID FID = SourceMgr.createFileIDForMemBuffer(SB);
428+
FileID FID = SourceMgr.createPredefinesFileIDForMemBuffer(SB);
429429
assert(!FID.isInvalid() && "Could not create FileID for predefines?");
430430

431431
// Start parsing the predefines.

lib/Parse/ParseAST.cpp

+39-15
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "clang/Parse/ParseAST.h"
15+
#include "clang/Parse/ParseDiagnostic.h"
1516
#include "clang/Sema/Sema.h"
1617
#include "clang/Sema/CodeCompleteConsumer.h"
1718
#include "clang/Sema/SemaConsumer.h"
@@ -77,27 +78,50 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
7778
S.getPreprocessor().EnterMainSourceFile();
7879
P.Initialize();
7980
S.Initialize();
80-
81-
if (ExternalASTSource *External = S.getASTContext().getExternalSource())
81+
82+
// C11 6.9p1 says translation units must have at least one top-level
83+
// declaration. C++ doesn't have this restriction. We also don't want to
84+
// complain if we have a precompiled header, although technically if the PCH
85+
// is empty we should still emit the (pedantic) diagnostic.
86+
bool WarnForEmptyTU = !S.getLangOpts().CPlusPlus;
87+
if (ExternalASTSource *External = S.getASTContext().getExternalSource()) {
8288
External->StartTranslationUnit(Consumer);
83-
84-
bool Abort = false;
89+
WarnForEmptyTU = false;
90+
}
91+
92+
// Clang's predefines contain top-level declarations for things like va_list,
93+
// making it hard to tell if the /user's/ translation unit has at least one
94+
// top-level declaration. So we parse cautiously, looking for a declaration
95+
// that doesn't come from our predefines.
96+
// Note that ParseTopLevelDecl returns 'true' at EOF.
97+
SourceManager &SM = S.getSourceManager();
8598
Parser::DeclGroupPtrTy ADecl;
86-
87-
while (!P.ParseTopLevelDecl(ADecl)) { // Not end of file.
88-
// If we got a null return and something *was* parsed, ignore it. This
89-
// is due to a top-level semicolon, an action override, or a parse error
90-
// skipping something.
99+
while (WarnForEmptyTU && !P.ParseTopLevelDecl(ADecl)) {
91100
if (ADecl) {
92-
if (!Consumer->HandleTopLevelDecl(ADecl.get())) {
93-
Abort = true;
94-
break;
101+
if (!Consumer->HandleTopLevelDecl(ADecl.get()))
102+
return;
103+
if (DeclGroupRef::iterator FirstDecl = ADecl.get().begin()) {
104+
SourceLocation DeclLoc = (*FirstDecl)->getLocation();
105+
WarnForEmptyTU = SM.isFromPredefines(DeclLoc);
95106
}
96107
}
97-
};
108+
}
98109

99-
if (Abort)
100-
return;
110+
// If we ended up seeing EOF before any top-level declarations, emit our
111+
// diagnostic. Otherwise, parse the rest of the file normally.
112+
if (WarnForEmptyTU) {
113+
P.Diag(diag::ext_empty_translation_unit);
114+
} else {
115+
while (!P.ParseTopLevelDecl(ADecl)) { // Not end of file.
116+
// If we got a null return and something *was* parsed, ignore it. This
117+
// is due to a top-level semicolon, an action override, or a parse error
118+
// skipping something.
119+
if (ADecl) {
120+
if (!Consumer->HandleTopLevelDecl(ADecl.get()))
121+
return;
122+
}
123+
};
124+
}
101125

102126
// Process any TopLevelDecls generated by #pragma weak.
103127
for (SmallVector<Decl*,2>::iterator

lib/Parse/Parser.cpp

-4
Original file line numberDiff line numberDiff line change
@@ -439,10 +439,6 @@ void Parser::Initialize() {
439439
// Prime the lexer look-ahead.
440440
ConsumeToken();
441441

442-
if (Tok.is(tok::eof) &&
443-
!getLangOpts().CPlusPlus) // Empty source file is an extension in C
444-
Diag(Tok, diag::ext_empty_source_file);
445-
446442
// Initialization for Objective-C context sensitive keywords recognition.
447443
// Referenced in Parser::ParseObjCTypeQualifierList.
448444
if (getLangOpts().ObjC1) {

test/Misc/warning-flags.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ This test serves two purposes:
1717

1818
The list of warnings below should NEVER grow. It should gradually shrink to 0.
1919

20-
CHECK: Warnings without flags (242):
20+
CHECK: Warnings without flags (241):
2121
CHECK-NEXT: ext_anonymous_struct_union_qualified
2222
CHECK-NEXT: ext_binary_literal
2323
CHECK-NEXT: ext_cast_fn_obj
@@ -26,7 +26,6 @@ CHECK-NEXT: ext_designated_init
2626
CHECK-NEXT: ext_duplicate_declspec
2727
CHECK-NEXT: ext_ellipsis_exception_spec
2828
CHECK-NEXT: ext_empty_fnmacro_arg
29-
CHECK-NEXT: ext_empty_source_file
3029
CHECK-NEXT: ext_enum_friend
3130
CHECK-NEXT: ext_enum_value_not_int
3231
CHECK-NEXT: ext_enumerator_list_comma

test/PCH/empty-with-headers.c

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %clang_cc1 -fsyntax-only -std=c99 -pedantic-errors %s
2+
// RUN: %clang_cc1 -fsyntax-only -std=c99 -emit-pch -o %t %s
3+
// RUN: %clang_cc1 -fsyntax-only -std=c99 -pedantic-errors -include-pch %t %s
4+
5+
// RUN: %clang_cc1 -fsyntax-only -std=c99 -pedantic-errors -DINCLUDED %s -verify
6+
// This last one should warn for -Wempty-translation-unit (C99 6.9p1).
7+
8+
#if defined(INCLUDED)
9+
10+
// empty except for the prefix header
11+
12+
#elif defined(HEADER)
13+
14+
typedef int my_int;
15+
#define INCLUDED
16+
17+
#else
18+
19+
#define HEADER
20+
#include "empty-with-headers.c"
21+
// empty except for the header
22+
23+
#endif
24+
25+
// This should only fire if the header is not included,
26+
// either explicitly or as a prefix header.
27+
// expected-error{{ISO C requires a translation unit to contain at least one declaration.}}

test/Parser/completely-empty-header-file.h

Whitespace-only changes.

test/Parser/empty-translation-unit.c

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %clang_cc1 -fsyntax-only -std=c99 -pedantic -W -verify %s
2+
// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++03 -pedantic-errors -W %s
3+
4+
#include "completely-empty-header-file.h"
5+
// no-warning -- an empty file is OK
6+
7+
#define A_MACRO_IS_NOT_GOOD_ENOUGH 1
8+
9+
// In C we should get this warning, but in C++ we shouldn't.
10+
// expected-warning{{ISO C requires a translation unit to contain at least one declaration.}}

test/Parser/opencl-pragma.cl

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 %s -verify -pedantic -fsyntax-only
1+
// RUN: %clang_cc1 %s -verify -pedantic -Wno-empty-translation-unit -fsyntax-only
22

33
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
44

test/Preprocessor/undef-error.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 %s -pedantic-errors -verify
1+
// RUN: %clang_cc1 %s -pedantic-errors -Wno-empty-translation-unit -verify
22
// PR2045
33

44
#define b

test/Sema/c89-2.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* RUN: %clang_cc1 %s -std=c89 -pedantic-errors -verify
1+
/* RUN: %clang_cc1 %s -std=c89 -pedantic-errors -Wno-empty-translation-unit -verify
22
*/
33

44
#if 1LL /* expected-error {{long long}} */

0 commit comments

Comments
 (0)