inclyc updated this revision to Diff 450496.
inclyc added a comment.

.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131314/new/

https://reviews.llvm.org/D131314

Files:
  clang/lib/Sema/SemaChecking.cpp
  clang/test/SemaCXX/format-strings.cpp

Index: clang/test/SemaCXX/format-strings.cpp
===================================================================
--- clang/test/SemaCXX/format-strings.cpp
+++ clang/test/SemaCXX/format-strings.cpp
@@ -166,6 +166,14 @@
 #if __cplusplus >= 201103L
 namespace evaluated {
 
+struct InitList {
+  static constexpr char value[] = {'%', 's', '%', 'd', '\0'}; // no note here because this is not literal
+};
+
+constexpr const char *init_list_func() { return InitList::value; }
+
+constexpr const char *init_list_func_wrap() { return init_list_func(); }
+
 constexpr const char *basic() { 
   return 
 "%s %d"; // expected-note {{format string is defined here}}
@@ -199,12 +207,15 @@
 
 void f() {
   printf(basic(), 1, 2); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}}
-  printf(correct_fmt(), 1, 2);
+  printf(correct_fmt(), 1, 2); // no warning
   printf(string_linebreak(), 1, 2, 3, 4); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}}
   printf(not_literal(), 1, 2, 3, 4); // expected-warning {{format string is not a string literal}}
   printf(wrap_constexpr(), 1, 2); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}}
+  printf(InitList::value, 1, 2); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}}
+  printf(init_list_func(), 1, 2); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}}
+  printf(init_list_func_wrap(), 1, 2); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}}
 }
 
 
-}
+} // namespace evaluated
 #endif
Index: clang/lib/Sema/SemaChecking.cpp
===================================================================
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -8469,25 +8469,26 @@
     Sema &S, const FormatStringLiteral *FExpr, const Expr *OrigFormatExpr,
     ArrayRef<const Expr *> Args, Sema::FormatArgumentPassingKind APK,
     unsigned format_idx, unsigned firstDataArg, Sema::FormatStringType Type,
-    bool inFunctionCall, Sema::VariadicCallType CallType,
+    bool NoNote, Sema::VariadicCallType CallType,
     llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
     bool IgnoreStringsWithoutSpecifiers);
 
-static const Expr *maybeConstEvalStringLiteral(ASTContext &Context,
-                                               const Expr *E);
-
-// Determine if an expression is a string literal or constant string.
-// If this function returns false on the arguments to a function expecting a
-// format string, we will usually need to emit a warning.
-// True string literals are then checked by CheckFormatString.
 static StringLiteralCheckType
-checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
-                      Sema::FormatArgumentPassingKind APK, unsigned format_idx,
-                      unsigned firstDataArg, Sema::FormatStringType Type,
-                      Sema::VariadicCallType CallType, bool InFunctionCall,
-                      llvm::SmallBitVector &CheckedVarArgs,
-                      UncoveredArgHandler &UncoveredArg, llvm::APSInt Offset,
-                      bool IgnoreStringsWithoutSpecifiers = false) {
+checkVarDecl(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
+             Sema::FormatArgumentPassingKind APK, unsigned format_idx,
+             unsigned firstDataArg, Sema::FormatStringType Type,
+             Sema::VariadicCallType CallType, bool NoNote,
+             llvm::SmallBitVector &CheckedVarArgs,
+             UncoveredArgHandler &UncoveredArg, llvm::APSInt Offset, QualType T,
+             const VarDecl *VD, bool IgnoreStringsWithoutSpecifiers);
+
+static StringLiteralCheckType checkFormatStringExprEvaluated(
+    Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
+    Sema::FormatArgumentPassingKind APK, unsigned format_idx,
+    unsigned firstDataArg, Sema::FormatStringType Type,
+    Sema::VariadicCallType CallType, bool NoNote,
+    llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
+    llvm::APSInt Offset, bool IgnoreStringsWithoutSpecifiers = false) {
   if (S.isConstantEvaluated())
     return SLCT_NotALiteral;
 tryAgain:
@@ -8510,8 +8511,7 @@
   case Stmt::ConditionalOperatorClass: {
     // The expression is a literal if both sub-expressions were, and it was
     // completely checked only if both sub-expressions were checked.
-    const AbstractConditionalOperator *C =
-        cast<AbstractConditionalOperator>(E);
+    const AbstractConditionalOperator *C = cast<AbstractConditionalOperator>(E);
 
     // Determine whether it is necessary to check both sub-expressions, for
     // example, because the condition expression is a constant that can be
@@ -8535,18 +8535,18 @@
     if (!CheckLeft)
       Left = SLCT_UncheckedLiteral;
     else {
-      Left = checkFormatStringExpr(S, C->getTrueExpr(), Args, APK, format_idx,
-                                   firstDataArg, Type, CallType, InFunctionCall,
-                                   CheckedVarArgs, UncoveredArg, Offset,
-                                   IgnoreStringsWithoutSpecifiers);
+      Left = checkFormatStringExprEvaluated(
+          S, C->getTrueExpr(), Args, APK, format_idx, firstDataArg, Type,
+          CallType, NoNote, CheckedVarArgs, UncoveredArg, Offset,
+          IgnoreStringsWithoutSpecifiers);
       if (Left == SLCT_NotALiteral || !CheckRight) {
         return Left;
       }
     }
 
-    StringLiteralCheckType Right = checkFormatStringExpr(
+    StringLiteralCheckType Right = checkFormatStringExprEvaluated(
         S, C->getFalseExpr(), Args, APK, format_idx, firstDataArg, Type,
-        CallType, InFunctionCall, CheckedVarArgs, UncoveredArg, Offset,
+        CallType, NoNote, CheckedVarArgs, UncoveredArg, Offset,
         IgnoreStringsWithoutSpecifiers);
 
     return (CheckLeft && Left < Right) ? Left : Right;
@@ -8575,110 +8575,10 @@
     // As an exception, do not flag errors for variables binding to
     // const string literals.
     if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
-      bool isConstant = false;
-      QualType T = DR->getType();
-
-      if (const ArrayType *AT = S.Context.getAsArrayType(T)) {
-        isConstant = AT->getElementType().isConstant(S.Context);
-      } else if (const PointerType *PT = T->getAs<PointerType>()) {
-        isConstant = T.isConstant(S.Context) &&
-                     PT->getPointeeType().isConstant(S.Context);
-      } else if (T->isObjCObjectPointerType()) {
-        // In ObjC, there is usually no "const ObjectPointer" type,
-        // so don't check if the pointee type is constant.
-        isConstant = T.isConstant(S.Context);
-      }
-
-      if (isConstant) {
-        if (const Expr *Init = VD->getAnyInitializer()) {
-          // Look through initializers like const char c[] = { "foo" }
-          if (const InitListExpr *InitList = dyn_cast<InitListExpr>(Init)) {
-            if (InitList->isStringLiteralInit())
-              Init = InitList->getInit(0)->IgnoreParenImpCasts();
-          }
-          return checkFormatStringExpr(
-              S, Init, Args, APK, format_idx, firstDataArg, Type, CallType,
-              /*InFunctionCall*/ false, CheckedVarArgs, UncoveredArg, Offset);
-        }
-      }
-
-      // When the format argument is an argument of this function, and this
-      // function also has the format attribute, there are several interactions
-      // for which there shouldn't be a warning. For instance, when calling
-      // v*printf from a function that has the printf format attribute, we
-      // should not emit a warning about using `fmt`, even though it's not
-      // constant, because the arguments have already been checked for the
-      // caller of `logmessage`:
-      //
-      //  __attribute__((format(printf, 1, 2)))
-      //  void logmessage(char const *fmt, ...) {
-      //    va_list ap;
-      //    va_start(ap, fmt);
-      //    vprintf(fmt, ap);  /* do not emit a warning about "fmt" */
-      //    ...
-      // }
-      //
-      // Another interaction that we need to support is calling a variadic
-      // format function from a format function that has fixed arguments. For
-      // instance:
-      //
-      //  __attribute__((format(printf, 1, 2)))
-      //  void logstring(char const *fmt, char const *str) {
-      //    printf(fmt, str);  /* do not emit a warning about "fmt" */
-      //  }
-      //
-      // Same (and perhaps more relatably) for the variadic template case:
-      //
-      //  template<typename... Args>
-      //  __attribute__((format(printf, 1, 2)))
-      //  void log(const char *fmt, Args&&... args) {
-      //    printf(fmt, forward<Args>(args)...);
-      //           /* do not emit a warning about "fmt" */
-      //  }
-      //
-      // Due to implementation difficulty, we only check the format, not the
-      // format arguments, in all cases.
-      //
-      if (const auto *PV = dyn_cast<ParmVarDecl>(VD)) {
-        if (const auto *D = dyn_cast<Decl>(PV->getDeclContext())) {
-          for (const auto *PVFormat : D->specific_attrs<FormatAttr>()) {
-            bool IsCXXMember = false;
-            if (const auto *MD = dyn_cast<CXXMethodDecl>(D))
-              IsCXXMember = MD->isInstance();
-
-            bool IsVariadic = false;
-            if (const FunctionType *FnTy = D->getFunctionType())
-              IsVariadic = cast<FunctionProtoType>(FnTy)->isVariadic();
-            else if (const auto *BD = dyn_cast<BlockDecl>(D))
-              IsVariadic = BD->isVariadic();
-            else if (const auto *OMD = dyn_cast<ObjCMethodDecl>(D))
-              IsVariadic = OMD->isVariadic();
-
-            Sema::FormatStringInfo CallerFSI;
-            if (Sema::getFormatStringInfo(PVFormat, IsCXXMember, IsVariadic,
-                                          &CallerFSI)) {
-              // We also check if the formats are compatible.
-              // We can't pass a 'scanf' string to a 'printf' function.
-              if (PV->getFunctionScopeIndex() == CallerFSI.FormatIdx &&
-                  Type == S.GetFormatStringType(PVFormat)) {
-                // Lastly, check that argument passing kinds transition in a
-                // way that makes sense:
-                // from a caller with FAPK_VAList, allow FAPK_VAList
-                // from a caller with FAPK_Fixed, allow FAPK_Fixed
-                // from a caller with FAPK_Fixed, allow FAPK_Variadic
-                // from a caller with FAPK_Variadic, allow FAPK_VAList
-                switch (combineFAPK(CallerFSI.ArgPassingKind, APK)) {
-                case combineFAPK(Sema::FAPK_VAList, Sema::FAPK_VAList):
-                case combineFAPK(Sema::FAPK_Fixed, Sema::FAPK_Fixed):
-                case combineFAPK(Sema::FAPK_Fixed, Sema::FAPK_Variadic):
-                case combineFAPK(Sema::FAPK_Variadic, Sema::FAPK_VAList):
-                  return SLCT_UncheckedLiteral;
-                }
-              }
-            }
-          }
-        }
-      }
+      return checkVarDecl(S, E, Args, APK, format_idx, firstDataArg, Type,
+                          CallType, NoNote, CheckedVarArgs, UncoveredArg,
+                          Offset, DR->getType(), VD,
+                          IgnoreStringsWithoutSpecifiers);
     }
 
     return SLCT_NotALiteral;
@@ -8687,14 +8587,15 @@
   case Stmt::CallExprClass:
   case Stmt::CXXMemberCallExprClass: {
     const CallExpr *CE = cast<CallExpr>(E);
-    if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl())) {
+    if (const NamedDecl *ND =
+            dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl())) {
       bool IsFirst = true;
       StringLiteralCheckType CommonResult;
       for (const auto *FA : ND->specific_attrs<FormatArgAttr>()) {
         const Expr *Arg = CE->getArg(FA->getFormatIdx().getASTIndex());
-        StringLiteralCheckType Result = checkFormatStringExpr(
-            S, Arg, Args, APK, format_idx, firstDataArg, Type, CallType,
-            InFunctionCall, CheckedVarArgs, UncoveredArg, Offset,
+        StringLiteralCheckType Result = checkFormatStringExprEvaluated(
+            S, Arg, Args, APK, format_idx, firstDataArg, Type, CallType, NoNote,
+            CheckedVarArgs, UncoveredArg, Offset,
             IgnoreStringsWithoutSpecifiers);
         if (IsFirst) {
           CommonResult = Result;
@@ -8709,18 +8610,13 @@
         if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString ||
             BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) {
           const Expr *Arg = CE->getArg(0);
-          return checkFormatStringExpr(
+          return checkFormatStringExprEvaluated(
               S, Arg, Args, APK, format_idx, firstDataArg, Type, CallType,
-              InFunctionCall, CheckedVarArgs, UncoveredArg, Offset,
+              NoNote, CheckedVarArgs, UncoveredArg, Offset,
               IgnoreStringsWithoutSpecifiers);
         }
       }
     }
-    if (const Expr *SLE = maybeConstEvalStringLiteral(S.Context, E))
-      return checkFormatStringExpr(S, SLE, Args, APK, format_idx, firstDataArg,
-                                   Type, CallType, /*InFunctionCall*/ false,
-                                   CheckedVarArgs, UncoveredArg, Offset,
-                                   IgnoreStringsWithoutSpecifiers);
     return SLCT_NotALiteral;
   }
   case Stmt::ObjCMessageExprClass: {
@@ -8743,9 +8639,9 @@
         }
 
         const Expr *Arg = ME->getArg(FA->getFormatIdx().getASTIndex());
-        return checkFormatStringExpr(
-            S, Arg, Args, APK, format_idx, firstDataArg, Type, CallType,
-            InFunctionCall, CheckedVarArgs, UncoveredArg, Offset,
+        return checkFormatStringExprEvaluated(
+            S, Arg, Args, APK, format_idx, firstDataArg, Type, CallType, NoNote,
+            CheckedVarArgs, UncoveredArg, Offset,
             IgnoreStringsWithoutSpecifiers);
       }
     }
@@ -8769,13 +8665,58 @@
       }
       FormatStringLiteral FStr(StrE, Offset.sextOrTrunc(64).getSExtValue());
       CheckFormatString(S, &FStr, E, Args, APK, format_idx, firstDataArg, Type,
-                        InFunctionCall, CallType, CheckedVarArgs, UncoveredArg,
+                        NoNote, CallType, CheckedVarArgs, UncoveredArg,
                         IgnoreStringsWithoutSpecifiers);
       return SLCT_CheckedLiteral;
     }
 
     return SLCT_NotALiteral;
   }
