https://github.com/budimirarandjelovicsyrmia updated 
https://github.com/llvm/llvm-project/pull/70024

From 9b9177b9b6e42fdf1cd54ccd9f16aae892b59f28 Mon Sep 17 00:00:00 2001
From: budimirarandjelovicsyrmia <budimir.arandjelo...@syrmia.com>
Date: Fri, 5 Apr 2024 15:20:37 +0200
Subject: [PATCH] [clang] Catch missing format attributes

---
 clang/docs/ReleaseNotes.rst                   |   3 +
 clang/include/clang/Basic/DiagnosticGroups.td |   1 -
 .../clang/Basic/DiagnosticSemaKinds.td        |   3 +
 clang/include/clang/Sema/Sema.h               |   4 +
 clang/lib/Sema/SemaChecking.cpp               |   4 +-
 clang/lib/Sema/SemaDeclAttr.cpp               | 120 ++++++-
 clang/test/Sema/attr-format-missing.c         | 277 ++++++++++++++++
 clang/test/Sema/attr-format-missing.cpp       | 303 ++++++++++++++++++
 8 files changed, 710 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/Sema/attr-format-missing.c
 create mode 100644 clang/test/Sema/attr-format-missing.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5a123b0b86dda..b6851374fdccc 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -501,6 +501,9 @@ Improvements to Clang's diagnostics
 - Clang emits a ``-Wparentheses`` warning for expressions with consecutive 
comparisons like ``x < y < z``.
   Fixes #GH20456.
 
+- Clang now diagnoses missing format attributes for non-template functions and
+  class/struct/union members. Fixes #GH70024
+
 Improvements to Clang's time-trace
 ----------------------------------
 
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index 4cb4f3d999f7a..1bd826eb715f9 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -507,7 +507,6 @@ def MainReturnType : DiagGroup<"main-return-type">;
 def MaxUnsignedZero : DiagGroup<"max-unsigned-zero">;
 def MissingBraces : DiagGroup<"missing-braces">;
 def MissingDeclarations: DiagGroup<"missing-declarations">;
-def : DiagGroup<"missing-format-attribute">;
 def : DiagGroup<"missing-include-dirs">;
 def MissingNoreturn : DiagGroup<"missing-noreturn">;
 def MultiChar : DiagGroup<"multichar">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e3b4186f1b06f..493791854a856 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1010,6 +1010,9 @@ def err_opencl_invalid_param : Error<
 def err_opencl_invalid_return : Error<
   "declaring function return value of type %0 is not allowed %select{; did you 
forget * ?|}1">;
 def warn_enum_value_overflow : Warning<"overflow in enumeration value">;
+def warn_missing_format_attribute : Warning<
+  "diagnostic behavior may be improved by adding the %0 format attribute to 
the declaration of %1">,
+  InGroup<DiagGroup<"missing-format-attribute">>, DefaultIgnore;
 def warn_pragma_options_align_reset_failed : Warning<
   "#pragma options align=reset failed: %0">,
   InGroup<IgnoredPragmas>;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 6c89d275215de..f149419ac6a66 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3688,6 +3688,10 @@ class Sema final : public SemaBase {
   bool DiagnoseSwiftName(Decl *D, StringRef Name, SourceLocation Loc,
                          const ParsedAttr &AL, bool IsAsync);
 
+  void DiagnoseMissingFormatAttributes(const FunctionDecl *FDecl,
+                                       ArrayRef<const Expr *> Args,
+                                       SourceLocation Loc);
+
   UuidAttr *mergeUuidAttr(Decl *D, const AttributeCommonInfo &CI,
                           StringRef UuidAsWritten, MSGuidDecl *GuidDecl);
 
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index f2dc8e9dd0050..0277375a7600a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -8088,8 +8088,10 @@ void Sema::checkCall(NamedDecl *FDecl, const 
FunctionProtoType *Proto,
     }
   }
 
