]> granicus.if.org Git - clang/commitdiff
Add pedantic warning -Wempty-translation-unit (C11 6.9p1).
authorJordan Rose <jordan_rose@apple.com>
Wed, 6 Jun 2012 17:25:21 +0000 (17:25 +0000)
committerJordan Rose <jordan_rose@apple.com>
Wed, 6 Jun 2012 17:25:21 +0000 (17:25 +0000)
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

14 files changed:
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Basic/SourceManager.h
include/clang/Parse/Parser.h
lib/Basic/SourceManager.cpp
lib/Lex/Preprocessor.cpp
lib/Parse/ParseAST.cpp
lib/Parse/Parser.cpp
test/Misc/warning-flags.c
test/PCH/empty-with-headers.c [new file with mode: 0644]
test/Parser/completely-empty-header-file.h [new file with mode: 0644]
test/Parser/empty-translation-unit.c [new file with mode: 0644]
test/Parser/opencl-pragma.cl
test/Preprocessor/undef-error.c
test/Sema/c89-2.c

index a5057c9d3200a88663c45ed1939362306415f8cd..9811bab2936f1cb443a2185ec501e8e771ee9371 100644 (file)
@@ -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;
index 3164f874f15299719e7e8eba2fc78cc15c43a218..f7fb1f523140f20c4a6d85b4fe18b6c65e841182 100644 (file)
@@ -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;
index 07d632090112ff7f1fc0ca6698a484f7d880400f..2222e781e80218d5e34544fc9790477282b444ff 100644 (file)
@@ -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,
index ed920eb488006c9908d2e7772ee81ac87e4c5837..783e073e665d9f519ef7344a8a1abc186d4a5c6d 100644 (file)
@@ -407,6 +407,7 @@ SourceManager::~SourceManager() {
 
 void SourceManager::clearIDTables() {
   MainFileID = FileID();
+  PredefinesFileID = FileID();
   LocalSLocEntryTable.clear();
   LoadedSLocEntryTable.clear();
   SLocEntryLoaded.clear();
index d387f4392deba80ddeaaa6ad178b710c33100e76..955c39cfe78db5bd9a72f76110bc26013d389386 100644 (file)
@@ -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.
index d1c2624f8cb3e00dc2cc44cbed5da4fd47ef6aa6..3f86c4d2c89bc61f94695c8b6c247900a1fe53f4 100644 (file)
@@ -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
index 504071405b736127cddf384e9b2122d76fbfd5fc..f0e2b3aa85f43e1172f643f05ec4300971af2c68 100644 (file)
@@ -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) {
index 98130c5e27ab331f4d74a1a540f1e982029e648a..cdfb38fb41668648019418a6925e41d9ef361a6a 100644 (file)
@@ -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/test/PCH/empty-with-headers.c b/test/PCH/empty-with-headers.c
new file mode 100644 (file)
index 0000000..751be1c
--- /dev/null
@@ -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/test/Parser/completely-empty-header-file.h b/test/Parser/completely-empty-header-file.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/Parser/empty-translation-unit.c b/test/Parser/empty-translation-unit.c
new file mode 100644 (file)
index 0000000..0dbf37e
--- /dev/null
@@ -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.}}
index 19460771137b2a24ebee562cb6bd9ad0a2154620..4c48b2a496f78ae15faa467cd3b9a046aa9d4d52 100644 (file)
@@ -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
 
index ad611decedda2072a5d122784af62f961a69f843..959c163e031d314c5e12e00d8a89ca1825b6b629 100644 (file)
@@ -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
index f6f6bd972090ce81b6f0e802873363a1056bd00d..14b955a6a4b3846a16826282a4749beacd3216a0 100644 (file)
@@ -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}} */