diff options
| -rw-r--r-- | clang/include/clang/Basic/DiagnosticParseKinds.td | 4 | ||||
| -rw-r--r-- | clang/include/clang/Basic/SourceManager.h | 20 | ||||
| -rw-r--r-- | clang/include/clang/Parse/Parser.h | 3 | ||||
| -rw-r--r-- | clang/lib/Basic/SourceManager.cpp | 1 | ||||
| -rw-r--r-- | clang/lib/Lex/Preprocessor.cpp | 2 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseAST.cpp | 54 | ||||
| -rw-r--r-- | clang/lib/Parse/Parser.cpp | 4 | ||||
| -rw-r--r-- | clang/test/Misc/warning-flags.c | 3 | ||||
| -rw-r--r-- | clang/test/PCH/empty-with-headers.c | 27 | ||||
| -rw-r--r-- | clang/test/Parser/completely-empty-header-file.h | 0 | ||||
| -rw-r--r-- | clang/test/Parser/empty-translation-unit.c | 10 | ||||
| -rw-r--r-- | clang/test/Parser/opencl-pragma.cl | 2 | ||||
| -rw-r--r-- | clang/test/Preprocessor/undef-error.c | 2 | ||||
| -rw-r--r-- | clang/test/Sema/c89-2.c | 2 | 
14 files changed, 108 insertions, 26 deletions
| diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index a5057c9d320..9811bab2936 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 3164f874f15..f7fb1f52314 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 07d63209011..2222e781e80 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 ed920eb4880..783e073e665 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 d387f4392de..955c39cfe78 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 d1c2624f8cb..3f86c4d2c89 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 504071405b7..f0e2b3aa85f 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 98130c5e27a..cdfb38fb416 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 00000000000..751be1c9eb3 --- /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 00000000000..e69de29bb2d --- /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 00000000000..0dbf37e447c --- /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 19460771137..4c48b2a496f 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 ad611decedd..959c163e031 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 f6f6bd97209..14b955a6a4b 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}} */ | 