+
+  case Stmt::InitListExprClass: {
+    auto *ILE = cast<InitListExpr>(E);
+    if (ILE->isStringLiteralInit()) {
+      auto *SL =
+          dyn_cast<StringLiteral>(ILE->getInit(0)->IgnoreParenImpCasts());
+      return checkFormatStringExprEvaluated(
+          S, SL, Args, APK, format_idx, firstDataArg, Type, CallType, NoNote,
+          CheckedVarArgs, UncoveredArg, Offset);
+    }
+    // looks like {'a', 'b', 'c'}
+    ArrayRef<clang::Expr *> AR = ILE->inits();
+    SmallString<128> StrBuf;
+    QualType Ty;
+    bool HasCStringEnd = false;
+    for (auto &Expr : AR) {
+      if (Expr->getStmtClass() == Stmt::CharacterLiteralClass) {
+        const auto *CL = cast<const CharacterLiteral>(Expr);
+        // construct a StringRef for this
+        unsigned int V = CL->getValue();
+        if (V == 0) {
+          HasCStringEnd = true;
+          break;
+        }
+        StrBuf.push_back(V);
+        Ty = Expr->getType();
+      } else {
+        // not a CharacterLiteral
+        return SLCT_NotALiteral;
+      }
+    }
+    if (!HasCStringEnd) {
+      // FIXME: fire a warning that this InitListExprClass does not end with
+      // '\0', with FixIt hints
+    }
+    const auto SR = StringRef(StrBuf);
+    const auto *SL = StringLiteral::Create(
+        S.Context, SR, StringLiteral::Ordinary,
+        /*Pascal*/ false, S.Context.getStringLiteralArrayType(Ty, SR.size()),
+        SourceLocation());
+    return checkFormatStringExprEvaluated(
+        S, SL, Args, APK, format_idx, firstDataArg, Type, CallType,
+        /*NoNote*/ true, CheckedVarArgs, UncoveredArg, Offset,
+        IgnoreStringsWithoutSpecifiers);
+  }
   case Stmt::BinaryOperatorClass: {
     const BinaryOperator *BinOp = cast<BinaryOperator>(E);
 
@@ -8830,18 +8771,158 @@
   }
 }
 
