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

From 5bc76cd96f52cf8184712c8e1ffa521f05e3e29c 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/Attr.h               |   7 +
 clang/include/clang/Sema/Sema.h               |   4 +
 clang/lib/Sema/SemaChecking.cpp               |   4 +-
 clang/lib/Sema/SemaDeclAttr.cpp               | 112 ++++++-
 clang/test/Sema/attr-format-missing.c         | 304 ++++++++++++++++++
 clang/test/Sema/attr-format-missing.cpp       | 178 ++++++++++
 9 files changed, 612 insertions(+), 4 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 9ed3ff4507671..63be1b80b983a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -626,6 +626,9 @@ Improvements to Clang's diagnostics
   used rather than when they are needed for constant evaluation or when code 
is generated for them.
   The check is now stricter to prevent crashes for some unsupported 
declarations (Fixes #GH95495).
 
+- Clang now diagnoses missing format attributes for non-template functions and
+  class/struct/union members. Fixes #GH60718
+
 Improvements to Clang's time-trace
 ----------------------------------
 
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index 1c4f305fb5d00..4836b499fbbee 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -505,7 +505,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 MissingIncludeDirs : 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 46ad359751d7d..e4fd8904b9f4d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1021,6 +1021,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/Attr.h b/clang/include/clang/Sema/Attr.h
index 3f0b10212789a..37c124ca7b454 100644
--- a/clang/include/clang/Sema/Attr.h
+++ b/clang/include/clang/Sema/Attr.h
@@ -123,6 +123,13 @@ inline bool isInstanceMethod(const Decl *D) {
   return false;
 }
 
+inline bool checkIfMethodHasImplicitObjectParameter(const Decl *D) {
+  if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(D))
+    return MethodDecl->isInstance() &&
+           !MethodDecl->hasCXXExplicitFunctionObjectParameter();
+  return false;
+}
+
 /// Diagnose mutually exclusive attributes when present on a given
 /// declaration. Returns true if diagnosed.
 template <typename AttrTy>
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 2e7af0f691cbb..74b6a8841da4a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3854,6 +3854,10 @@ class Sema final : public SemaBase {
 
   enum class RetainOwnershipKind { NS, CF, OS };
 
+  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 87988519e7691..63697fab64ee1 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -4119,8 +4119,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 99b400307d644..c6a84f47796a7 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -3547,7 +3547,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;
@@ -3660,7 +3660,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();
@@ -5361,6 +5361,114 @@ static void handlePreferredTypeAttr(Sema &S, Decl *D, 
const ParsedAttr &AL) {
   D->addAttr(::new (S.Context) PreferredTypeAttr(S.Context, AL, ParmTSI));
 }
 
+// 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) {
+                      int OffsetFormatIndex =
+                          Attr->getFormatIdx() - FormatArgumentIndexOffset;
+                      if (OffsetFormatIndex < 0 ||
+                          (unsigned)OffsetFormatIndex >= Args.size())
+                        return false;
+
+                      const auto *FormatArgExpr = dyn_cast<DeclRefExpr>(
+                          Args[OffsetFormatIndex]->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 OffsetFormatIndex =
+                Attr->getFormatIdx() - ParentFunctionFormatArgumentIndexOffset;
+
+            if (OffsetFormatIndex < 0 ||
+                (unsigned)OffsetFormatIndex >= NumOfParentFunctionParams)
+              return false;
+
+            if (ParentFuncDecl->parameters()[OffsetFormatIndex] != 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
+  SourceLocation ParentFuncLoc = ParentFuncDecl->getLocation();
+  Diag(ParentFuncLoc, diag::warn_missing_format_attribute)
+      << AttrType << ParentFuncDecl
+      << FixItHint::CreateInsertion(ParentFuncLoc,
+                                    (llvm::Twine("__attribute__((format(") +
+                                     AttrType->getName() + ", " +
+                                     llvm::Twine(StringIndex) + ", " +
+                                     llvm::Twine(FirstToCheck) + ")))")
+                                        .str());
+}
+
 
//===----------------------------------------------------------------------===//
 // 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..136ae0739791e
--- /dev/null
+++ b/clang/test/Sema/attr-format-missing.c
@@ -0,0 +1,304 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,c_diagnostics 
-Wmissing-format-attribute %s
+// RUN: %clang_cc1 -fsyntax-only -Wmissing-format-attribute 
-fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s 
--check-prefixes=CHECK,C-CHECK
+// RUN: %clang_cc1 -fsyntax-only -x c++ -verify=expected,cpp_diagnostics 
-Wmissing-format-attribute %s
+// RUN: %clang_cc1 -fsyntax-only -x c++ -verify=expected,cpp_diagnostics 
-std=c++2b -Wmissing-format-attribute %s
+// RUN: %clang_cc1 -fsyntax-only -x c++ -verify=expected,cpp_diagnostics 
-std=c++23 -Wmissing-format-attribute %s
+// RUN: not %clang_cc1 -fsyntax-only -x c++ -Wmissing-format-attribute 
-fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s --check-prefixes=CHECK
+
+#ifndef __cplusplus
+typedef unsigned short char16_t;
+typedef unsigned int char32_t;
+typedef __WCHAR_TYPE__ wchar_t;
+#endif
+
+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: 
fix-it:"{{.*}}":{[[@LINE-4]]:6-[[@LINE-4]]:6}:"__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: 
fix-it:"{{.*}}":{[[@LINE-4]]:6-[[@LINE-4]]:6}:"__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: 
fix-it:"{{.*}}":{[[@LINE-3]]:6-[[@LINE-3]]:6}:"__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: 
fix-it:"{{.*}}":{[[@LINE-5]]:6-[[@LINE-5]]:6}:"__attribute__((format(scanf, 1, 
0)))"
+}
+
+void f4(char* out, ... /* args */) // #f4
+{
+    va_list args;
+    vprintf("test", args); // expected-no-warning
+
+    const char *ch;
+    vscanf(ch, args); // expected-no-warning
+}
+
+void f5(va_list args) // #f5
+{
+    char *ch;
+    vscanf(ch, args); // expected-no-warning
+}
+
+void f6(char *out, va_list args) // #f6
+{
+    char *ch;
+    vscanf(ch, args); // expected-no-warning
+    vprintf("test", args); // expected-no-warning
+    vprintf(out, args); // expected-warning@#f6 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f6'}}
+                        // CHECK: 
fix-it:"{{.*}}":{[[@LINE-6]]:6-[[@LINE-6]]:6}:"__attribute__((format(printf, 1, 
0)))"
+}
+
+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: 
fix-it:"{{.*}}":{[[@LINE-5]]:6-[[@LINE-5]]:6}:"__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: 
fix-it:"{{.*}}":{[[@LINE-7]]:6-[[@LINE-7]]:6}:"__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]); // expected-no-warning
+    vprintf(out, &args[0]); // expected-no-warning
+}
+
+void f9(const char out[], ... /* args */) // #f9
+{
+    va_list args;
+    char *ch;
+    vscanf(ch, args); // expected-no-warning
+    vscanf(out, args); // expected-warning@#f9 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f9'}}
+                       // CHECK: 
fix-it:"{{.*}}":{[[@LINE-6]]:6-[[@LINE-6]]:6}:"__attribute__((format(scanf, 1, 
2)))"
+    vsprintf(ch, out, args); // expected-warning@#f9 {{diagnostic behavior may 
be improved by adding the 'printf' format attribute to the declaration of 'f9'}}
+                             // CHECK: 
fix-it:"{{.*}}":{[[@LINE-8]]:6-[[@LINE-8]]:6}:"__attribute__((format(printf, 1, 
2)))"
+}
+
+void f10(const wchar_t *out, ... /* args */) // #f10
+{
+    va_list args;
+    vprintf(out, args);
+#if __SIZEOF_WCHAR_T__ == 4
+                        // c_diagnostics-warning@-2 {{incompatible pointer 
types passing 'const wchar_t *' (aka 'const int *') to parameter of type 'const 
char *'}}
+#else
+                        // c_diagnostics-warning@-4 {{incompatible pointer 
types passing 'const wchar_t *' (aka 'const unsigned short *') to parameter of 
type 'const char *'}}
+#endif
+                        // c_diagnostics-note@#vprintf {{passing argument to 
parameter here}}
+                        // c_diagnostics-warning@#f10 {{diagnostic behavior 
may be improved by adding the 'printf' format attribute to the declaration of 
'f10'}}
+                        // cpp_diagnostics-error@-8 {{no matching function for 
call to 'vprintf'}}
+                        // cpp_diagnostics-note@#vprintf {{candidate function 
not viable: no known conversion from 'const wchar_t *' to 'const char *' for 
1st argument}}
+                        // C-CHECK: 
fix-it:"{{.*}}":{[[@LINE-13]]:6-[[@LINE-13]]:6}:"__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: 
fix-it:"{{.*}}":{[[@LINE-15]]:6-[[@LINE-15]]:6}:"__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: 
fix-it:"{{.*}}":{[[@LINE-17]]:6-[[@LINE-17]]:6}:"__attribute__((format(scanf, 
1, 2)))"
+
+}
+
+void f11(const char *out) // #f11
+{
+    va_list args;
+    vscanf(out, args); // expected-warning@#f11 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f11'}}
+                       // CHECK: 
fix-it:"{{.*}}":{[[@LINE-4]]:6-[[@LINE-4]]:6}:"__attribute__((format(scanf, 1, 
0)))"
+}
+
+void f12(const char16_t *out, ... /* args */) // #f12
+{
+    va_list args;
+    vscanf(out, args); // c_diagnostics-warning {{incompatible pointer types 
passing 'const char16_t *' (aka 'const unsigned short *') to parameter of type 
'const char *'}}
+                       // c_diagnostics-note@#vscanf {{passing argument to 
parameter here}}
+                       // c_diagnostics-warning@#f12 {{diagnostic behavior may 
be improved by adding the 'scanf' format attribute to the declaration of 'f12'}}
+                       // C-CHECK: 
fix-it:"{{.*}}":{[[@LINE-6]]:6-[[@LINE-6]]:6}:"__attribute__((format(scanf, 1, 
2)))"
+                        // cpp_diagnostics-error@-4 {{no matching function for 
call to 'vscanf'}}
+                        // cpp_diagnostics-note@#vscanf {{candidate function 
not viable: no known conversion from 'const char16_t *' to 'const char *' for 
1st argument}}
+}
+
+void f13(const char32_t *out, ... /* args */) // #f13
+{
+    va_list args;
+    vscanf(out, args); // c_diagnostics-warning {{incompatible pointer types 
passing 'const char32_t *' (aka 'const unsigned int *') to parameter of type 
'const char *'}}
+                       // c_diagnostics-note@#vscanf {{passing argument to 
parameter here}}
+                       // c_diagnostics-warning@#f13 {{diagnostic behavior may 
be improved by adding the 'scanf' format attribute to the declaration of 'f13'}}
+                       // C-CHECK: 
fix-it:"{{.*}}":{[[@LINE-6]]:6-[[@LINE-6]]:6}:"__attribute__((format(scanf, 1, 
2)))"
+                        // cpp_diagnostics-error@-4 {{no matching function for 
call to 'vscanf'}}
+                        // cpp_diagnostics-note@#vscanf {{candidate function 
not viable: no known conversion from 'const char32_t *' to 'const char *' for 
1st argument}}
+}
+
+void f14(const unsigned char *out, ... /* args */) // #f14
+{
+    va_list args;
+    vprintf(out, args); // c_diagnostics-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}}
+                        // c_diagnostics-note@#vprintf {{passing argument to 
parameter here}}
+                        // c_diagnostics-warning@#f14 {{diagnostic behavior 
may be improved by adding the 'printf' format attribute to the declaration of 
'f14'}}
+                        // C-CHECK: 
fix-it:"{{.*}}":{[[@LINE-6]]:6-[[@LINE-6]]:6}:"__attribute__((format(printf, 1, 
2)))"
+                        // cpp_diagnostics-error@-4 {{no matching function for 
call to 'vprintf'}}
+                        // cpp_diagnostics-note@#vprintf {{candidate function 
not viable: no known conversion from 'const unsigned char *' to 'const char *' 
for 1st argument}}
+    vscanf((const char *) out, args); // expected-warning@#f14 {{diagnostic 
behavior may be improved by adding the 'scanf' format attribute to the 
declaration of 'f14'}}
+                                      // CHECK: 
fix-it:"{{.*}}":{[[@LINE-10]]:6-[[@LINE-10]]:6}:"__attribute__((format(scanf, 
1, 2)))"
+    vscanf((char *) out, args); // expected-warning@#f14 {{diagnostic behavior 
may be improved by adding the 'scanf' format attribute to the declaration of 
'f14'}}
+                                // CHECK: 
fix-it:"{{.*}}":{[[@LINE-12]]:6-[[@LINE-12]]:6}:"__attribute__((format(scanf, 
1, 2)))"
+}
+
+__attribute__((format(printf, 1, 2)))
+void f15(const unsigned char *out, ... /* args */) // #f15
+{
+    va_list args;
+    vprintf(out, args); // c_diagnostics-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}}
+                        // c_diagnostics-note@#vprintf {{passing argument to 
parameter here}}
+                        // cpp_diagnostics-error@-2 {{no matching function for 
call to 'vprintf'}}
+                        // cpp_diagnostics-note@#vprintf {{candidate function 
not viable: no known conversion from 'const unsigned char *' to 'const char *' 
for 1st argument}}
+    vscanf((const char *) out, args); // expected-warning@#f15 {{diagnostic 
behavior may be improved by adding the 'scanf' format attribute to the 
declaration of 'f15'}}
+                                      // CHECK: 
fix-it:"{{.*}}":{[[@LINE-8]]:6-[[@LINE-8]]:6}:"__attribute__((format(scanf, 1, 
2)))"
+    vprintf((const char *) out, args); // expected-no-warning
+    vscanf((char *) out, args); // expected-warning@#f15 {{diagnostic behavior 
may be improved by adding the 'scanf' format attribute to the declaration of 
'f15'}}
+                                // CHECK: 
fix-it:"{{.*}}":{[[@LINE-11]]:6-[[@LINE-11]]:6}:"__attribute__((format(scanf, 
1, 2)))"
+    vprintf((char *) out, args); // expected-no-warning
+}
+
+void f16(signed char *out, ... /* args */) // #f16
+{
+    va_list args;
+    vscanf(out, args); // c_diagnostics-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}} \
+                       // c_diagnostics-note@#vscanf {{passing argument to 
parameter here}} \
+                       // c_diagnostics-warning@#f16 {{diagnostic behavior may 
be improved by adding the 'scanf' format attribute to the declaration of 'f16'}}
+                       // C-CHECK: 
fix-it:"{{.*}}":{[[@LINE-6]]:6-[[@LINE-6]]:6}:"__attribute__((format(scanf, 1, 
2)))"
+                        // cpp_diagnostics-error@-4 {{no matching function for 
call to 'vscanf'}}
+                        // cpp_diagnostics-note@#vscanf {{candidate function 
not viable: no known conversion from 'signed char *' to 'const char *' for 1st 
argument}}
+    vscanf((const char *) out, args); // expected-warning@#f16 {{diagnostic 
behavior may be improved by adding the 'scanf' format attribute to the 
declaration of 'f16'}}
+                                      // CHECK: 
fix-it:"{{.*}}":{[[@LINE-10]]:6-[[@LINE-10]]:6}:"__attribute__((format(scanf, 
1, 2)))"
+    vprintf((char *) out, args); // expected-warning@#f16 {{diagnostic 
behavior may be improved by adding the 'printf' format attribute to the 
declaration of 'f16'}}
+                                 // CHECK: 
fix-it:"{{.*}}":{[[@LINE-12]]:6-[[@LINE-12]]:6}:"__attribute__((format(printf, 
1, 2)))"
+}
+
+__attribute__((format(scanf, 1, 2)))
+void f17(signed char *out, ... /* args */) // #f17
+{
+    va_list args;
+    vprintf(out, args); // c_diagnostics-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}}
+                        // c_diagnostics-note@#vprintf {{passing argument to 
parameter here}}
+                        // c_diagnostics-warning@#f17 {{diagnostic behavior 
may be improved by adding the 'printf' format attribute to the declaration of 
'f17'}}
+                        // C-CHECK: 
fix-it:"{{.*}}":{[[@LINE-6]]:6-[[@LINE-6]]:6}:"__attribute__((format(printf, 1, 
2)))"
+                        // cpp_diagnostics-error@-4 {{no matching function for 
call to 'vprintf'}}
+                        // cpp_diagnostics-note@#vprintf {{candidate function 
not viable: no known conversion from 'signed char *' to 'const char *' for 1st 
argument}}
+    vscanf((const char *) out, args); // expected-no-warning
+    vprintf((const char *) out, args); // expected-warning@#f17 {{diagnostic 
behavior may be improved by adding the 'printf' format attribute to the 
declaration of 'f17'}}
+                                       // CHECK: 
fix-it:"{{.*}}":{[[@LINE-11]]:6-[[@LINE-11]]:6}:"__attribute__((format(printf, 
1, 2)))"
+    vscanf((char *) out, args); // expected-no-warning
+    vprintf((char *) out, args); // expected-warning@#f17 {{diagnostic 
behavior may be improved by adding the 'printf' format attribute to the 
declaration of 'f17'}}
+                                 // CHECK: 
fix-it:"{{.*}}":{[[@LINE-14]]:6-[[@LINE-14]]:6}:"__attribute__((format(printf, 
1, 2)))"
+}
+
+__attribute__((format(printf, 1, 2)))
+void f18(unsigned char out[], ... /* args */) // #f18
+{
+    va_list args;
+    vprintf(out, args); // c_diagnostics-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}}
+                        // c_diagnostics-note@#vprintf {{passing argument to 
parameter here}}
+                        // cpp_diagnostics-error@-2 {{no matching function for 
call to 'vprintf'}}
+                        // cpp_diagnostics-note@#vprintf {{candidate function 
not viable: no known conversion from 'unsigned char *' to 'const char *' for 
1st argument}}
+    vscanf(out, args); // c_diagnostics-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}}
+                       // c_diagnostics-note@#vscanf {{passing argument to 
parameter here}}
+                       // c_diagnostics-warning@#f18 {{diagnostic behavior may 
be improved by adding the 'scanf' format attribute to the declaration of 'f18'}}
+                       // C-CHECK: 
fix-it:"{{.*}}":{[[@LINE-10]]:6-[[@LINE-10]]:6}:"__attribute__((format(scanf, 
1, 2)))"
+                        // cpp_diagnostics-error@-4 {{no matching function for 
call to 'vscanf'}}
+                        // cpp_diagnostics-note@#vscanf {{candidate function 
not viable: no known conversion from 'unsigned char *' to 'const char *' for 
1st argument}}
+}
+
+void f19(char* out) // #f19
+{
+    va_list args;
+    const char* ch;
+    vsprintf(out, ch, args); // expected-no-warning
+    vscanf(out, args); // expected-warning@#f19 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f19'}}
+                       // CHECK: 
fix-it:"{{.*}}":{[[@LINE-6]]:6-[[@LINE-6]]:6}:"__attribute__((format(scanf, 1, 
0)))"
+}
+
+void f20(const char *out, ... /* args */) // #f20
+{
+    int a;
+    printf(out, a); // expected-warning@#f20 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f20'}}
+                    // CHECK: 
fix-it:"{{.*}}":{[[@LINE-4]]:6-[[@LINE-4]]:6}:"__attribute__((format(printf, 1, 
0)))"
+    printf(out, 1); // expected-warning@#f20 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f20'}}
+                    // CHECK: 
fix-it:"{{.*}}":{[[@LINE-6]]:6-[[@LINE-6]]:6}:"__attribute__((format(printf, 1, 
0)))"
+}
+
+__attribute__((format(printf, 1, 2)))
+void f21(const char *out, ... /* args */) // #f21
+{
+    int a;
+    printf(out, a); // expected-no-warning
+    printf(out, 1); // expected-no-warning
+}
+
+void f22(char* ch, const char *out, ... /* args */) // #f22
+{
+    va_list args;
+    printf(ch, args); // expected-warning@#f22 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f22}}
+                      // CHECK: 
fix-it:"{{.*}}":{[[@LINE-4]]:6-[[@LINE-4]]:6}:"__attribute__((format(printf, 1, 
3)))"
+    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: 
fix-it:"{{.*}}":{[[@LINE-7]]:6-[[@LINE-7]]:6}:"__attribute__((format(printf, 2, 
0)))"
+    printf(out, 1); // expected-warning@#f22 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f22'}}
+                    // CHECK: 
fix-it:"{{.*}}":{[[@LINE-9]]:6-[[@LINE-9]]:6}:"__attribute__((format(printf, 2, 
0)))"
+    printf(out, args); // expected-warning@#f22 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f22'}}
+                       // CHECK: 
fix-it:"{{.*}}":{[[@LINE-11]]:6-[[@LINE-11]]:6}:"__attribute__((format(printf, 
2, 3)))"
+}
+
+typedef va_list tdVaList;
+typedef int tdInt;
+
+void f23(const char *out, ... /* args */) // #f23
+{
+    tdVaList args;
+    printf(out, args); // expected-warning@#f23 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f23'}}
+                       // CHECK: 
fix-it:"{{.*}}":{[[@LINE-4]]:6-[[@LINE-4]]:6}:"__attribute__((format(printf, 1, 
2)))"
+    tdInt a;
+    scanf(out, a); // expected-warning@#f23 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f23'}}
+                   // CHECK: 
fix-it:"{{.*}}":{[[@LINE-7]]:6-[[@LINE-7]]:6}:"__attribute__((format(scanf, 1, 
0)))"
+}
+
+void f24(const char *out, tdVaList args) // #f24
+{
+    scanf(out, args); // expected-warning@#f24 {{diagnostic behavior may be 
improved by adding the 'scanf' format attribute to the declaration of 'f24'}}
+                      // CHECK: 
fix-it:"{{.*}}":{[[@LINE-3]]:6-[[@LINE-3]]:6}:"__attribute__((format(scanf, 1, 
0)))"
+    tdInt a;
+    printf(out, a); // expected-warning@#f24 {{diagnostic behavior may be 
improved by adding the 'printf' format attribute to the declaration of 'f24'}}
+                    // CHECK: 
fix-it:"{{.*}}":{[[@LINE-6]]:6-[[@LINE-6]]:6}:"__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..ba66c29fbb9bf
--- /dev/null
+++ b/clang/test/Sema/attr-format-missing.cpp
@@ -0,0 +1,178 @@
+// 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
+// RUN: not %clang_cc1 -fsyntax-only -Wmissing-format-attribute 
-fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %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); // expected-no-warning
+    vprintf(str.c_str(), args); // expected-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); // expected-no-warning
+    vprintf(std::string(str).c_str(), args); // expected-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); // expected-no-warning
+    vprintf((const char *)str.c_str(), args); // expected-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); // 
expected-no-warning
+    vprintf((const char *) std::wstring(str).c_str(), args); // 
expected-no-warning
+}
+
+__attribute__((format(printf, 1, 2))) // expected-error {{format argument not 
a string type}}
+void f8(std::wstring_view str, ... /* args */); // #f8
+
+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: 
fix-it:"{{.*}}":{[[@LINE-4]]:10-[[@LINE-4]]:10}:"__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: 
fix-it:"{{.*}}":{[[@LINE-4]]:10-[[@LINE-4]]:10}:"__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: 
fix-it:"{{.*}}":{[[@LINE-4]]:10-[[@LINE-4]]:10}:"__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: 
fix-it:"{{.*}}":{[[@LINE-4]]:10-[[@LINE-4]]:10}:"__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: 
fix-it:"{{.*}}":{[[@LINE-5]]:10-[[@LINE-5]]:10}:"__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: 
fix-it:"{{.*}}":{[[@LINE-4]]:10-[[@LINE-4]]:10}:"__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: 
fix-it:"{{.*}}":{[[@LINE-4]]:10-[[@LINE-4]]:10}:"__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: 
fix-it:"{{.*}}":{[[@LINE-4]]:5-[[@LINE-4]]:5}:"__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: 
fix-it:"{{.*}}":{[[@LINE-4]]:5-[[@LINE-4]]:5}:"__attribute__((format(printf, 2, 
3)))"
+    }
+
+    ~C1()
+    {
+        const char *out;
+        va_list args;
+        vprintf(out, args); // expected-no-warning
+    }
+};
+
+// TODO: implement for templates
+template <int N>
+void func(char (&str)[N], ... /* args */)
+{
+    va_list args;
+    vprintf(str, args); // expected-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