Add a new -Wundefined-inline warning for inline functions which are used but not
authorNick Lewycky <nicholas@mxc.ca>
Fri, 1 Feb 2013 08:13:20 +0000 (08:13 +0000)
committerNick Lewycky <nicholas@mxc.ca>
Fri, 1 Feb 2013 08:13:20 +0000 (08:13 +0000)
defined. Fixes PR14993!

llvm-svn: 174158

14 files changed:
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/ExternalSemaSource.h
clang/include/clang/Sema/MultiplexExternalSemaSource.h
clang/include/clang/Sema/Sema.h
clang/include/clang/Serialization/ASTBitCodes.h
clang/include/clang/Serialization/ASTReader.h
clang/lib/Sema/MultiplexExternalSemaSource.cpp
clang/lib/Sema/Sema.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Serialization/ASTReader.cpp
clang/lib/Serialization/ASTWriter.cpp
clang/test/Analysis/engine/replay-without-inlining.c
clang/test/SemaCXX/undefined-inline.cpp [new file with mode: 0644]

index fe47c3a..b1f4fb6 100644 (file)
@@ -3284,10 +3284,12 @@ def note_deleted_assign_field : Note<
   "%select{copy|move}0 assignment operator of %1 is implicitly deleted "
   "because field %2 is of %select{reference|const-qualified}4 type %3">;
 
-// This should eventually be an error.
+// These should be errors.
 def warn_undefined_internal : Warning<
   "%select{function|variable}0 %q1 has internal linkage but is not defined">,
   InGroup<DiagGroup<"undefined-internal">>;
+def warn_undefined_inline : Warning<"inline function %q0 is not defined">,
+  InGroup<DiagGroup<"undefined-inline">>;
 def note_used_here : Note<"used here">;
 
 def warn_internal_in_extern_inline : ExtWarn<
index 6693dc8..cbce757 100644 (file)
@@ -67,9 +67,11 @@ public:
   virtual void ReadKnownNamespaces(
                            SmallVectorImpl<NamespaceDecl *> &Namespaces);
 
