Nathan-Huckleberry created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Create CFG for initializers and perform analysis based warnings on global 
variables


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D63889

Files:
  clang/include/clang/AST/DeclBase.h
  clang/include/clang/Sema/AnalysisBasedWarnings.h
  clang/lib/Analysis/AnalysisDeclContext.cpp
  clang/lib/Sema/AnalysisBasedWarnings.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/test/Sema/warn-unreachable-warning-var-decl.cpp

Index: clang/test/Sema/warn-unreachable-warning-var-decl.cpp
===================================================================
--- /dev/null
+++ clang/test/Sema/warn-unreachable-warning-var-decl.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -verify %s
+int e = 1 ? 0 : 1/0;
+int g = 1 ? 1/0 : 0;// expected-warning{{division by zero is undefined}}
+
+#define SHIFT(n)        (((n) == 32) ? 0 : ((1<<(n))-1))
+
+int x = SHIFT(32);
+int y = SHIFT(0);
+
+// FIXME: Expressions in lambdas aren't ignored
+int z = [](){
+  return 1 ? 0 : 1/0; // expected-warning{{division by zero is undefined}}
+}();
+
+int f(void)
+{
+    int x = 1 ? 0 : 1/0;
+    return x;
+}
+
+template<typename T>
+struct X0 {
+    static T value;
+};
+
+template<typename T>
+struct X1 {
+    static T value;
+};
+
+template<typename T>
+T X0<T>::value = 3.14; // expected-warning{{implicit conversion from 'double' to 'int' changes value from 3.14 to 3}}
+
+template<typename T>
+T X1<T>::value = 1 ? 0 : 1/0;
+
+template struct X0<int>; // expected-note{{in instantiation of static data member 'X0<int>::value' requested here}}
+
+constexpr signed char c1 = 100 * 2; // ok expected-warning{{changes value}}
+constexpr signed char c2 = '\x64' * '\2'; // also ok  expected-warning{{changes value}}
+constexpr int shr_32 = 0 >> 32; // expected-error {{constant expression}} expected-note {{shift count 32 >= width of type}}
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -4881,6 +4881,8 @@
            "default argument expression has capturing blocks?");
   }
 
+  AnalysisWarnings.IssueWarningsForRegisteredVarDecl(Param);
+
   // We already type-checked the argument, so we know it works.
   // Just mark all of the declarations in this potentially-evaluated expression
   // as being "referenced".
@@ -16662,7 +16664,7 @@
   case ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed:
     if (!Stmts.empty() && getCurFunctionOrMethodDecl()) {
       FunctionScopes.back()->PossiblyUnreachableDiags.
-        push_back(sema::PossiblyUnreachableDiag(PD, Loc, Stmts));
+        push_back(clang::sema::PossiblyUnreachableDiag(PD, Loc, Stmts));
       return true;
     }
 
@@ -16671,17 +16673,36 @@
     // but nonetheless is always required to be a constant expression, so we
     // can skip diagnosing.
     // FIXME: Using the mangling context here is a hack.
+    //
+    // Mangling context seems to only be defined on constexpr vardecl that
+    // displayed errors
+    // This skips warnings that were already emitted as notes on errors.
     if (auto *VD = dyn_cast_or_null<VarDecl>(
             ExprEvalContexts.back().ManglingContextDecl)) {
       if (VD->isConstexpr() ||
           (VD->isStaticDataMember() && VD->isFirstDecl() && !VD->isInline()))
         break;
+    }
+
+    // For any other kind of variable, we should build a CFG for its
+    // initializer and check whether the context in question is reachable.
+    if(auto *VD = dyn_cast_or_null<VarDecl>(
+        CurContext->getLastDecl())) {
+      if(VD->getDefinition()) {
+        VD = VD->getDefinition();
+      }
       // FIXME: For any other kind of variable, we should build a CFG for its
       // initializer and check whether the context in question is reachable.
+      // Some cases aren't picked up by path analysis currently
+      if(!Stmts.empty() && VD->isFileVarDecl()) {
+        AnalysisWarnings.RegisterVarDeclWarning(VD, clang::sema::PossiblyUnreachableDiag(PD, Loc, Stmts));
+        return true;
+      }
     }
 
     Diag(Loc, PD);
     return true;
+
   }
 
   return false;
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -288,6 +288,9 @@
     UnparsedDefaultArgInstantiations.erase(InstPos);
   }
 
+  // Check for delayed warnings
+  AnalysisWarnings.IssueWarningsForRegisteredVarDecl(Param);
+
   return false;
 }
 
@@ -333,7 +336,21 @@
     return;
   }
 
+  // Temporarily change the context and add param to it
+  // Allows DiagRuntimeBehavior to register this param with
+  // any possibly warnings
+  // Param gets added back to context when function is added
+  // to context
+  // FIXME: Params should probably be diagnosed after being
+  // actually added to context. Either params get added to
+  // context before the function scope or diagnostics are
+  // delayed until function scope is added
+  DeclContext *Cur = CurContext;
+  CurContext = Param->getDeclContext();
+  CurContext->addDecl(Param);
   SetParamDefaultArgument(Param, DefaultArg, EqualLoc);