-  if (FD)
+  if (FD) {
     diagnoseArgDependentDiagnoseIfAttrs(FD, ThisArg, Args, Loc);
+    DiagnoseMissingFormatAttributes(FD, Args, Range.getBegin());
+  }
 }
 
 void Sema::CheckConstrainedAuto(const AutoType *AutoT, SourceLocation Loc) {
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index ca5938083917f..c978182a625ea 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -166,6 +166,13 @@ static bool isInstanceMethod(const Decl *D) {
   return false;
 }
 
+static bool checkIfMethodHasImplicitObjectParameter(const Decl *D) {
+  if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(D))
+    return MethodDecl->isInstance() &&
+           !MethodDecl->hasCXXExplicitFunctionObjectParameter();
+  return false;
+}
+
 static inline bool isNSStringType(QualType T, ASTContext &Ctx,
                                   bool AllowNSAttributedString = false) {
   const auto *PT = T->getAs<ObjCObjectPointerType>();
@@ -315,7 +322,7 @@ static bool checkFunctionOrMethodParameterIndex(
   // In C++ the implicit 'this' function parameter also counts.
   // Parameters are counted from one.
   bool HP = hasFunctionProto(D);
-  bool HasImplicitThisParam = isInstanceMethod(D);
+  bool HasImplicitThisParam = checkIfMethodHasImplicitObjectParameter(D);
   bool IV = HP && isFunctionOrMethodVariadic(D);
   unsigned NumParams =
       (HP ? getFunctionOrMethodNumParams(D) : 0) + HasImplicitThisParam;
@@ -4076,7 +4083,7 @@ static void handleFormatAttr(Sema &S, Decl *D, const 
ParsedAttr &AL) {
 
   // In C++ the implicit 'this' function parameter also counts, and they are
   // counted from one.
-  bool HasImplicitThisParam = isInstanceMethod(D);
+  bool HasImplicitThisParam = checkIfMethodHasImplicitObjectParameter(D);
   unsigned NumArgs = getFunctionOrMethodNumParams(D) + HasImplicitThisParam;
 
   IdentifierInfo *II = AL.getArgAsIdent(0)->Ident;
@@ -4190,7 +4197,7 @@ static void handleCallbackAttr(Sema &S, Decl *D, const 
ParsedAttr &AL) {
     return;
   }
 
-  bool HasImplicitThisParam = isInstanceMethod(D);
+  bool HasImplicitThisParam = checkIfMethodHasImplicitObjectParameter(D);
   int32_t NumArgs = getFunctionOrMethodNumParams(D);
 
   FunctionDecl *FD = D->getAsFunction();
@@ -7185,6 +7192,113 @@ static void handleSwiftAsyncAttr(Sema &S, Decl *D, 
const ParsedAttr &AL) {
     checkSwiftAsyncErrorBlock(S, D, ErrorAttr, AsyncAttr);
 }
 
+// This function is called only if function call is not inside template body.
+// TODO: Add call for function calls inside template body.
+// Check if parent function misses format attribute. If misses, emit warning.
+void Sema::DiagnoseMissingFormatAttributes(const FunctionDecl *FDecl,
+                                           ArrayRef<const Expr *> Args,
+                                           SourceLocation Loc) {
+  assert(FDecl);
+
+  const FunctionDecl *ParentFuncDecl = getCurFunctionDecl();
+  if (!ParentFuncDecl)
+    return;
+
+  // If function is a member of struct/union/class and has implicit object
+  // parameter, format attribute argument indexing starts from 2. Otherwise, it
+  // starts from 1.
+  unsigned int FormatArgumentIndexOffset =
+      checkIfMethodHasImplicitObjectParameter(FDecl) ? 2 : 1;
+  unsigned int ParentFunctionFormatArgumentIndexOffset =
+      checkIfMethodHasImplicitObjectParameter(ParentFuncDecl) ? 2 : 1;
+
+  // Check if function has format attribute with forwarded format string.
+  IdentifierInfo *AttrType;
+  const ParmVarDecl *FormatArg;
+  if (!llvm::any_of(FDecl->specific_attrs<FormatAttr>(),
+                    [&](const FormatAttr *Attr) {
+                      const int FormatIndexOffseted =
+                          Attr->getFormatIdx() - FormatArgumentIndexOffset;
+                      if (FormatIndexOffseted < 0 ||
+                          (unsigned)FormatIndexOffseted >= Args.size())
+                        return false;
+
+                      const auto *FormatArgExpr = dyn_cast<DeclRefExpr>(
+                          Args[FormatIndexOffseted]->IgnoreParenCasts());
+                      if (!FormatArgExpr)
+                        return false;
+
+                      FormatArg = dyn_cast_or_null<ParmVarDecl>(
+                          FormatArgExpr->getReferencedDeclOfCallee());
+                      if (!FormatArg)
+                        return false;
+
+                      AttrType = Attr->getType();
+                      return true;
+                    }))
+    return;
+
+  // Check if format string argument is parent function parameter.
+  unsigned int StringIndex = 0;
+  if (!llvm::any_of(ParentFuncDecl->parameters(),
+                    [&](const ParmVarDecl *Param) {
+                      StringIndex = Param->getFunctionScopeIndex() +
+                                    ParentFunctionFormatArgumentIndexOffset;
+
+                      return Param == FormatArg;
+                    }))
+    return;
+
+  unsigned NumOfParentFunctionParams = ParentFuncDecl->getNumParams();
+
+  // Compare parent and calling function format attribute arguments (archetype
+  // and format string).
+  if (llvm::any_of(
+          ParentFuncDecl->specific_attrs<FormatAttr>(),
+          [&](const FormatAttr *Attr) {
+            if (Attr->getType() != AttrType)
+              return false;
+            int FormatIndexOffseted =
+                Attr->getFormatIdx() - ParentFunctionFormatArgumentIndexOffset;
+
+            if (FormatIndexOffseted < 0 ||
+                (unsigned)FormatIndexOffseted >= NumOfParentFunctionParams)
+              return false;
+
+            if (ParentFuncDecl->parameters()[FormatIndexOffseted] != FormatArg)
+              return false;
+
+            return true;
+          }))
+    return;
+
+  // If parent function is variadic, check if last argument of child function 
is
+  // va_list.
+  unsigned FirstToCheck = [&]() -> unsigned {
+    if (!ParentFuncDecl->isVariadic())
+      return 0;
+    const auto *FirstToCheckArg =
+        dyn_cast<DeclRefExpr>(Args[Args.size() - 1]->IgnoreParenCasts());
+    if (!FirstToCheckArg)
+      return 0;
+
+    if (FirstToCheckArg->getType().getCanonicalType() !=
+        Context.getBuiltinVaListType().getCanonicalType())
+      return 0;
+    return NumOfParentFunctionParams + ParentFunctionFormatArgumentIndexOffset;
+  }();
+
+  // Emit warning
+  llvm::Twine InsertionText(
+      llvm::Twine("__attribute__((format(") + AttrType->getName() + ", " +
+      llvm::Twine(StringIndex) + ", " + llvm::Twine(FirstToCheck) + ")))");
+  SourceLocation ParentFuncLoc = ParentFuncDecl->getLocation();
+  Diag(ParentFuncLoc, diag::warn_missing_format_attribute)
+      << AttrType << ParentFuncDecl
+      << FixItHint::CreateInsertion(ParentFuncLoc,
+                                    InsertionText.getSingleStringRef());
+}
+
 
//===----------------------------------------------------------------------===//
 // Microsoft specific attribute handlers.
 
//===----------------------------------------------------------------------===//
diff --git a/clang/test/Sema/attr-format-missing.c 
b/clang/test/Sema/attr-format-missing.c
new file mode 100644
index 0000000000000..751ece186e750
--- /dev/null
+++ b/clang/test/Sema/attr-format-missing.c
@@ -0,0 +1,277 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wmissing-format-attribute %s
+
+typedef unsigned short char16_t;
+typedef unsigned int char32_t;
+typedef __WCHAR_TYPE__ wchar_t;
+typedef __SIZE_TYPE__ size_t;
+typedef __builtin_va_list va_list;
+
+__attribute__((__format__(__printf__, 1, 2)))
+int printf(const char *, ...); // #printf
+
+__attribute__((__format__(__scanf__, 1, 2)))
+int scanf(const char *, ...); // #scanf
+
+__attribute__((__format__(__printf__, 1, 0)))
+int vprintf(const char *, va_list); // #vprintf
+
+__attribute__((__format__(__scanf__, 1, 0)))
+int vscanf(const char *, va_list); // #vscanf
+
+__attribute__((__format__(__printf__, 2, 0)))
+int vsprintf(char *, const char *, va_list); // #vsprintf
+
+__attribute__((__format__(__printf__, 3, 0)))
+int vsnprintf(char *ch, size_t, const char *, va_list); // #vsnprintf
+
+__attribute__((__format__(__scanf__, 1, 4)))
+void f1(char *out, const size_t len, const char *format, ... /* args */) // #f1
+{
+    va_list args;
+    vsnprintf(out, len, format, args); // expected-warning@#f1 {{diagnostic 
behavior may be improved by adding the 'printf' format attribute to the 
declaration of 'f1'}}
+                                       // CHECK-FIXES: 
__attribute__((format(printf, 3, 4)))
+}
+
+__attribute__((__format__(__printf__, 1, 4)))
+void f2(char *out, const size_t len, const char *format, ... /* args */) // #f2
+{
+    va_list args;
+    vsnprintf(out, len, format, args); // expected-warning@#f2 {{diagnostic 
behavior may be improved by adding the 'printf' format attribute to the 
declaration of 'f2'}}
+                                       // CHECK-FIXES: 
__attribute__((format(printf, 3, 4)))
+}
+
+void f3(char *out, va_list args) // #f3
+{
+    vprintf(out, args); // expected-warning@#f3 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f3'}}
+                        // CHECK-FIXES: __attribute__((format(printf, 1, 0)))
+    vscanf(out, args); // expected-warning@#f3 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f3'}}
+                       // CHECK-FIXES: __attribute__((format(scanf, 1, 0)))
+}
+
+void f4(char* out, ... /* args */) // #f4
+{
+    va_list args;
+    vprintf("test", args); // no warning
+
+    const char *ch;
+    vscanf(ch, args); // no warning
+}
+
+void f5(va_list args) // #f5
+{
+    char *ch;
+    vscanf(ch, args); // no warning
+}
+
+void f6(char *out, va_list args) // #f6
+{
+    char *ch;
+    vscanf(ch, args); // no warning
+    vprintf("test", args); // no warning
+}
+
+void f7(const char *out, ... /* args */) // #f7
+{
+    va_list args;
+
+    vscanf(out, &args[0]); // expected-warning@#f7 {{diagnostic behavior may 
be improved by adding the 'scanf' format attribute to the declaration of 'f7'}}
+                           // CHECK-FIXES: __attribute__((format(scanf, 1, 0)))
+    vprintf(out, &args[0]); // expected-warning@#f7 {{diagnostic behavior may 
be improved by adding the 'printf' format attribute to the declaration of 'f7'}}
+                            // CHECK-FIXES: __attribute__((format(printf, 1, 
0)))
+}
+
+__attribute__((format(scanf, 1, 0)))
+__attribute__((format(printf, 1, 2)))
+void f8(const char *out, ... /* args */) // #f8
+{
+    va_list args;
+
+    vscanf(out, &args[0]); // no warning
+    vprintf(out, &args[0]); // no warning
+}
+
+void f9(const char out[], ... /* args */) // #f9
+{
+    va_list args;
+    char *ch;
+    vscanf(ch, args); // no warning
+    vsprintf(ch, out, args); // expected-warning@#f9 {{diagnostic behavior may 
be improved by adding the 'printf' format attribute to the declaration of 'f9'}}
+                             // CHECK-FIXES: __attribute__((format(printf, 1, 
2)))
+}
+
+void f10(const wchar_t *out, ... /* args */) // #f10
+{
+    va_list args;
+    vprintf(out, args);
+#if __SIZEOF_WCHAR_T__ == 4
+                        // expected-warning@-2 {{incompatible pointer types 
passing 'const wchar_t *' (aka 'const int *') to parameter of type 'const char 
*'}}
+#else
+                        // expected-warning@-4 {{incompatible pointer types 
passing 'const wchar_t *' (aka 'const unsigned short *') to parameter of type 
'const char *'}}
+#endif
+                        // expected-note@#vprintf {{passing argument to 
parameter here}}
+                        // expected-warning@#f10 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f10'}}
+                        // CHECK-FIXES: __attribute__((format(printf, 1, 2)))
+    vscanf((const char *) out, args); // expected-warning@#f10 {{diagnostic 
behavior may be improved by adding the 'scanf' format attribute to the 
declaration of 'f10'}}
+                                      // CHECK-FIXES: 
__attribute__((format(scanf, 1, 2)))
+    vscanf((char *) out, args); // expected-warning@#f10 {{diagnostic behavior 
may be improved by adding the 'scanf' format attribute to the declaration of 
'f10'}}
+                                // CHECK-FIXES: __attribute__((format(scanf, 
1, 2)))
+}
+
+__attribute__((format(printf, 1, 2))) // expected-error {{format argument not 
a string type}}
+void f11(const wchar_t *out, ... /* args */); // #f11
+
+void f12(const char16_t *out, ... /* args */) // #f12
+{
+    va_list args;
+    vscanf(out, args); // expected-warning {{incompatible pointer types 
passing 'const char16_t *' (aka 'const unsigned short *') to parameter of type 
'const char *'}}
+                       // expected-note@#vscanf {{passing argument to 
parameter here}}
+                       // expected-warning@#f12 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f12'}}
+                       // CHECK-FIXES: __attribute__((format(scanf, 1, 2)))
+}
+
+__attribute__((format(printf, 1, 2))) // expected-error {{format argument not 
a string type}}
+void f13(const char16_t *out, ... /* args */); // #f13
+
+void f14(const char32_t *out, ... /* args */) // #f14
+{
+    va_list args;
+    vscanf(out, args); // expected-warning {{incompatible pointer types 
passing 'const char32_t *' (aka 'const unsigned int *') to parameter of type 
'const char *'}}
+                       // expected-note@#vscanf {{passing argument to 
parameter here}}
+                       // expected-warning@#f14 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f14'}}
+                       // CHECK-FIXES: __attribute__((format(scanf, 1, 2)))
+}
+
+__attribute__((format(scanf, 1, 2))) // expected-error {{format argument not a 
string type}}
+void f15(const char32_t *out, ... /* args */); // #f15
+
+void f16(const unsigned char *out, ... /* args */) // #f16
+{
+    va_list args;
+    vprintf(out, args); // expected-warning {{passing 'const unsigned char *' 
to parameter of type 'const char *' converts between pointers to integer types 
where one is of the unique plain 'char' type and the other is not}}
+                        // expected-note@#vprintf {{passing argument to 
parameter here}}
+                        // expected-warning@#f16 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f16'}}
+                        // CHECK-FIXES: __attribute__((format(printf, 1, 2)))
+    vscanf((const char *) out, args); // no warning
+                                      // expected-warning@#f16 {{diagnostic 
behavior may be improved by adding the 'scanf' format attribute to the 
declaration of 'f16'}}
+                                      // CHECK-FIXES: 
__attribute__((format(scanf, 1, 2)))
+    vscanf((char *) out, args); // no warning
+                                // expected-warning@#f16 {{diagnostic behavior 
may be improved by adding the 'scanf' format attribute to the declaration of 
'f16'}}
+                                // CHECK-FIXES: __attribute__((format(scanf, 
1, 2)))
+}
+
+__attribute__((format(printf, 1, 2)))
+void f17(const unsigned char *out, ... /* args */) // #f17
+{
+    va_list args;
+    vprintf(out, args); // expected-warning {{passing 'const unsigned char *' 
to parameter of type 'const char *' converts between pointers to integer types 
where one is of the unique plain 'char' type and the other is not}}
+                        // expected-note@#vprintf {{passing argument to 
parameter here}}
+    vscanf((const char *) out, args); // expected-warning@#f17 {{diagnostic 
behavior may be improved by adding the 'scanf' format attribute to the 
declaration of 'f17'}}
+                                      // CHECK-FIXES: 
__attribute__((format(scanf, 1, 2)))
+    vprintf((const char *) out, args); // no warning
+    vscanf((char *) out, args); // expected-warning@#f17 {{diagnostic behavior 
may be improved by adding the 'scanf' format attribute to the declaration of 
'f17'}}
+                                // CHECK-FIXES: __attribute__((format(scanf, 
1, 2)))
+    vprintf((char *) out, args); // no warning
+}
+
+void f18(signed char *out, ... /* args */) // #f18
+{
+    va_list args;
+    vscanf(out, args); // expected-warning {{passing 'signed char *' to 
parameter of type 'const char *' converts between pointers to integer types 
where one is of the unique plain 'char' type and the other is not}} \
+                       // expected-note@#vscanf {{passing argument to 
parameter here}} \
+                       // expected-warning@#f18 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f18'}}
+                       // CHECK-FIXES: __attribute__((format(scanf, 1, 2)))
+    vscanf((const char *) out, args); // expected-warning@#f18 {{diagnostic 
behavior may be improved by adding the 'scanf' format attribute to the 
declaration of 'f18'}}
+                                      // CHECK-FIXES: 
__attribute__((format(scanf, 1, 2)))
+    vprintf((char *) out, args); // expected-warning@#f18 {{diagnostic 
behavior may be improved by adding the 'printf' format attribute to the 
declaration of 'f18'}}
+                                 // CHECK-FIXES: __attribute__((format(printf, 
1, 2)))
+}
+
+__attribute__((format(scanf, 1, 2)))
+void f19(signed char *out, ... /* args */) // #f19
+{
+    va_list args;
+    vprintf(out, args); // expected-warning {{passing 'signed char *' to 
parameter of type 'const char *' converts between pointers to integer types 
where one is of the unique plain 'char' type and the other is not}}
+                        // expected-note@#vprintf {{passing argument to 
parameter here}}
+                        // expected-warning@#f19 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f19'}}
+                        // CHECK-FIXES: __attribute__((format(printf, 1, 2)))
+    vscanf((const char *) out, args); // no warning
+    vprintf((const char *) out, args); // expected-warning@#f19 {{diagnostic 
behavior may be improved by adding the 'printf' format attribute to the 
declaration of 'f19'}}
+                                       // CHECK-FIXES: 
__attribute__((format(printf, 1, 2)))
+    vscanf((char *) out, args); // no warning
+    vprintf((char *) out, args); // expected-warning@#f19 {{diagnostic 
behavior may be improved by adding the 'printf' format attribute to the 
declaration of 'f19'}}
+                                 // CHECK-FIXES: __attribute__((format(printf, 
1, 2)))
+}
+
+__attribute__((format(printf, 1, 2)))
+void f20(unsigned char out[], ... /* args */) // #f20
+{
+    va_list args;
+    vprintf(out, args); // expected-warning {{passing 'unsigned char *' to 
parameter of type 'const char *' converts between pointers to integer types 
where one is of the unique plain 'char' type and the other is not}}
+                        // expected-note@#vprintf {{passing argument to 
parameter here}}
+    vscanf(out, args); // expected-warning {{passing 'unsigned char *' to 
parameter of type 'const char *' converts between pointers to integer types 
where one is of the unique plain 'char' type and the other is not}}
+                       // expected-note@#vscanf {{passing argument to 
parameter here}}
+                       // expected-warning@#f20 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f20'}}
+                       // CHECK-FIXES: __attribute__((format(scanf, 1, 2)))
+}
+
+void f21(char* out) // #f21
+{
+    va_list args;
+    const char* ch;
+    vsprintf(out, ch, args); // no warning
+    vscanf(out, args); // expected-warning@#f21 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f21'}}
+                       // CHECK-FIXES: __attribute__((format(scanf, 1, 0)))
+}
+
+void f22(const char *out, ... /* args */) // #f22
+{
+    int a;
+    printf(out, a); // expected-warning@#f22 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f22'}}
+                    // CHECK-FIXES: __attribute__((format(printf, 1, 0)))
+    printf(out, 1); // expected-warning@#f22 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f22'}}
+                    // CHECK-FIXES: __attribute__((format(printf, 1, 0)))
+}
+
+__attribute__((format(printf, 1, 2)))
+void f23(const char *out, ... /* args */) // #f23
+{
+    int a;
+    printf(out, a); // no warning
+    printf(out, 1); // no warning
+}
+
+void f24(char* ch, const char *out, ... /* args */) // #f24
+{
+    va_list args;
+    printf(ch, args); // expected-warning@#f24 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f24}}
+                      // CHECK-FIXES: __attribute__((format(printf, 1, 3)))
+    int a;
+    printf(out, a); // expected-warning@#f24 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f24'}}
+                    // CHECK-FIXES: __attribute__((format(printf, 2, 0)))
+    printf(out, 1); // expected-warning@#f24 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f24'}}
+                    // CHECK-FIXES: __attribute__((format(printf, 2, 0)))
+    printf(out, args); // expected-warning@#f24 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f24'}}
+                       // CHECK-FIXES: __attribute__((format(printf, 2, 3)))
+}
+
+typedef va_list tdVaList;
+typedef int tdInt;
+
+void f25(const char *out, ... /* args */) // #f25
+{
+    tdVaList args;
+    printf(out, args); // expected-warning@#f25 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f25'}}
+                       // CHECK-FIXES: __attribute__((format(printf, 1, 2)))
+    tdInt a;
+    scanf(out, a); // expected-warning@#f25 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f25'}}
+                   // CHECK-FIXES: __attribute__((format(scanf, 1, 0)))
+}
+
+void f26(const char *out, tdVaList args) // #f26
+{
+    scanf(out, args); // expected-warning@#f26 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f26'}}
+                      // CHECK-FIXES: __attribute__((format(scanf, 1, 0)))
+    tdInt a;
+    printf(out, a); // expected-warning@#f26 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f26'}}
+                    // CHECK-FIXES: __attribute__((format(printf, 1, 0)))
+}
diff --git a/clang/test/Sema/attr-format-missing.cpp 
b/clang/test/Sema/attr-format-missing.cpp
new file mode 100644
index 0000000000000..2250f3d3b5af0
--- /dev/null
+++ b/clang/test/Sema/attr-format-missing.cpp
@@ -0,0 +1,303 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,beforeCxx2b 
-Wmissing-format-attribute %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2b -Wmissing-format-attribute 
%s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++23 -Wmissing-format-attribute 
%s
+
+typedef __SIZE_TYPE__ size_t;
+typedef __builtin_va_list va_list;
+
+namespace std
+{
+    template<class Elem> struct basic_string_view {};
+    template<class Elem> struct basic_string {
+        const Elem *c_str() const noexcept;
+        basic_string(const basic_string_view<Elem> SW);
+    };
+
+    using string = basic_string<char>;
+    using wstring = basic_string<wchar_t>;
+    using string_view = basic_string_view<char>;
+    using wstring_view = basic_string_view<wchar_t>;
+}
+
+__attribute__((__format__(__printf__, 1, 2)))
+int printf(const char *, ...); // #printf
+
+__attribute__((__format__(__scanf__, 1, 2)))
+int scanf(const char *, ...); // #scanf
+
+__attribute__((__format__(__printf__, 1, 0)))
+int vprintf(const char *, va_list); // #vprintf
+
+__attribute__((__format__(__scanf__, 1, 0)))
+int vscanf(const char *, va_list); // #vscanf
+
+__attribute__((__format__(__printf__, 2, 0)))
+int vsprintf(char *, const char *, va_list); // #vsprintf
+
+__attribute__((__format__(__printf__, 3, 0)))
+int vsnprintf(char *ch, size_t, const char *, va_list); // #vsnprintf
+
+void f1(const std::string &str, ... /* args */) // #f1
+{
+    va_list args;
+    vscanf(str.c_str(), args); // no warning
+    vprintf(str.c_str(), args); // no warning
+}
+
+__attribute__((format(printf, 1, 2))) // expected-error {{format argument not 
a string type}}
+void f2(const std::string &str, ... /* args */); // #f2
+
+void f3(std::string_view str, ... /* args */) // #f3
+{
+    va_list args;
+    vscanf(std::string(str).c_str(), args); // no warning
+    vprintf(std::string(str).c_str(), args); // no warning
+}
+
+__attribute__((format(printf, 1, 2))) // expected-error {{format argument not 
a string type}}
+void f4(std::string_view str, ... /* args */); // #f4
+
+void f5(const std::wstring &str, ... /* args */) // #f5
+{
+    va_list args;
+    vscanf((const char *)str.c_str(), args); // no warning
+    vprintf((const char *)str.c_str(), args); // no warning
+}
+
+__attribute__((format(printf, 1, 2))) // expected-error {{format argument not 
a string type}}
+void f6(const std::wstring &str, ... /* args */); // #f6
+
+void f7(std::wstring_view str, ... /* args */) // #f7
+{
+    va_list args;
+    vscanf((const char *) std::wstring(str).c_str(), args); // no warning
+    vprintf((const char *) std::wstring(str).c_str(), args); // no warning
+}
+
+__attribute__((format(printf, 1, 2))) // expected-error {{format argument not 
a string type}}
+void f8(std::wstring_view str, ... /* args */); // #f8
+
+void f9(const wchar_t *out, ... /* args */) // #f9
+{
+    va_list args;
+    vprintf(out, args); // expected-error {{no matching function for call to 
'vprintf'}}
+                        // expected-note@#vprintf {{candidate function not 
viable: no known conversion from 'const wchar_t *' to 'const char *' for 1st 
argument}}
+    vscanf((const char *) out, args); // expected-warning@#f9 {{diagnostic 
behavior may be improved by adding the 'scanf' format attribute to the 
declaration of 'f9'}}
+                                      // CHECK-FIXES: 
__attribute__((format(scanf, 1, 2)))
+    vscanf((char *) out, args); // expected-warning@#f9 {{diagnostic behavior 
may be improved by adding the 'scanf' format attribute to the declaration of 
'f9'}}
+                                // CHECK-FIXES: __attribute__((format(scanf, 
1, 2)))
+}
+
+__attribute__((format(printf, 1, 2))) // expected-error {{format argument not 
a string type}}
+void f10(const wchar_t *out, ... /* args */); // #f10
+
+void f11(const char16_t *out, ... /* args */) // #f11
+{
+    va_list args;
+    vscanf(out, args); // expected-error {{no matching function for call to 
'vscanf'}}
+                       // expected-note@#vscanf {{candidate function not 
viable: no known conversion from 'const char16_t *' to 'const char *' for 1st 
argument}}
+}
+
+__attribute__((format(printf, 1, 2))) // expected-error {{format argument not 
a string type}}
+void f12(const char16_t *out, ... /* args */); // #f12
+
+void f13(const char32_t *out, ... /* args */) // #f13
+{
+    va_list args;
+    vscanf(out, args); // expected-error {{no matching function for call to 
'vscanf'}}
+                       // expected-note@#vscanf {{candidate function not 
viable: no known conversion from 'const char32_t *' to 'const char *' for 1st 
argument}}
+}
+
+__attribute__((format(scanf, 1, 2))) // expected-error {{format argument not a 
string type}}
+void f14(const char32_t *out, ... /* args */); // #f14
+
+void f15(const char *out, ... /* args */) // #f15
+{
+    va_list args;
+    vscanf(out, args); // expected-warning@#f15 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f15'}}
+                       // CHECK-FIXES: __attribute__((format(scanf, 1, 2)))
+}
+
+__attribute__((format(scanf, 1, 2)))
+void f16(const char *out, ... /* args */) // #f16
+{
+    va_list args;
+    vscanf(out, args); // no warning
+}
+
+void f17(const unsigned char *out, ... /* args */) // #f17
+{
+    va_list args;
+    vscanf(out, args); // expected-error {{no matching function for call to 
'vscanf'}}
+                       // expected-note@#vscanf {{candidate function not 
viable: no known conversion from 'const unsigned char *' to 'const char *' for 
1st argument}}
+}
+
+__attribute__((format(scanf, 1, 2)))
+void f18(const unsigned char *out, ... /* args */) // #f18
+{
+    va_list args;
+    vprintf(out, args); // expected-error {{no matching function for call to 
'vprintf'}}
+                        // expected-note@#vprintf {{candidate function not 
viable: no known conversion from 'const unsigned char *' to 'const char *' for 
1st argument}}
+}
+
+void f19(const signed char *out, ... /* args */) // #f19
+{
+    va_list args;
+    vprintf(out, args); // expected-error {{no matching function for call to 
'vprintf'}}
+                        // expected-note@#vprintf {{candidate function not 
viable: no known conversion from 'const signed char *' to 'const char *' for 
1st argument}}
+}
+
+__attribute__((format(scanf, 1, 2)))
+void f20(const signed char *out, ... /* args */) // #f20
+{
+    va_list args;
+    vscanf(out, args); // expected-error {{no matching function for call to 
'vscanf'}}
+                       // expected-note@#vscanf {{candidate function not 
viable: no known conversion from 'const signed char *' to 'const char *' for 
1st argument}}
+}
+
+void f21(const char out[], ... /* args */) // #f21
+{
+    va_list args;
+    vscanf(out, args); // expected-warning@#f21 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f21'}}
+                       // CHECK-FIXES: __attribute__((format(scanf, 1, 2)))
+}
+
+__attribute__((format(scanf, 1, 0)))
+void f22(const char out[], ... /* args */) // #f22
+{
+    va_list args;
+    vscanf(out, args); // no warning
+}
+
+void f23(const char *out) // #f23
+{
+    va_list args;
+    vscanf(out, args); // expected-warning@#f23 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f23'}}
+                       // CHECK-FIXES: __attribute__((format(scanf, 1, 0)))
+}
+
+void f24(const char *out, va_list args) // #f24
+{
+    vprintf(out, args); // expected-warning@#f24 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f24'}}
+                        // CHECK-FIXES: __attribute__((format(printf, 1, 0)))
+}
+
+typedef va_list tdVaList;
+typedef int tdInt;
+void f25(const char *out, ... /* args */) // #f25
+{
+    tdVaList args;
+    printf(out, args); // expected-warning@#f25 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f25'}}
+                       // CHECK-FIXES: __attribute__((format(printf, 1, 2)))
+    tdInt a;
+    scanf(out, a); // expected-warning@#f25 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f25'}}
+                   // CHECK-FIXES: __attribute__((format(scanf, 1, 0)))
+}
+
+void f26(const char *out, tdVaList args) // #f26
+{
+    scanf(out, args); // expected-warning@#f26 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f26'}}
+                      // CHECK-FIXES: __attribute__((format(scanf, 1, 0)))
+    tdInt a;
+    printf(out, a); // expected-warning@#f26 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f26'}}
+                    // CHECK-FIXES: __attribute__((format(printf, 1, 0)))
+}
+
+struct S1
+{
+    void fn1(const char *out, ... /* args */) // #S1_fn1
+    {
+        va_list args;
+        vscanf(out, args); // expected-warning@#S1_fn1 {{diagnostic behavior 
may be improved by adding the 'scanf' format attribute to the declaration of 
'fn1'}}
+                           // CHECK-FIXES: __attribute__((format(scanf, 2, 3)))
+    }
+
+    __attribute__((format(scanf, 2, 0)))
+    void fn2(const char *out, va_list args); // #S1_fn2
+
+    void fn3(const char *out, ... /* args */);
+
+    void fn4(this S1& expliciteThis, const char *out, va_list args) // #S1_fn4
+    {
+        expliciteThis.fn2(out, args); // beforeCxx2b-error@#S1_fn4 {{explicit 
object parameters are incompatible with C++ standards before C++2b}}
+                                      // expected-warning@#S1_fn4 {{diagnostic 
behavior may be improved by adding the 'scanf' format attribute to the 
declaration of 'fn4'}}
+                                      // CHECK-FIXES: 
__attribute__((format(scanf, 2, 0)))
+    }
+};
+
+void S1::fn3(const char *out, ... /* args */) // #S1_fn3
+{
+    va_list args;
+    fn2(out, args); // expected-warning@#S1_fn3 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'fn3'}}
+                    // CHECK-FIXES: __attribute__((format(scanf, 2, 3)))
+}
+
+union U1
+{
+    __attribute__((format(printf, 2, 0)))
+    void fn1(const char *out, va_list args); // #U1_fn1
+
+    void fn2(const char *out, ... /* args */) // #U1_fn2
+    {
+        va_list args;
+        fn1(out, args); // expected-warning@#U1_fn2 {{diagnostic behavior may 
be improved by adding the 'printf' format attribute to the declaration of 
'fn2'}}
+                        // CHECK-FIXES: __attribute__((format(printf, 2, 3)))
+    }
+
+    void fn3(this U1&, const char *out) // #U1_fn3
+    {
+        va_list args;
+        printf(out, args); // beforeCxx2b-error@#U1_fn3 {{explicit object 
parameters are incompatible with C++ standards before C++2b}}
+                           // expected-warning@#U1_fn3 {{diagnostic behavior 
may be improved by adding the 'printf' format attribute to the declaration of 
'fn3'}}
+                           // CHECK-FIXES: __attribute__((format(printf, 2, 
0)))
+    }
+};
+
+class C1
+{
+    __attribute__((format(printf, 3, 0)))
+    void fn1(const int n, const char *out, va_list args); // #C1_fn1
+
+    void fn2(const char *out, const int n, ... /* args */) // #C1_fn2
+    {
+        va_list args;
+        fn1(n, out, args); // expected-warning@#C1_fn2 {{diagnostic behavior 
may be improved by adding the 'printf' format attribute to the declaration of 
'fn2'}}
+                           // CHECK-FIXES: __attribute__((format(printf, 2, 
4)))
+    }
+
+    void fn3(this const C1&, const char *out, va_list args) // #C1_fn3
+    {
+        scanf(out, args); // beforeCxx2b-error@#C1_fn3 {{explicit object 
parameters are incompatible with C++ standards before C++2b}}
+                          // expected-warning@#C1_fn3 {{diagnostic behavior 
may be improved by adding the 'scanf' format attribute to the declaration of 
'fn3'}}
+                          // CHECK-FIXES: __attribute__((format(scanf, 2, 0)))
+    }
+
+    C1(const int n, const char *out) //#C1_C1a
+    {
+        va_list args;
+        fn1(n, out, args); // expected-warning@#C1_C1a {{diagnostic behavior 
may be improved by adding the 'printf' format attribute to the declaration of 
'C1'}}
+                           // CHECK-FIXES: __attribute__((format(printf, 3, 
0)))
+    }
+
+    C1(const char *out, ... /* args */) // #C1_C1b
+    {
+        va_list args;
+        printf(out, args); // expected-warning@#C1_C1b {{diagnostic behavior 
may be improved by adding the 'printf' format attribute to the declaration of 
'C1'}}
+                           // CHECK-FIXES: __attribute__((format(printf, 2, 
3)))
+    }
+
+    ~C1()
+    {
+        const char *out;
+        va_list args;
+        vprintf(out, args); // no warning
+    }
+};
+
+// TODO: implement for templates
+template <int N>
+void func(char (&str)[N], ... /* args */)
+{
+    va_list args;
+    vprintf(str, args); // no warning
+}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to