[clang] [clang] Catch missing format attributes (PR #70024)
Budimir Aranđelović via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 5 06:21:20 PDT 2024
https://github.com/budimirarandjelovicsyrmia updated https://github.com/llvm/llvm-project/pull/70024
>From 184fa1b21011d3700f2d501c49882bb87fcb5f38 Mon Sep 17 00:00:00 2001
From: budimirarandjelovicsyrmia <budimir.arandjelovic at 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 | 3 +
clang/lib/Sema/SemaChecking.cpp | 4 +-
clang/lib/Sema/SemaDeclAttr.cpp | 117 ++++++++-
clang/test/Sema/attr-format-missing.c | 223 ++++++++++++++++
clang/test/Sema/attr-format-missing.cpp | 238 ++++++++++++++++++
8 files changed, 587 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 3a84ff16a1e4d4..c6cd05da32332d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -381,6 +381,9 @@ Bug Fixes in This Version
- Fixed a regression in CTAD that a friend declaration that befriends itself may cause
incorrect constraint substitution. (#GH86769).
+- Clang now diagnoses missing format attributes for non-template functions and
+ class/struct/union members. Fixes #GH70024
+
Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 520168f01fd846..5c6a69ff1586db 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 : 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 df57f5e6ce11ba..d32bf4c8a9549e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1008,6 +1008,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 8c98d8c7fef7a7..1d57cb8779d7d0 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1698,6 +1698,9 @@ class Sema final {
ChangedStateAtExit
};
+ void DiagnoseMissingFormatAttributes(const FunctionDecl *FDecl,
+ ArrayRef<const Expr *> Args,
+ SourceLocation Loc);
void DiagnoseNonDefaultPragmaAlignPack(PragmaAlignPackDiagnoseKind Kind,
SourceLocation IncludeLoc);
void DiagnoseUnterminatedPragmaAlignPack();
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 3dcd18b3afc8b4..77e01ba344e5a5 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -8034,8 +8034,10 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
}
}
- if (FD)
+ if (FD) {
diagnoseArgDependentDiagnoseIfAttrs(FD, ThisArg, Args, Loc);
+ DiagnoseMissingFormatAttributes(FD, Args, Range.getBegin());
+ }
}
/// CheckConstructorCall - Check a constructor call for correctness and safety
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 8bce04640e748e..281cae2bfa52de 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -159,6 +159,13 @@ static bool isInstanceMethod(const Decl *D) {
return false;
}
+static bool checkIfMethodHasImplicitThisParam(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>();
@@ -308,7 +315,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 = checkIfMethodHasImplicitThisParam(D);
bool IV = HP && isFunctionOrMethodVariadic(D);
unsigned NumParams =
(HP ? getFunctionOrMethodNumParams(D) : 0) + HasImplicitThisParam;
@@ -4008,7 +4015,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 = checkIfMethodHasImplicitThisParam(D);
unsigned NumArgs = getFunctionOrMethodNumParams(D) + HasImplicitThisParam;
IdentifierInfo *II = AL.getArgAsIdent(0)->Ident;
@@ -4122,7 +4129,7 @@ static void handleCallbackAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
return;
}
- bool HasImplicitThisParam = isInstanceMethod(D);
+ bool HasImplicitThisParam = checkIfMethodHasImplicitThisParam(D);
int32_t NumArgs = getFunctionOrMethodNumParams(D);
FunctionDecl *FD = D->getAsFunction();
@@ -7116,6 +7123,110 @@ 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, format attribute argument
+ // indexing starts from 2. Otherwise, it starts from 1.
+ unsigned int FormatArgumentIndexOffset =
+ checkIfMethodHasImplicitThisParam(FDecl) ? 2 : 1;
+ unsigned int ParentFunctionFormatArgumentIndexOffset =
+ checkIfMethodHasImplicitThisParam(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().getAsString() != "va_list")
+ 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.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 00000000000000..ea475a90c6e40b
--- /dev/null
+++ b/clang/test/Sema/attr-format-missing.c
@@ -0,0 +1,223 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wmissing-format-attribute %s
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <uchar.h>
+#include <wchar.h>
+
+__attribute__((__format__ (__scanf__, 1, 4)))
+void f1(char *out, const size_t len, const char *format, ... /* args */)
+{
+ va_list args;
+ vsnprintf(out, len, format, args); // expected-warning {{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 */)
+{
+ va_list args;
+ vsnprintf(out, len, format, args); // expected-warning {{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)
+{
+ vprintf(out, args); // expected-warning {{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 {{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 */)
+{
+ va_list args;
+ vprintf("test", args); // no warning
+
+ const char *ch;
+ vscanf(ch, args); // no warning
+}
+
+void f5(va_list args)
+{
+ char *ch;
+ vscanf(ch, args); // no warning
+}
+
+void f6(char *out, va_list args)
+{
+ char *ch;
+ vscanf(ch, args); // no warning
+ vprintf("test", args); // no warning
+}
+
+void f7(const char *out, ... /* args */)
+{
+ va_list args;
+
+ vscanf(out, &args[0]); // expected-warning {{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 {{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 */)
+{
+ va_list args;
+
+ vscanf(out, &args[0]); // no warning
+ vprintf(out, &args[0]); // no warning
+}
+
+void f9(const char out[], ... /* args */)
+{
+ va_list args;
+ vscanf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'scanf' format attribute to the declaration of 'f9'}}
+ // CHECK-FIXES: __attribute__((format(scanf, 1, 2)))
+ char *ch;
+ vprintf(ch, args); // no warning
+}
+
+void f10(const wchar_t *out, ... /* args */)
+{
+ va_list args;
+ vprintf(out, args); // expected-warning {{incompatible pointer types passing 'const wchar_t *' (aka 'const int *') to parameter of type 'const char *'}}
+ // expected-warning {{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); // no warning
+ // expected-warning {{diagnostic behavior may be improved by adding the 'printf' format attribute to the declaration of 'f10'}}
+ // CHECK-FIXES: __attribute__((format(scanf, 1, 2)))
+ vscanf((char *) out, args); // no warning
+ // expected-warning {{diagnostic behavior may be improved by adding the 'printf' 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 */);
+
+void f12(const char16_t *out, ... /* args */)
+{
+ 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-warning {{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 */);
+
+void f14(const char32_t *out, ... /* args */)
+{
+ 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-warning {{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 */);
+
+void f16(const unsigned char *out, ... /* args */)
+{
+ 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-warning {{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 {{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 {{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 */)
+{
+ 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}}
+ vscanf((const char *) out, args); // expected-warning {{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 {{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 */)
+{
+ 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-warning {{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 {{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 {{diagnostic behavior may be improved by adding the 'scanf' 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 */)
+{
+ 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-warning {{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 {{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 {{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 */)
+{
+ 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}}
+ 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-warning {{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) {
+ va_list args;
+ const char* ch;
+ vsprintf(out, ch, args); // no warning
+ vscanf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'printf' format attribute to the declaration of 'f21'}}
+ // CHECK-FIXES: __attribute__((format(scanf, 1, 0)))
+}
+
+void f22(const char *out, ... /* args */)
+{
+ int a;
+ printf(out, a); // expected-warning {{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 {{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 */)
+{
+ int a;
+ printf(out, a); // no warning
+ printf(out, 1); // no warning
+}
+
+void f24(char* ch, const char *out, ... /* args */)
+{
+ va_list args;
+ printf(ch, args); // expected-warning {{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 {{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 {{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 {{diagnostic behavior may be improved by adding the 'printf' format attribute to the declaration of 'f24'}}
+ // CHECK-FIXES: __attribute__((format(printf, 2, 3)))
+}
diff --git a/clang/test/Sema/attr-format-missing.cpp b/clang/test/Sema/attr-format-missing.cpp
new file mode 100644
index 00000000000000..4c469f111e95c9
--- /dev/null
+++ b/clang/test/Sema/attr-format-missing.cpp
@@ -0,0 +1,238 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wmissing-format-attribute %s
+
+#include <iostream>
+#include <cstdarg>
+
+void f1(const std::string &str, ... /* args */)
+{
+ 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 */);
+
+void f3(std::string_view str, ... /* args */)
+{
+ 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 */);
+
+void f5(const std::wstring &str, ... /* args */)
+{
+ 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 */);
+
+void f7(std::wstring_view str, ... /* args */)
+{
+ 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 */);
+
+void f9(const wchar_t *out, ... /* args */)
+{
+ va_list args;
+ vprintf(out, args); // expected-error {{no matching function for call to 'vprintf'}}
+ vscanf((const char *) out, args); // expected-warning {{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 {{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 */);
+
+void f11(const char16_t *out, ... /* args */)
+{
+ va_list args;
+ vscanf(out, args); // expected-error {{no matching function for call to 'vscanf'}}
+}
+
+__attribute__((format(printf, 1, 2))) // expected-error {{format argument not a string type}}
+void f12(const char16_t *out, ... /* args */);
+
+void f13(const char32_t *out, ... /* args */)
+{
+ va_list args;
+ vscanf(out, args); // expected-error {{no matching function for call to 'vscanf'}}
+}
+
+__attribute__((format(scanf, 1, 2))) // expected-error {{format argument not a string type}}
+void f14(const char32_t *out, ... /* args */);
+
+void f15(const char *out, ... /* args */)
+{
+ va_list args;
+ vscanf(out, args); // expected-warning {{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 */)
+{
+ va_list args;
+ vscanf(out, args); // no warning
+}
+
+void f17(const unsigned char *out, ... /* args */)
+{
+ va_list args;
+ vscanf(out, args); // expected-error {{no matching function for call to 'vscanf'}}
+}
+
+__attribute__((format(scanf, 1, 2)))
+void f18(const unsigned char *out, ... /* args */)
+{
+ va_list args;
+ vprintf(out, args); // expected-error {{no matching function for call to 'vprintf'}}
+}
+
+void f19(const signed char *out, ... /* args */)
+{
+ va_list args;
+ vprintf(out, args); // expected-error {{no matching function for call to 'vprintf'}}
+}
+
+__attribute__((format(scanf, 1, 2)))
+void f20(const signed char *out, ... /* args */)
+{
+ va_list args;
+ vscanf(out, args); // expected-error {{no matching function for call to 'vscanf'}}
+}
+
+void f21(const char out[], ... /* args */)
+{
+ va_list args;
+ vscanf(out, args); // expected-warning {{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 */)
+{
+ va_list args;
+ vscanf(out, args); // no warning
+}
+
+void f23(const char *out)
+{
+ va_list args;
+ vscanf(out, args); // expected-warning {{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)
+{
+ vprintf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'printf' format attribute to the declaration of 'f24'}}
+ // CHECK-FIXES: __attribute__((format(printf, 1, 0)))
+}
+
+struct S1
+{
+ void fn1(const char *out, ... /* args */)
+ {
+ va_list args;
+ vscanf(out, args); // expected-warning {{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);
+
+ void fn3(const char *out, ... /* args */);
+
+ void fn4(this S1& expliciteThis, const char *out, va_list args)
+ {
+ expliciteThis.fn2(out, args); // expected-warning {{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 */)
+{
+ va_list args;
+ fn2(out, args); // expected-warning {{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);
+
+ void fn2(const char *out, ... /* args */)
+ {
+ va_list args;
+ fn1(out, args); // expected-warning {{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)
+ {
+ va_list args;
+ printf(out, args); // expected-warning {{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);
+
+ void fn2(const char *out, const int n, ... /* args */)
+ {
+ va_list args;
+ fn1(n, out, args); // expected-warning {{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)
+ {
+ scanf(out, args); // expected-warning {{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)
+ {
+ va_list args;
+ fn1(n, out, args); // expected-warning {{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, ...)
+ {
+ va_list args;
+ printf(out, args); // expected-warning {{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
+}
More information about the cfe-commits
mailing list