+  CurContext->removeDecl(Param);
+  CurContext = Cur;
 }
 
 /// ActOnParamUnparsedDefaultArgument - We've seen a default
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -31,6 +31,7 @@
 #include "clang/Lex/Lexer.h" // TODO: Extract static functions to fix layering.
 #include "clang/Lex/ModuleLoader.h" // TODO: Sema shouldn't depend on Lex
 #include "clang/Lex/Preprocessor.h" // Included for isCodeCompletionEnabled()
+#include "clang/Sema/AnalysisBasedWarnings.h"
 #include "clang/Sema/CXXFieldCollector.h"
 #include "clang/Sema/DeclSpec.h"
 #include "clang/Sema/DelayedDiagnostic.h"
@@ -11849,6 +11850,8 @@
 void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
   if (var->isInvalidDecl()) return;
 
+  AnalysisWarnings.IssueWarningsForRegisteredVarDecl(var);
+
   if (getLangOpts().OpenCL) {
     // OpenCL v2.0 s6.12.5 - Every block variable declaration must have an
     // initialiser
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===================================================================
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2003,11 +2003,56 @@
     isEnabled(D, warn_use_in_invalid_state);
 }
 
-static void flushDiagnostics(Sema &S, const sema::FunctionScopeInfo *fscope) {
-  for (const auto &D : fscope->PossiblyUnreachableDiags)
+static void flushDiagnostics(Sema &S,
+SmallVector<clang::sema::PossiblyUnreachableDiag, 4> PossiblyUnreachableDiags) {
+  for (const auto &D : PossiblyUnreachableDiags)
     S.Diag(D.Loc, D.PD);
 }
 
+void clang::sema::emitPossiblyUnreachableDiags(Sema &S, AnalysisDeclContext &AC,
+SmallVector<clang::sema::PossiblyUnreachableDiag, 4> PossiblyUnreachableDiags) {
+
+  if (!PossiblyUnreachableDiags.empty()) {
+    bool analyzed = false;
+
+    // Register the expressions with the CFGBuilder.
+    for (const auto &D : PossiblyUnreachableDiags) {
+      for (const Stmt *S : D.Stmts)
+        AC.registerForcedBlockExpression(S);
+    }
+
+    if (AC.getCFG()) {
+      analyzed = true;
+      for (const auto &D : PossiblyUnreachableDiags) {
+        bool AllReachable = true;
+        for (const Stmt *S : D.Stmts) {
+          const CFGBlock *block = AC.getBlockForRegisteredExpression(S);
+          CFGReverseBlockReachabilityAnalysis *cra =
+              AC.getCFGReachablityAnalysis();
+          // FIXME: We should be able to assert that block is non-null, but
+          // the CFG analysis can skip potentially-evaluated expressions in
+          // edge cases; see test/Sema/vla-2.c.
+          if (block && cra) {
+            // Can this block be reached from the entrance?
+            if (!cra->isReachable(&AC.getCFG()->getEntry(), block)) {
+              AllReachable = false;
+              break;
+            }
+          }
+          // If we cannot map to a basic block, assume the statement is
+          // reachable.
+        }
+
+        if (AllReachable)
+          S.Diag(D.Loc, D.PD);
+      }
+    }
+
+    if (!analyzed)
+      flushDiagnostics(S, PossiblyUnreachableDiags);
+  }
+}
+
 void clang::sema::
 AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
                                      sema::FunctionScopeInfo *fscope,
@@ -2033,7 +2078,7 @@
 
   if (Diags.hasUncompilableErrorOccurred()) {
     // Flush out any possibly unreachable diagnostics.
-    flushDiagnostics(S, fscope);
+    flushDiagnostics(S, fscope->PossiblyUnreachableDiags);
     return;
   }
 
@@ -2085,45 +2130,7 @@
   }
 
   // Emit delayed diagnostics.
-  if (!fscope->PossiblyUnreachableDiags.empty()) {
-    bool analyzed = false;
-
-    // Register the expressions with the CFGBuilder.
-    for (const auto &D : fscope->PossiblyUnreachableDiags) {
-      for (const Stmt *S : D.Stmts)
-        AC.registerForcedBlockExpression(S);
-    }
-
-    if (AC.getCFG()) {
-      analyzed = true;
-      for (const auto &D : fscope->PossiblyUnreachableDiags) {
-        bool AllReachable = true;
-        for (const Stmt *S : D.Stmts) {
-          const CFGBlock *block = AC.getBlockForRegisteredExpression(S);
-          CFGReverseBlockReachabilityAnalysis *cra =
-              AC.getCFGReachablityAnalysis();
-          // FIXME: We should be able to assert that block is non-null, but
-          // the CFG analysis can skip potentially-evaluated expressions in
-          // edge cases; see test/Sema/vla-2.c.
-          if (block && cra) {
-            // Can this block be reached from the entrance?
-            if (!cra->isReachable(&AC.getCFG()->getEntry(), block)) {
-              AllReachable = false;
-              break;
-            }
-          }
-          // If we cannot map to a basic block, assume the statement is
-          // reachable.
-        }
-
-        if (AllReachable)
-          S.Diag(D.Loc, D.PD);
-      }
-    }
-
-    if (!analyzed)
-      flushDiagnostics(S, fscope);
-  }
+  emitPossiblyUnreachableDiags(S, AC, fscope->PossiblyUnreachableDiags);
 
   // Warning: check missing 'return'
   if (P.enableCheckFallThrough) {
@@ -2249,6 +2256,25 @@
   }
 }
 
