aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/Basic/DiagnosticParseKinds.td4
-rw-r--r--clang/include/clang/Basic/SourceManager.h20
-rw-r--r--clang/include/clang/Parse/Parser.h3
-rw-r--r--clang/lib/Basic/SourceManager.cpp1
-rw-r--r--clang/lib/Lex/Preprocessor.cpp2
-rw-r--r--clang/lib/Parse/ParseAST.cpp54
-rw-r--r--clang/lib/Parse/Parser.cpp4
-rw-r--r--clang/test/Misc/warning-flags.c3
-rw-r--r--clang/test/PCH/empty-with-headers.c27
-rw-r--r--clang/test/Parser/completely-empty-header-file.h0
-rw-r--r--clang/test/Parser/empty-translation-unit.c10
-rw-r--r--clang/test/Parser/opencl-pragma.cl2
-rw-r--r--clang/test/Preprocessor/undef-error.c2
-rw-r--r--clang/test/Sema/c89-2.c2
14 files changed, 108 insertions, 26 deletions
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index a5057c9..9811bab 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -20,7 +20,9 @@ def warn_file_asm_volatile : Warning<
let CategoryName = "Parse Issue" in {
-def ext_empty_source_file : Extension<"ISO C forbids an empty source file">;
+def ext_empty_translation_unit : Extension<
+ "ISO C requires a translation unit to contain at least one declaration.">,
+ InGroup<DiagGroup<"empty-translation-unit">>;
def warn_cxx98_compat_top_level_semi : Warning<
"extra ';' outside of a function is incompatible with C++98">,
InGroup<CXX98CompatPedantic>, DefaultIgnore;
diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h
index 3164f87..f7fb1f5 100644
--- a/clang/include/clang/Basic/SourceManager.h
+++ b/clang/include/clang/Basic/SourceManager.h
@@ -584,6 +584,9 @@ class SourceManager : public RefCountedBase<SourceManager> {
/// \brief The file ID for the precompiled preamble there is one.
FileID PreambleFileID;
+ /// \brief The file ID for the preprocessor's predefines.
+ FileID PredefinesFileID;
+
// Statistics for -print-stats.
mutable unsigned NumLinearScans, NumBinaryProbes;
@@ -628,6 +631,14 @@ public:
MainFileID = createFileIDForMemBuffer(Buffer);
return MainFileID;
}
+
+ /// \brief Create the FileID for a memory buffer that contains the
+ /// preprocessor's predefines.
+ FileID createPredefinesFileIDForMemBuffer(const llvm::MemoryBuffer *Buffer) {
+ assert(PredefinesFileID.isInvalid() && "PredefinesFileID already set!");
+ PredefinesFileID = createFileIDForMemBuffer(Buffer);
+ return PredefinesFileID;
+ }
//===--------------------------------------------------------------------===//
// MainFileID creation and querying methods.
@@ -636,6 +647,9 @@ public:
/// getMainFileID - Returns the FileID of the main source file.
FileID getMainFileID() const { return MainFileID; }
+ /// \brief Returns the FileID of the preprocessor predefines buffer.
+ FileID getPredefinesFileID() const { return PredefinesFileID; }
+
/// createMainFileID - Create the FileID for the main source file.
FileID createMainFileID(const FileEntry *SourceFile,
SrcMgr::CharacteristicKind Kind = SrcMgr::C_User) {
@@ -1113,6 +1127,12 @@ public:
return getFileID(Loc) == getMainFileID();
}
+ /// isFromPredefines - Returns true if the provided SourceLocation is
+ /// within the processor's predefines buffer.
+ bool isFromPredefines(SourceLocation Loc) const {
+ return getFileID(Loc) == getPredefinesFileID();
+ }
+
/// isInSystemHeader - Returns if a SourceLocation is in a system header.
bool isInSystemHeader(SourceLocation Loc) const {
return getFileCharacteristic(Loc) != SrcMgr::C_User;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 07d6320..2222e78 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -730,6 +730,9 @@ private:
public:
DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID);
DiagnosticBuilder Diag(const Token &Tok, unsigned DiagID);
+ DiagnosticBuilder Diag(unsigned DiagID) {
+ return Diag(Tok, DiagID);
+ }
private:
void SuggestParentheses(SourceLocation Loc, unsigned DK,
diff --git a/clang/lib/Basic/SourceManager.cpp b/clang/lib/Basic/SourceManager.cpp
index ed920eb..783e073 100644
--- a/clang/lib/Basic/SourceManager.cpp
+++ b/clang/lib/Basic/SourceManager.cpp
@@ -407,6 +407,7 @@ SourceManager::~SourceManager() {
void SourceManager::clearIDTables() {
MainFileID = FileID();
+ PredefinesFileID = FileID();
LocalSLocEntryTable.clear();
LoadedSLocEntryTable.clear();
SLocEntryLoaded.clear();
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index d387f43..955c39c 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -425,7 +425,7 @@ void Preprocessor::EnterMainSourceFile() {
llvm::MemoryBuffer *SB =
llvm::MemoryBuffer::getMemBufferCopy(Predefines, "<built-in>");
assert(SB && "Cannot create predefined source buffer");
- FileID FID = SourceMgr.createFileIDForMemBuffer(SB);
+ FileID FID = SourceMgr.createPredefinesFileIDForMemBuffer(SB);
assert(!FID.isInvalid() && "Could not create FileID for predefines?");
// Start parsing the predefines.
diff --git a/clang/lib/Parse/ParseAST.cpp b/clang/lib/Parse/ParseAST.cpp
index d1c2624..3f86c4d 100644
--- a/clang/lib/Parse/ParseAST.cpp
+++ b/clang/lib/Parse/ParseAST.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Parse/ParseAST.h"
+#include "clang/Parse/ParseDiagnostic.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/CodeCompleteConsumer.h"
#include "clang/Sema/SemaConsumer.h"
@@ -77,27 +78,50 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
S.getPreprocessor().EnterMainSourceFile();
P.Initialize();
S.Initialize();
-
- if (ExternalASTSource *External = S.getASTContext().getExternalSource())
+
+ // C11 6.9p1 says translation units must have at least one top-level
+ // declaration. C++ doesn't have this restriction. We also don't want to
+ // complain if we have a precompiled header, although technically if the PCH
+ // is empty we should still emit the (pedantic) diagnostic.
+ bool WarnForEmptyTU = !S.getLangOpts().CPlusPlus;
+ if (ExternalASTSource *External = S.getASTContext().getExternalSource()) {
External->StartTranslationUnit(Consumer);
-
- bool Abort = false;
+ WarnForEmptyTU = false;
+ }
+
+ // Clang's predefines contain top-level declarations for things like va_list,
+ // making it hard to tell if the /user's/ translation unit has at least one
+ // top-level declaration. So we parse cautiously, looking for a declaration
+ // that doesn't come from our predefines.
+ // Note that ParseTopLevelDecl returns 'true' at EOF.
+ SourceManager &SM = S.getSourceManager();
Parser::DeclGroupPtrTy ADecl;
-
- while (!P.ParseTopLevelDecl(ADecl)) { // Not end of file.
- // If we got a null return and something *was* parsed, ignore it. This
- // is due to a top-level semicolon, an action override, or a parse error
- // skipping something.
+ while (WarnForEmptyTU && !P.ParseTopLevelDecl(ADecl)) {
if (ADecl) {
- if (!Consumer->HandleTopLevelDecl(ADecl.get())) {
- Abort = true;
- break;
+ if (!Consumer->HandleTopLevelDecl(ADecl.get()))
+ return;
+ if (DeclGroupRef::iterator FirstDecl = ADecl.get().begin()) {
+ SourceLocation DeclLoc = (*FirstDecl)->getLocation();
+ WarnForEmptyTU = SM.isFromPredefines(DeclLoc);
}
}
- };
+ }
- if (Abort)
- return;
+ // If we ended up seeing EOF before any top-level declarations, emit our
+ // diagnostic. Otherwise, parse the rest of the file normally.
+ if (WarnForEmptyTU) {
+ P.Diag(diag::ext_empty_translation_unit);
+ } else {
+ while (!P.ParseTopLevelDecl(ADecl)) { // Not end of file.
+ // If we got a null return and something *was* parsed, ignore it. This
+ // is due to a top-level semicolon, an action override, or a parse error
+ // skipping something.
+ if (ADecl) {
+ if (!Consumer->HandleTopLevelDecl(ADecl.get()))
+ return;
+ }
+ };
+ }
// Process any TopLevelDecls generated by #pragma weak.
for (SmallVector<Decl*,2>::iterator
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 5040714..f0e2b3a 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -439,10 +439,6 @@ void Parser::Initialize() {
// Prime the lexer look-ahead.
ConsumeToken();
- if (Tok.is(tok::eof) &&
- !getLangOpts().CPlusPlus) // Empty source file is an extension in C
- Diag(Tok, diag::ext_empty_source_file);
-
// Initialization for Objective-C context sensitive keywords recognition.
// Referenced in Parser::ParseObjCTypeQualifierList.
if (getLangOpts().ObjC1) {
diff --git a/clang/test/Misc/warning-flags.c b/clang/test/Misc/warning-flags.c
index 98130c5..cdfb38f 100644
--- a/clang/test/Misc/warning-flags.c
+++ b/clang/test/Misc/warning-flags.c
@@ -17,7 +17,7 @@ This test serves two purposes:
The list of warnings below should NEVER grow. It should gradually shrink to 0.
-CHECK: Warnings without flags (242):
+CHECK: Warnings without flags (241):
CHECK-NEXT: ext_anonymous_struct_union_qualified
CHECK-NEXT: ext_binary_literal
CHECK-NEXT: ext_cast_fn_obj
@@ -26,7 +26,6 @@ CHECK-NEXT: ext_designated_init
CHECK-NEXT: ext_duplicate_declspec
CHECK-NEXT: ext_ellipsis_exception_spec
CHECK-NEXT: ext_empty_fnmacro_arg
-CHECK-NEXT: ext_empty_source_file
CHECK-NEXT: ext_enum_friend
CHECK-NEXT: ext_enum_value_not_int
CHECK-NEXT: ext_enumerator_list_comma
diff --git a/clang/test/PCH/empty-with-headers.c b/clang/test/PCH/empty-with-headers.c
new file mode 100644
index 0000000..751be1c
--- /dev/null
+++ b/clang/test/PCH/empty-with-headers.c
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c99 -pedantic-errors %s
+// RUN: %clang_cc1 -fsyntax-only -std=c99 -emit-pch -o %t %s
+// RUN: %clang_cc1 -fsyntax-only -std=c99 -pedantic-errors -include-pch %t %s
+
+// RUN: %clang_cc1 -fsyntax-only -std=c99 -pedantic-errors -DINCLUDED %s -verify
+// This last one should warn for -Wempty-translation-unit (C99 6.9p1).
+
+#if defined(INCLUDED)
+
+// empty except for the prefix header
+
+#elif defined(HEADER)
+
+typedef int my_int;
+#define INCLUDED
+
+#else
+
+#define HEADER
+#include "empty-with-headers.c"
+// empty except for the header
+
+#endif
+
+// This should only fire if the header is not included,
+// either explicitly or as a prefix header.
+// expected-error{{ISO C requires a translation unit to contain at least one declaration.}}
diff --git a/clang/test/Parser/completely-empty-header-file.h b/clang/test/Parser/completely-empty-header-file.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/clang/test/Parser/completely-empty-header-file.h
diff --git a/clang/test/Parser/empty-translation-unit.c b/clang/test/Parser/empty-translation-unit.c
new file mode 100644
index 0000000..0dbf37e
--- /dev/null
+++ b/clang/test/Parser/empty-translation-unit.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c99 -pedantic -W -verify %s
+// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++03 -pedantic-errors -W %s
+
+#include "completely-empty-header-file.h"
+// no-warning -- an empty file is OK
+
+#define A_MACRO_IS_NOT_GOOD_ENOUGH 1
+
+// In C we should get this warning, but in C++ we shouldn't.
+// expected-warning{{ISO C requires a translation unit to contain at least one declaration.}}
diff --git a/clang/test/Parser/opencl-pragma.cl b/clang/test/Parser/opencl-pragma.cl
index 1946077..4c48b2a 100644
--- a/clang/test/Parser/opencl-pragma.cl
+++ b/clang/test/Parser/opencl-pragma.cl
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -verify -pedantic -fsyntax-only
+// RUN: %clang_cc1 %s -verify -pedantic -Wno-empty-translation-unit -fsyntax-only
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
diff --git a/clang/test/Preprocessor/undef-error.c b/clang/test/Preprocessor/undef-error.c
index ad611de..959c163 100644
--- a/clang/test/Preprocessor/undef-error.c
+++ b/clang/test/Preprocessor/undef-error.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -pedantic-errors -verify
+// RUN: %clang_cc1 %s -pedantic-errors -Wno-empty-translation-unit -verify
// PR2045
#define b
diff --git a/clang/test/Sema/c89-2.c b/clang/test/Sema/c89-2.c
index f6f6bd9..14b955a 100644
--- a/clang/test/Sema/c89-2.c
+++ b/clang/test/Sema/c89-2.c
@@ -1,4 +1,4 @@
-/* RUN: %clang_cc1 %s -std=c89 -pedantic-errors -verify
+/* RUN: %clang_cc1 %s -std=c89 -pedantic-errors -Wno-empty-translation-unit -verify
*/
#if 1LL /* expected-error {{long long}} */