-// If this expression can be evaluated at compile-time,
-// check if the result is a StringLiteral and return it
-// otherwise return nullptr
-static const Expr *maybeConstEvalStringLiteral(ASTContext &Context,
-                                               const Expr *E) {
-  Expr::EvalResult Result;
-  if (E->EvaluateAsRValue(Result, Context) && Result.Val.isLValue()) {
-    const auto *LVE = Result.Val.getLValueBase().dyn_cast<const Expr *>();
-    if (isa_and_nonnull<StringLiteral>(LVE))
-      return LVE;
+static StringLiteralCheckType
+checkVarDecl(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
+             Sema::FormatArgumentPassingKind APK, unsigned format_idx,
+             unsigned firstDataArg, Sema::FormatStringType Type,
+             Sema::VariadicCallType CallType, bool NoNote,
+             llvm::SmallBitVector &CheckedVarArgs,
+             UncoveredArgHandler &UncoveredArg, llvm::APSInt Offset, QualType T,
+             const VarDecl *VD, bool IgnoreStringsWithoutSpecifiers) {
+  bool isConstant = false;
+
+  if (const ArrayType *AT = S.Context.getAsArrayType(T)) {
+    isConstant = AT->getElementType().isConstant(S.Context);
+  } else if (const PointerType *PT = T->getAs<PointerType>()) {
+    isConstant =
+        T.isConstant(S.Context) && PT->getPointeeType().isConstant(S.Context);
+  } else if (T->isObjCObjectPointerType()) {
+    // In ObjC, there is usually no "const ObjectPointer" type,
+    // so don't check if the pointee type is constant.
+    isConstant = T.isConstant(S.Context);
+  }
+
+  if (isConstant) {
+    if (const Expr *Init = VD->getAnyInitializer()) {
+      // Look through initializers like const char c[] = { "foo" }
+      if (const InitListExpr *InitList = dyn_cast<InitListExpr>(Init)) {
+        if (InitList->isStringLiteralInit())
+          Init = InitList->getInit(0)->IgnoreParenImpCasts();
+      }
+      return checkFormatStringExprEvaluated(
+          S, Init, Args, APK, format_idx, firstDataArg, Type, CallType,
+          /*NoNote*/ false, CheckedVarArgs, UncoveredArg, Offset);
+    }
   }
-  return nullptr;
+
+  // When the format argument is an argument of this function, and this
+  // function also has the format attribute, there are several interactions
+  // for which there shouldn't be a warning. For instance, when calling
+  // v*printf from a function that has the printf format attribute, we
+  // should not emit a warning about using `fmt`, even though it's not
+  // constant, because the arguments have already been checked for the
+  // caller of `logmessage`:
+  //
+  //  __attribute__((format(printf, 1, 2)))
+  //  void logmessage(char const *fmt, ...) {
+  //    va_list ap;
+  //    va_start(ap, fmt);
+  //    vprintf(fmt, ap);  /* do not emit a warning about "fmt" */
+  //    ...
+  // }
+  //
+  // Another interaction that we need to support is calling a variadic
+  // format function from a format function that has fixed arguments. For
+  // instance:
+  //
+  //  __attribute__((format(printf, 1, 2)))
+  //  void logstring(char const *fmt, char const *str) {
+  //    printf(fmt, str);  /* do not emit a warning about "fmt" */
+  //  }
+  //
+  // Same (and perhaps more relatably) for the variadic template case:
+  //
+  //  template<typename... Args>
+  //  __attribute__((format(printf, 1, 2)))
+  //  void log(const char *fmt, Args&&... args) {
+  //    printf(fmt, forward<Args>(args)...);
+  //           /* do not emit a warning about "fmt" */
+  //  }
+  //
+  // Due to implementation difficulty, we only check the format, not the
+  // format arguments, in all cases.
+  //
+  if (const auto *PV = dyn_cast<ParmVarDecl>(VD)) {
+    if (const auto *D = dyn_cast<Decl>(PV->getDeclContext())) {
+      for (const auto *PVFormat : D->specific_attrs<FormatAttr>()) {
+        bool IsCXXMember = false;
+        if (const auto *MD = dyn_cast<CXXMethodDecl>(D))
+          IsCXXMember = MD->isInstance();
+
+        bool IsVariadic = false;
+        if (const FunctionType *FnTy = D->getFunctionType())
+          IsVariadic = cast<FunctionProtoType>(FnTy)->isVariadic();
+        else if (const auto *BD = dyn_cast<BlockDecl>(D))
+          IsVariadic = BD->isVariadic();
+        else if (const auto *OMD = dyn_cast<ObjCMethodDecl>(D))
+          IsVariadic = OMD->isVariadic();
+
+        Sema::FormatStringInfo CallerFSI;
+        if (Sema::getFormatStringInfo(PVFormat, IsCXXMember, IsVariadic,
+                                      &CallerFSI)) {
+          // We also check if the formats are compatible.
+          // We can't pass a 'scanf' string to a 'printf' function.
+          if (PV->getFunctionScopeIndex() == CallerFSI.FormatIdx &&
+              Type == S.GetFormatStringType(PVFormat)) {
+            // Lastly, check that argument passing kinds transition in a
+            // way that makes sense:
+            // from a caller with FAPK_VAList, allow FAPK_VAList
+            // from a caller with FAPK_Fixed, allow FAPK_Fixed
+            // from a caller with FAPK_Fixed, allow FAPK_Variadic
+            // from a caller with FAPK_Variadic, allow FAPK_VAList
+            switch (combineFAPK(CallerFSI.ArgPassingKind, APK)) {
+            case combineFAPK(Sema::FAPK_VAList, Sema::FAPK_VAList):
+            case combineFAPK(Sema::FAPK_Fixed, Sema::FAPK_Fixed):
+            case combineFAPK(Sema::FAPK_Fixed, Sema::FAPK_Variadic):
+            case combineFAPK(Sema::FAPK_Variadic, Sema::FAPK_VAList):
+              return SLCT_UncheckedLiteral;
+            }
+          }
+        }
+      }
+    }
+  }
+  return SLCT_NotALiteral;
+}
+
+// Determine if an expression is a string literal or constant string.
+// If this function returns false on the arguments to a function expecting a
+// format string, we will usually need to emit a warning.
+// True string literals are then checked by CheckFormatString.
+static StringLiteralCheckType
+checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
+                      Sema::FormatArgumentPassingKind APK, unsigned format_idx,
+                      unsigned firstDataArg, Sema::FormatStringType Type,
+                      Sema::VariadicCallType CallType, bool NoNote,
+                      llvm::SmallBitVector &CheckedVarArgs,
+                      UncoveredArgHandler &UncoveredArg, llvm::APSInt Offset,
+                      bool IgnoreStringsWithoutSpecifiers = false) {
+  auto T = checkFormatStringExprEvaluated(
+      S, E, Args, APK, format_idx, firstDataArg, Type, CallType, NoNote,
+      CheckedVarArgs, UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers);
+  if (T == SLCT_NotALiteral) {
+    Expr::EvalResult Result;
+    if (E->EvaluateAsRValue(Result, S.Context) && Result.Val.isLValue()) {
+      if (isa<CallExpr>(E) || isa<CXXMemberCallExpr>(E)) {
+        const auto *LVE = Result.Val.getLValueBase().dyn_cast<const Expr *>();
+
+        if (LVE)
+          return checkFormatStringExprEvaluated(
+              S, LVE, Args, APK, format_idx, firstDataArg, Type, CallType,
+              /*NoNote*/ false, CheckedVarArgs, UncoveredArg, Offset,
+              IgnoreStringsWithoutSpecifiers);
+      }
+
+      const auto *LVVD =
+          Result.Val.getLValueBase().dyn_cast<const ValueDecl *>();
+      if (isa_and_nonnull<const VarDecl>(LVVD))
+        return checkVarDecl(
+            S, E, Args, APK, format_idx, firstDataArg, Type, CallType, NoNote,
+            CheckedVarArgs, UncoveredArg, Offset, LVVD->getType(),
+            dyn_cast<const VarDecl>(LVVD), IgnoreStringsWithoutSpecifiers);
+    }
+  }
+  return T;
 }
 
 Sema::FormatStringType Sema::GetFormatStringType(const FormatAttr *Format) {
@@ -8976,7 +9057,7 @@
   llvm::SmallBitVector CoveredArgs;
   bool usesPositionalArgs = false;
   bool atFirstArg = true;
-  bool inFunctionCall;
+  bool noNote;
   Sema::VariadicCallType CallType;
   llvm::SmallBitVector &CheckedVarArgs;
   UncoveredArgHandler &UncoveredArg;
@@ -8988,14 +9069,14 @@
                      unsigned numDataArgs, const char *beg,
                      Sema::FormatArgumentPassingKind APK,
                      ArrayRef<const Expr *> Args, unsigned formatIdx,
-                     bool inFunctionCall, Sema::VariadicCallType callType,
+                     bool noNote, Sema::VariadicCallType callType,
                      llvm::SmallBitVector &CheckedVarArgs,
                      UncoveredArgHandler &UncoveredArg)
       : S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr), FSType(type),
         FirstDataArg(firstDataArg), NumDataArgs(numDataArgs), Beg(beg),