-  virtual void ReadUndefinedInternals(
+  /// \brief Load the set of used but not defined functions or variables with
+  /// internal linkage, or used but not defined internal functions.
+  virtual void ReadUndefinedButUsed(
                          llvm::DenseMap<NamedDecl*, SourceLocation> &Undefined);
-  
+
   /// \brief Do last resort, unqualified lookup on a LookupResult that
   /// Sema cannot find.
   ///
index 494ba65..32b7276 100644 (file)
@@ -253,7 +253,9 @@ public:
   /// which will be used during typo correction.
   virtual void ReadKnownNamespaces(SmallVectorImpl<NamespaceDecl*> &Namespaces);
 
-  virtual void ReadUndefinedInternals(
+  /// \brief Load the set of used but not defined functions or variables with
+  /// internal linkage, or used but not defined inline functions.
+  virtual void ReadUndefinedButUsed(
                          llvm::DenseMap<NamedDecl*, SourceLocation> &Undefined);
   
   /// \brief Do last resort, unqualified lookup on a LookupResult that
index d0d3ff9..da80f0c 100644 (file)
@@ -738,12 +738,12 @@ public:
   // argument locations.
   llvm::DenseMap<ParmVarDecl *, SourceLocation> UnparsedDefaultArgLocs;
 
-  /// UndefinedInternals - all the used, undefined objects with
-  /// internal linkage in this translation unit.
-  llvm::DenseMap<NamedDecl *, SourceLocation> UndefinedInternals;
+  /// UndefinedInternals - all the used, undefined objects which require a
+  /// definition in this translation unit.
+  llvm::DenseMap<NamedDecl *, SourceLocation> UndefinedButUsed;
 
   /// Obtain a sorted list of functions that are undefined but ODR-used.
-  void getUndefinedInternals(
+  void getUndefinedButUsed(
     llvm::SmallVectorImpl<std::pair<NamedDecl *, SourceLocation> > &Undefined);
 
   typedef std::pair<ObjCMethodList, ObjCMethodList> GlobalMethods;
index 1664342..6f76367 100644 (file)
@@ -526,9 +526,9 @@ namespace clang {
       /// being deserialized.
       MACRO_UPDATES = 48,
 
-      /// \brief Record code for undefined but used internal functions and
-      /// variables.
-      UNDEFINED_INTERNALS = 49
+      /// \brief Record code for undefined but used functions and variables that
+      /// need a definition in this TU.
+      UNDEFINED_BUT_USED = 49
     };
 
     /// \brief Record types used within a source manager block.
index aa3633b..d3f3466 100644 (file)
@@ -677,8 +677,9 @@ private:
   /// \brief A list of the namespaces we've seen.
   SmallVector<uint64_t, 4> KnownNamespaces;
 
-  /// \brief A list of undefined decls with internal linkage.
-  SmallVector<uint64_t, 8> UndefinedInternals;
+  /// \brief A list of undefined decls with internal linkage followed by the
+  /// SourceLocation of a matching ODR-use.
+  SmallVector<uint64_t, 8> UndefinedButUsed;
 
   /// \brief A list of modules that were imported by precompiled headers or
   /// any other non-module AST file.
@@ -1520,7 +1521,7 @@ public:
   virtual void ReadKnownNamespaces(
                            SmallVectorImpl<NamespaceDecl *> &Namespaces);
 
-  virtual void ReadUndefinedInternals(
+  virtual void ReadUndefinedButUsed(
                         llvm::DenseMap<NamedDecl *, SourceLocation> &Undefined);
 
   virtual void ReadTentativeDefinitions(
index 609324d..ecce6d8 100644 (file)
@@ -201,10 +201,10 @@ void MultiplexExternalSemaSource::ReadKnownNamespaces(
     Sources[i]->ReadKnownNamespaces(Namespaces);
 }
 
-void MultiplexExternalSemaSource::ReadUndefinedInternals(
+void MultiplexExternalSemaSource::ReadUndefinedButUsed(
                          llvm::DenseMap<NamedDecl*, SourceLocation> &Undefined){
   for(size_t i = 0; i < Sources.size(); ++i)
-    Sources[i]->ReadUndefinedInternals(Undefined);
+    Sources[i]->ReadUndefinedButUsed(Undefined);
 }
   
 bool MultiplexExternalSemaSource::LookupUnqualified(LookupResult &R, Scope *S){ 
index 76ea996..5e5011b 100644 (file)
@@ -366,12 +366,16 @@ static bool ShouldRemoveFromUnused(Sema *SemaRef, const DeclaratorDecl *D) {
 }
 
 namespace {
-  struct SortUndefinedInternal {
+  struct SortUndefinedButUsed {
     const SourceManager &SM;
-    explicit SortUndefinedInternal(SourceManager &SM) : SM(SM) {}
+    explicit SortUndefinedButUsed(SourceManager &SM) : SM(SM) {}
 
     bool operator()(const std::pair<NamedDecl *, SourceLocation> &l,
                     const std::pair<NamedDecl *, SourceLocation> &r) const {
+      if (l.second.isValid() && !r.second.isValid())
+        return true;
+      if (!l.second.isValid() && r.second.isValid())
+        return false;
       if (l.second != r.second)
         return SM.isBeforeInTranslationUnit(l.second, r.second);
       return SM.isBeforeInTranslationUnit(l.first->getLocation(),
@@ -381,55 +385,65 @@ namespace {
 }
 
 /// Obtains a sorted list of functions that are undefined but ODR-used.
-void Sema::getUndefinedInternals(
+void Sema::getUndefinedButUsed(
     SmallVectorImpl<std::pair<NamedDecl *, SourceLocation> > &Undefined) {
   for (llvm::DenseMap<NamedDecl *, SourceLocation>::iterator
-         I = UndefinedInternals.begin(), E = UndefinedInternals.end();
+         I = UndefinedButUsed.begin(), E = UndefinedButUsed.end();
        I != E; ++I) {
     NamedDecl *ND = I->first;
 
     // Ignore attributes that have become invalid.
     if (ND->isInvalidDecl()) continue;
 
-    // If we found out that the decl is external, don't warn.
-    if (ND->getLinkage() == ExternalLinkage) continue;
-
     // __attribute__((weakref)) is basically a definition.
     if (ND->hasAttr<WeakRefAttr>()) continue;
 
     if (FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) {
       if (FD->isDefined())
         continue;
+      if (FD->getLinkage() == ExternalLinkage &&
+          !FD->getMostRecentDecl()->isInlined())
+        continue;
     } else {
       if (cast<VarDecl>(ND)->hasDefinition() != VarDecl::DeclarationOnly)
         continue;
+      if (ND->getLinkage() == ExternalLinkage)
+        continue;
     }
 
     Undefined.push_back(std::make_pair(ND, I->second));
   }
 
-  // Sort (in order of use site) so that we're not (as) dependent on
-  // the iteration order through an llvm::DenseMap.
+  // Sort (in order of use site) so that we're not dependent on the iteration
+  // order through an llvm::DenseMap.
   std::sort(Undefined.begin(), Undefined.end(),
-            SortUndefinedInternal(Context.getSourceManager()));
+            SortUndefinedButUsed(Context.getSourceManager()));
 }
 
-/// checkUndefinedInternals - Check for undefined objects with internal linkage.
-static void checkUndefinedInternals(Sema &S) {
-  if (S.UndefinedInternals.empty()) return;
+/// checkUndefinedButUsed - Check for undefined objects with internal linkage
+/// or that are inline.
+static void checkUndefinedButUsed(Sema &S) {
+  if (S.UndefinedButUsed.empty()) return;
 
   // Collect all the still-undefined entities with internal linkage.
   SmallVector<std::pair<NamedDecl *, SourceLocation>, 16> Undefined;
-  S.getUndefinedInternals(Undefined);
+  S.getUndefinedButUsed(Undefined);
   if (Undefined.empty()) return;
 
   for (SmallVectorImpl<std::pair<NamedDecl *, SourceLocation> >::iterator
          I = Undefined.begin(), E = Undefined.end(); I != E; ++I) {
     NamedDecl *ND = I->first;
 
-    S.Diag(ND->getLocation(), diag::warn_undefined_internal)
-      << isa<VarDecl>(ND) << ND;
-    S.Diag(I->second, diag::note_used_here);
+    if (ND->getLinkage() != ExternalLinkage) {
+      S.Diag(ND->getLocation(), diag::warn_undefined_internal)
+        << isa<VarDecl>(ND) << ND;
+    } else {
+      assert(cast<FunctionDecl>(ND)->getMostRecentDecl()->isInlined() &&
+             "used object requires definition but isn't inline or internal?");
+      S.Diag(ND->getLocation(), diag::warn_undefined_inline) << ND;
+    }
+    if (I->second.isValid())
+      S.Diag(I->second, diag::note_used_here);
   }
 }
 
@@ -743,8 +757,8 @@ void Sema::ActOnEndOfTranslationUnit() {
     }
 
     if (ExternalSource)
-      ExternalSource->ReadUndefinedInternals(UndefinedInternals);
-    checkUndefinedInternals(*this);
+      ExternalSource->ReadUndefinedButUsed(UndefinedButUsed);
+    checkUndefinedButUsed(*this);
   }
 
   if (Diags.getDiagnosticLevel(diag::warn_unused_private_field,
@@ -1088,7 +1102,7 @@ void ExternalSemaSource::ReadKnownNamespaces(
                            SmallVectorImpl<NamespaceDecl *> &Namespaces) {
 }
 
-void ExternalSemaSource::ReadUndefinedInternals(
+void ExternalSemaSource::ReadUndefinedButUsed(
                        llvm::DenseMap<NamedDecl *, SourceLocation> &Undefined) {
 }
 
index 4404c6f..0d549fc 100644 (file)
@@ -2215,6 +2215,23 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S) {
     New->setType(QualType(NewType, 0));
     NewQType = Context.getCanonicalType(New->getType());
   }
+
+  // If this redeclaration makes the function inline, we may need to add it to
+  // UndefinedButUsed.
+  if (!Old->isInlined() && New->isInlined() &&
+      !New->hasAttr<GNUInlineAttr>() &&
+      (getLangOpts().CPlusPlus || !getLangOpts().GNUInline) &&
+      Old->isUsed(false) &&
+      !Old->isDefined() && !New->isThisDeclarationADefinition())
+    UndefinedButUsed.insert(std::make_pair(Old->getCanonicalDecl(),
+                                           SourceLocation()));
+
+  // If this redeclaration makes it newly gnu_inline, we don't want to warn
+  // about it.
+  if (New->hasAttr<GNUInlineAttr>() &&
+      Old->isInlined() && !Old->hasAttr<GNUInlineAttr>()) {
+    UndefinedButUsed.erase(Old->getCanonicalDecl());
+  }
   
   if (getLangOpts().CPlusPlus) {
     // (C++98 13.1p2):
@@ -8431,12 +8448,17 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
   if (FD) {
     FD->setBody(Body);
 
-    // The only way to be included in UndefinedInternals is if there is an
-    // ODR-use before the definition. Avoid the expensive map lookup if this
+    // The only way to be included in UndefinedButUsed is if there is an
+    // ODR use before the definition. Avoid the expensive map lookup if this
     // is the first declaration.
-    if (FD->getPreviousDecl() != 0 && FD->getPreviousDecl()->isUsed() &&
-        FD->getLinkage() != ExternalLinkage)
-      UndefinedInternals.erase(FD);
+    if (FD->getPreviousDecl() != 0 && FD->getPreviousDecl()->isUsed()) {
+      if (FD->getLinkage() != ExternalLinkage)
+        UndefinedButUsed.erase(FD);
+      else if (FD->isInlined() &&
+               (LangOpts.CPlusPlus || !LangOpts.GNUInline) &&
+               (!FD->getPreviousDecl()->hasAttr<GNUInlineAttr>()))
+        UndefinedButUsed.erase(FD);
+    }
 
     // If the function implicitly returns zero (like 'main') or is naked,
     // don't complain about missing return statements.
index 87d75f6..5dd75ce 100644 (file)
@@ -10506,9 +10506,13 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) {
   }
 
   // Keep track of used but undefined functions.
-  if (!Func->isDefined() && Func->getLinkage() != ExternalLinkage) {
-    SourceLocation &old = UndefinedInternals[Func->getCanonicalDecl()];
-    if (old.isInvalid()) old = Loc;
+  if (!Func->isDefined()) {
+    if (Func->getLinkage() != ExternalLinkage)
+      UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc));
+    else if (Func->getMostRecentDecl()->isInlined() &&
+             (LangOpts.CPlusPlus || !LangOpts.GNUInline) &&
+             !Func->getMostRecentDecl()->hasAttr<GNUInlineAttr>())
+      UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc));
   }
 
   // Normally the must current decl is marked used while processing the use and
@@ -11021,7 +11025,7 @@ static void MarkVarDeclODRUsed(Sema &SemaRef, VarDecl *Var,
   if (Var->hasDefinition(SemaRef.Context) == VarDecl::DeclarationOnly &&
       Var->getLinkage() != ExternalLinkage &&
       !(Var->isStaticDataMember() && Var->hasInit())) {
-    SourceLocation &old = SemaRef.UndefinedInternals[Var->getCanonicalDecl()];
+    SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()];
     if (old.isInvalid()) old = Loc;
   }
 
index d38b530..1524da9 100644 (file)
@@ -2462,19 +2462,19 @@ bool ASTReader::ReadASTBlock(ModuleFile &F) {
         KnownNamespaces.push_back(getGlobalDeclID(F, Record[I]));
       break;
 
-    case UNDEFINED_INTERNALS:
-      if (UndefinedInternals.size() % 2 != 0) {
-        Error("Invalid existing UndefinedInternals");
+    case UNDEFINED_BUT_USED:
+      if (UndefinedButUsed.size() % 2 != 0) {
+        Error("Invalid existing UndefinedButUsed");
         return true;
       }
 
       if (Record.size() % 2 != 0) {
-        Error("invalid undefined internals record");
+        Error("invalid undefined-but-used record");
         return true;
       }
       for (unsigned I = 0, N = Record.size(); I != N; /* in loop */) {
-        UndefinedInternals.push_back(getGlobalDeclID(F, Record[I++]));
-        UndefinedInternals.push_back(
+        UndefinedButUsed.push_back(getGlobalDeclID(F, Record[I++]));
+        UndefinedButUsed.push_back(
             ReadSourceLocation(F, Record, I).getRawEncoding());
       }
       break;
@@ -5962,16 +5962,15 @@ void ASTReader::ReadKnownNamespaces(
   }
 }
 
-void ASTReader::ReadUndefinedInternals(
+void ASTReader::ReadUndefinedButUsed(
                         llvm::DenseMap<NamedDecl*, SourceLocation> &Undefined) {
-  for (unsigned Idx = 0, N = UndefinedInternals.size(); Idx != N;) {
-    NamedDecl *D = cast<NamedDecl>(GetDecl(UndefinedInternals[Idx++]));
+  for (unsigned Idx = 0, N = UndefinedButUsed.size(); Idx != N;) {
+    NamedDecl *D = cast<NamedDecl>(GetDecl(UndefinedButUsed[Idx++]));
     SourceLocation Loc =
-        SourceLocation::getFromRawEncoding(UndefinedInternals[Idx++]);
+        SourceLocation::getFromRawEncoding(UndefinedButUsed[Idx++]);
     Undefined.insert(std::make_pair(D, Loc));
   }
 }
-    
 
 void ASTReader::ReadTentativeDefinitions(
                   SmallVectorImpl<VarDecl *> &TentativeDefs) {
index 8120799..2d97546 100644 (file)
@@ -824,7 +824,7 @@ void ASTWriter::WriteBlockInfoBlock() {
   RECORD(OPENCL_EXTENSIONS);
   RECORD(DELEGATING_CTORS);
   RECORD(KNOWN_NAMESPACES);
-  RECORD(UNDEFINED_INTERNALS);
+  RECORD(UNDEFINED_BUT_USED);
   RECORD(MODULE_OFFSET_MAP);
   RECORD(SOURCE_MANAGER_LINE_TABLE);
   RECORD(OBJC_CATEGORIES_MAP);
@@ -3588,15 +3588,15 @@ void ASTWriter::WriteASTCore(Sema &SemaRef,
       AddDeclRef(I->first, KnownNamespaces);
   }
 
-  // Build a record of all used, undefined objects with internal linkage.
-  RecordData UndefinedInternals;
+  // Build a record of all used, undefined objects that require definitions.
+  RecordData UndefinedButUsed;
 
   SmallVector<std::pair<NamedDecl *, SourceLocation>, 16> Undefined;
-  SemaRef.getUndefinedInternals(Undefined);
+  SemaRef.getUndefinedButUsed(Undefined);
   for (SmallVectorImpl<std::pair<NamedDecl *, SourceLocation> >::iterator
          I = Undefined.begin(), E = Undefined.end(); I != E; ++I) {
-    AddDeclRef(I->first, UndefinedInternals);
-    AddSourceLocation(I->second, UndefinedInternals);
+    AddDeclRef(I->first, UndefinedButUsed);
+    AddSourceLocation(I->second, UndefinedButUsed);
   }
 
   // Write the control block
@@ -3814,9 +3814,9 @@ void ASTWriter::WriteASTCore(Sema &SemaRef,
   if (!KnownNamespaces.empty())
     Stream.EmitRecord(KNOWN_NAMESPACES, KnownNamespaces);
 
-  // Write the undefined internal functions and variables.
-  if (!UndefinedInternals.empty())
-    Stream.EmitRecord(UNDEFINED_INTERNALS, UndefinedInternals);
+  // Write the undefined internal functions and variables, and inline functions.
+  if (!UndefinedButUsed.empty())
+    Stream.EmitRecord(UNDEFINED_BUT_USED, UndefinedButUsed);
   
   // Write the visible updates to DeclContexts.
   for (llvm::SmallPtrSet<const DeclContext *, 16>::iterator
index 0602973..14b2b81 100644 (file)
@@ -16,7 +16,7 @@ typedef struct {
     int cur;
     int end;
 } IB;
-inline unsigned long gl(IB *input);
+unsigned long gl(IB *input);
 inline void gbs(IB *input, unsigned char *buf, int count);
 void getB(IB *st, Hdr2 *usedtobeundef);
 inline unsigned char gb(IB *input) {
diff --git a/clang/test/SemaCXX/undefined-inline.cpp b/clang/test/SemaCXX/undefined-inline.cpp
new file mode 100644 (file)
index 0000000..ad719ae
--- /dev/null
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// PR14993
+
+namespace test1 {
+  inline void f();  // expected-warning{{inline function 'test1::f' is not defined}}
+  void test() { f(); }  // expected-note{{used here}}
+}
+
+namespace test2 {
+  inline int f();
+  void test() { (void)sizeof(f()); }
+}
+
+namespace test3 {
+  void f();  // expected-warning{{inline function 'test3::f' is not defined}}
+  inline void f();
+  void test() { f(); }  // expected-note{{used here}}
+}
+
+namespace test4 {
+  inline void error_on_zero(int);    // expected-warning{{inline function 'test4::error_on_zero' is not defined}}
+  inline void error_on_zero(char*) {}
+  void test() { error_on_zero(0); }  // expected-note{{used here}}
+}
+
+namespace test5 {
+  struct X { void f(); };
+  void test(X &x) { x.f(); }
+}
+
+namespace test6 {
+  struct X { inline void f(); };  // expected-warning{{inline function 'test6::X::f' is not defined}}
+  void test(X &x) { x.f(); }  // expected-note{{used here}}
+}
+
+namespace test7 {
+  void f();  // expected-warning{{inline function 'test7::f' is not defined}}
+  void test() { f(); } // no used-here note.
+  inline void f();
+}
+
+namespace test8 {
+  inline void foo() __attribute__((gnu_inline));
+  void test() { foo(); }
+}
+
+namespace test9 {
+  void foo();
+  void test() { foo(); }
+  inline void foo() __attribute__((gnu_inline));
+}
+
+namespace test10 {
+  inline void foo();
+  void test() { foo(); }
+  inline void foo() __attribute__((gnu_inline));
+}