+void clang::sema::AnalysisBasedWarnings::RegisterVarDeclWarning(VarDecl *VD,
+    clang::sema::PossiblyUnreachableDiag PossiblyUnreachableDiag) {
+  VarDeclPossiblyUnreachableDiags[VD].emplace_back(PossiblyUnreachableDiag);
+}
+
+void clang::sema::AnalysisBasedWarnings::IssueWarningsForRegisteredVarDecl(VarDecl *VD) {
+  AnalysisDeclContext AC(nullptr, VD);
+
+  AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true;
+  AC.getCFGBuildOptions().AddEHEdges = false;
+  AC.getCFGBuildOptions().AddInitializers = true;
+  AC.getCFGBuildOptions().AddImplicitDtors = true;
+  AC.getCFGBuildOptions().AddTemporaryDtors = true;
+  AC.getCFGBuildOptions().AddCXXNewAllocator = false;
+  AC.getCFGBuildOptions().AddCXXDefaultInitExprInCtors = true;
+
+  clang::sema::emitPossiblyUnreachableDiags(S, AC, VarDeclPossiblyUnreachableDiags[VD]);
+}
+
 void clang::sema::AnalysisBasedWarnings::PrintStats() const {
   llvm::errs() << "\n*** Analysis Based Warnings Stats:\n";
 
Index: clang/lib/Analysis/AnalysisDeclContext.cpp
===================================================================
--- clang/lib/Analysis/AnalysisDeclContext.cpp
+++ clang/lib/Analysis/AnalysisDeclContext.cpp
@@ -119,6 +119,11 @@
     return BD->getBody();
   else if (const auto *FunTmpl = dyn_cast_or_null<FunctionTemplateDecl>(D))
     return FunTmpl->getTemplatedDecl()->getBody();
+  else if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) {
+    if(VD->hasGlobalStorage()) {
+      return const_cast<Stmt*>(dyn_cast<Stmt>(VD->getInit()));
+    }
+  }
 
   llvm_unreachable("unknown code decl");
 }
Index: clang/include/clang/Sema/AnalysisBasedWarnings.h
===================================================================
--- clang/include/clang/Sema/AnalysisBasedWarnings.h
+++ clang/include/clang/Sema/AnalysisBasedWarnings.h
@@ -13,7 +13,9 @@
 #ifndef LLVM_CLANG_SEMA_ANALYSISBASEDWARNINGS_H
 #define LLVM_CLANG_SEMA_ANALYSISBASEDWARNINGS_H
 
+#include "clang/AST/Decl.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/MapVector.h"
 
 namespace clang {
 
@@ -23,8 +25,10 @@
 class ObjCMethodDecl;
 class QualType;
 class Sema;
+class AnalysisDeclContext;
 namespace sema {
   class FunctionScopeInfo;
+  class PossiblyUnreachableDiag;
 }
 
 namespace sema {
@@ -49,6 +53,7 @@
 
   enum VisitFlag { NotVisited = 0, Visited = 1, Pending = 2 };
   llvm::DenseMap<const FunctionDecl*, VisitFlag> VisitedFD;
+  llvm::MapVector<VarDecl*, SmallVector<PossiblyUnreachableDiag, 4>> VarDeclPossiblyUnreachableDiags;
 
   /// \name Statistics
   /// @{
@@ -92,11 +97,19 @@
   void IssueWarnings(Policy P, FunctionScopeInfo *fscope,
                      const Decl *D, QualType BlockType);
 
+  void RegisterVarDeclWarning(VarDecl *VD, PossiblyUnreachableDiag
+                       PossiblyUnreachableDiag);
+
+  void IssueWarningsForRegisteredVarDecl(VarDecl *VD);
+
   Policy getDefaultPolicy() { return DefaultPolicy; }
 
   void PrintStats() const;
 };
 
+void emitPossiblyUnreachableDiags(Sema &S, AnalysisDeclContext &AC,
+SmallVector<PossiblyUnreachableDiag, 4> PossiblyUnreachableDiags);
+
 }} // end namespace clang::sema
 
 #endif
Index: clang/include/clang/AST/DeclBase.h
===================================================================
--- clang/include/clang/AST/DeclBase.h
+++ clang/include/clang/AST/DeclBase.h
@@ -1776,6 +1776,10 @@
     return const_cast<DeclContext*>(this)->getParent();
   }
 
+  Decl *getLastDecl() const {
+    return LastDecl;
+  }
+
   /// getLexicalParent - Returns the containing lexical DeclContext. May be
   /// different from getParent, e.g.:
   ///
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to