-        ArgPassingKind(APK), Args(Args), FormatIdx(formatIdx),
-        inFunctionCall(inFunctionCall), CallType(callType),
-        CheckedVarArgs(CheckedVarArgs), UncoveredArg(UncoveredArg) {
+        ArgPassingKind(APK), Args(Args), FormatIdx(formatIdx), noNote(noNote),
+        CallType(callType), CheckedVarArgs(CheckedVarArgs),
+        UncoveredArg(UncoveredArg) {
     CoveredArgs.resize(numDataArgs);
     CoveredArgs.reset();
   }
@@ -9031,7 +9112,7 @@
 
   template <typename Range>
   static void
-  EmitFormatDiagnostic(Sema &S, bool inFunctionCall, const Expr *ArgumentExpr,
+  EmitFormatDiagnostic(Sema &S, bool NoNote, const Expr *ArgumentExpr,
                        const PartialDiagnostic &PDiag, SourceLocation StringLoc,
                        bool IsStringLocation, Range StringRange,
                        ArrayRef<FixItHint> Fixit = None);
@@ -9362,16 +9443,17 @@
                                               bool IsStringLocation,
                                               Range StringRange,
                                               ArrayRef<FixItHint> FixIt) {
-  EmitFormatDiagnostic(S, inFunctionCall, Args[FormatIdx], PDiag,
-                       Loc, IsStringLocation, StringRange, FixIt);
+  EmitFormatDiagnostic(S, noNote, Args[FormatIdx], PDiag, Loc, IsStringLocation,
+                       StringRange, FixIt);
 }
 
 /// If the format string is not within the function call, emit a note
 /// so that the function call and string are in diagnostic messages.
 ///
-/// \param InFunctionCall if true, the format string is within the function
-/// call and only one diagnostic message will be produced.  Otherwise, an
-/// extra note will be emitted pointing to location of the format string.
+/// \param NoNote if true, only one diagnostic message will be produced. Perhaps
+/// the format string is within the function call or is evaluted which does not
+/// have an appropriate source location. Otherwise, an extra note will be
+/// emitted pointing to location of the format string.
 ///
 /// \param ArgumentExpr the expression that is passed as the format string
 /// argument in the function call.  Used for getting locations when two
@@ -9395,10 +9477,10 @@
 /// \param FixIt optional fix it hint for the format string.
 template <typename Range>
 void CheckFormatHandler::EmitFormatDiagnostic(
-    Sema &S, bool InFunctionCall, const Expr *ArgumentExpr,
+    Sema &S, bool NoNote, const Expr *ArgumentExpr,
     const PartialDiagnostic &PDiag, SourceLocation Loc, bool IsStringLocation,
     Range StringRange, ArrayRef<FixItHint> FixIt) {
-  if (InFunctionCall) {
+  if (NoNote) {
     const Sema::SemaDiagnosticBuilder &D = S.Diag(Loc, PDiag);
     D << StringRange;
     D << FixIt;
@@ -9427,13 +9509,12 @@
                      unsigned numDataArgs, bool isObjC, const char *beg,
                      Sema::FormatArgumentPassingKind APK,
                      ArrayRef<const Expr *> Args, unsigned formatIdx,
-                     bool inFunctionCall, Sema::VariadicCallType CallType,
+                     bool noNote, Sema::VariadicCallType CallType,
                      llvm::SmallBitVector &CheckedVarArgs,
                      UncoveredArgHandler &UncoveredArg)
       : CheckFormatHandler(s, fexpr, origFormatExpr, type, firstDataArg,
-                           numDataArgs, beg, APK, Args, formatIdx,
-                           inFunctionCall, CallType, CheckedVarArgs,
-                           UncoveredArg) {}
+                           numDataArgs, beg, APK, Args, formatIdx, noNote,
+                           CallType, CheckedVarArgs, UncoveredArg) {}
 
   bool isObjCContext() const { return FSType == Sema::FST_NSString; }
 
@@ -10375,13 +10456,12 @@
                     unsigned firstDataArg, unsigned numDataArgs,
                     const char *beg, Sema::FormatArgumentPassingKind APK,
                     ArrayRef<const Expr *> Args, unsigned formatIdx,
-                    bool inFunctionCall, Sema::VariadicCallType CallType,
+                    bool noNote, Sema::VariadicCallType CallType,
                     llvm::SmallBitVector &CheckedVarArgs,
                     UncoveredArgHandler &UncoveredArg)
       : CheckFormatHandler(s, fexpr, origFormatExpr, type, firstDataArg,
-                           numDataArgs, beg, APK, Args, formatIdx,
-                           inFunctionCall, CallType, CheckedVarArgs,
-                           UncoveredArg) {}
+                           numDataArgs, beg, APK, Args, formatIdx, noNote,
+                           CallType, CheckedVarArgs, UncoveredArg) {}
 
   bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,
                             const char *startSpecifier,
@@ -10544,13 +10624,13 @@
     Sema &S, const FormatStringLiteral *FExpr, const Expr *OrigFormatExpr,
     ArrayRef<const Expr *> Args, Sema::FormatArgumentPassingKind APK,
     unsigned format_idx, unsigned firstDataArg, Sema::FormatStringType Type,
-    bool inFunctionCall, Sema::VariadicCallType CallType,
+    bool NoNote, Sema::VariadicCallType CallType,
     llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
     bool IgnoreStringsWithoutSpecifiers) {
   // CHECK: is the format string a wide literal?
   if (!FExpr->isAscii() && !FExpr->isUTF8()) {
     CheckFormatHandler::EmitFormatDiagnostic(
-        S, inFunctionCall, Args[format_idx],
+        S, NoNote, Args[format_idx],
         S.PDiag(diag::warn_format_string_is_wide_literal), FExpr->getBeginLoc(),
         /*IsStringLocation*/ true, OrigFormatExpr->getSourceRange());
     return;
@@ -10576,7 +10656,7 @@
   // embedded null character.
   if (TypeSize <= StrRef.size() && !StrRef.substr(0, TypeSize).contains('\0')) {
     CheckFormatHandler::EmitFormatDiagnostic(
-        S, inFunctionCall, Args[format_idx],
+        S, NoNote, Args[format_idx],
         S.PDiag(diag::warn_printf_format_string_not_null_terminated),
         FExpr->getBeginLoc(),
         /*IsStringLocation=*/true, OrigFormatExpr->getSourceRange());
@@ -10586,8 +10666,8 @@
   // CHECK: empty format string?
   if (StrLen == 0 && numDataArgs > 0) {
     CheckFormatHandler::EmitFormatDiagnostic(
-        S, inFunctionCall, Args[format_idx],
-        S.PDiag(diag::warn_empty_format_string), FExpr->getBeginLoc(),
+        S, NoNote, Args[format_idx], S.PDiag(diag::warn_empty_format_string),
+        FExpr->getBeginLoc(),
         /*IsStringLocation*/ true, OrigFormatExpr->getSourceRange());
     return;
   }
@@ -10598,8 +10678,7 @@
     CheckPrintfHandler H(
         S, FExpr, OrigFormatExpr, Type, firstDataArg, numDataArgs,
         (Type == Sema::FST_NSString || Type == Sema::FST_OSTrace), Str, APK,
-        Args, format_idx, inFunctionCall, CallType, CheckedVarArgs,
-        UncoveredArg);
+        Args, format_idx, NoNote, CallType, CheckedVarArgs, UncoveredArg);
 
     if (!analyze_format_string::ParsePrintfString(
             H, Str, Str + StrLen, S.getLangOpts(), S.Context.getTargetInfo(),
@@ -10607,7 +10686,7 @@
       H.DoneProcessing();
   } else if (Type == Sema::FST_Scanf) {
     CheckScanfHandler H(S, FExpr, OrigFormatExpr, Type, firstDataArg,
-                        numDataArgs, Str, APK, Args, format_idx, inFunctionCall,
+                        numDataArgs, Str, APK, Args, format_idx, NoNote,
                         CallType, CheckedVarArgs, UncoveredArg);
 
     if (!analyze_format_string::ParseScanfString(
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to