[clang] [clang] Catch missing format attributes (PR #70024)
Budimir Aranđelović via cfe-commits
cfe-commits at lists.llvm.org
Wed Dec 6 04:43:30 PST 2023
https://github.com/budimirarandjelovicsyrmia updated https://github.com/llvm/llvm-project/pull/70024
>From 9d49cf51be0ad67f57c862ef70221d37c727033f Mon Sep 17 00:00:00 2001
From: budimirarandjelovicsyrmia <budimir.arandjelovic at syrmia.com>
Date: Fri, 13 Oct 2023 14:45:15 +0200
Subject: [PATCH] [clang] Catch missing format attributes
---
clang/docs/ReleaseNotes.rst | 4 +-
clang/include/clang/Basic/DiagnosticGroups.td | 2 +-
.../clang/Basic/DiagnosticSemaKinds.td | 3 +
clang/include/clang/Sema/Sema.h | 4 ++
clang/lib/Sema/SemaChecking.cpp | 4 +-
clang/lib/Sema/SemaDeclAttr.cpp | 65 +++++++++++++++++++
clang/test/Sema/attr-format-missing.c | 58 +++++++++++++++++
7 files changed, 137 insertions(+), 3 deletions(-)
create mode 100644 clang/test/Sema/attr-format-missing.c
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9cc2a72f4c864..1ca055f65f211 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -65,7 +65,9 @@ Improvements to Clang's diagnostics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- We now generate a diagnostic for signed integer overflow due to unary minus
in a non-constant expression context. This fixes
- `Issue 31643 <https://github.com/llvm/llvm-project/issues/31643>`_
+ `Issue 31643 <https://github.com/llvm/llvm-project/issues/31643>`_.
+- We now generate a diagnostic for missing format attributes
+ `Issue 60718 <https://github.com/llvm/llvm-project/issues/60718>`_.
Non-comprehensive list of changes in this release
-------------------------------------------------
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 17fdcffa2d427..b8b77df84beb2 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -482,7 +482,7 @@ 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 MissingFormatAttribute: 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 6d6f474f6dcda..3cd2270b2fc41 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -936,6 +936,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<MissingFormatAttribute>, 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 741c2503127af..064506e709603 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10615,6 +10615,10 @@ 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 4602284309491..d3ac6cb519c56 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -6014,8 +6014,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 1a0bfb3d91bcc..87235a2975610 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6849,6 +6849,71 @@ static void handleSwiftAsyncAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
checkSwiftAsyncErrorBlock(S, D, ErrorAttr, AsyncAttr);
}
+// Warn if parent function misses format attribute. Parent function misses
+// format attribute if there is an argument format string forwarded to calling
+// function with format attribute, parent function has a parameter which is
+// either char or string or pointer to char and parent function format attribute
+// type does not match with calling function format attribute type.
+void Sema::DiagnoseMissingFormatAttributes(const FunctionDecl *FDecl,
+ ArrayRef<const Expr *> Args,
+ SourceLocation Loc) {
+ assert(FDecl);
+
+ // Check if function has format attribute with forwarded format string.
+ IdentifierInfo *AttrType;
+ if (!llvm::any_of(
+ FDecl->specific_attrs<FormatAttr>(), [&](const FormatAttr *Attr) {
+ if (!Args[Attr->getFirstArg()]->getReferencedDeclOfCallee())
+ return false;
+
+ AttrType = Attr->getType();
+ return true;
+ }))
+ return;
+
+ const FunctionDecl *ParentFuncDecl = getCurFunctionDecl();
+ if (!ParentFuncDecl)
+ return;
+
+ // Check if parent function has char, string or pointer to char parameter.
+ unsigned int StringIndex = 0;
+ if (!llvm::any_of(
+ ParentFuncDecl->parameters(), [&](const ParmVarDecl *Param) {
+ StringIndex = Param->getFunctionScopeIndex() + 1;
+ QualType Ty = Param->getType();
+ if (isNSStringType(Ty, Context, true))
+ return true;
+ if (isCFStringType(Ty, Context))
+ return true;
+ if (Ty->isPointerType() &&
+ Ty->castAs<PointerType>()->getPointeeType()->isCharType())
+ return true;
+ return false;
+ }))
+ return;
+
+ // Check if there is parent function format attribute which type matches
+ // calling function format attribute type.
+ if (llvm::any_of(
+ ParentFuncDecl->specific_attrs<FormatAttr>(),
+ [&](const FormatAttr *Attr) { return Attr->getType() == AttrType; }))
+ return;
+
+ unsigned int FirstToCheck =
+ ParentFuncDecl->isVariadic() ? (ParentFuncDecl->getNumParams() + 1) : 0;
+
+ // Diagnose missing format attributes
+ std::string InsertionText;
+ llvm::raw_string_ostream OS(InsertionText);
+ OS << "__attribute__((format(" + AttrType->getName() + ", " +
+ std::to_string(StringIndex) + ", " + std::to_string(FirstToCheck) +
+ ")))";
+ SourceLocation ParentFuncLoc = ParentFuncDecl->getLocation();
+ Diag(ParentFuncLoc, diag::warn_missing_format_attribute)
+ << AttrType << ParentFuncDecl
+ << FixItHint::CreateInsertion(ParentFuncLoc, InsertionText);
+}
+
//===----------------------------------------------------------------------===//
// 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..1384fde6eecf7
--- /dev/null
+++ b/clang/test/Sema/attr-format-missing.c
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wmissing-format-attribute %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c99 -Wmissing-format-attribute %s
+
+#include <stdarg.h>
+#include <stdio.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, 1, 4)))
+}
+
+void f2(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 'f2'}}
+ // 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 'f2'}}
+ // CHECK-FIXES: __attribute__((format(scanf, 1, 0)))
+}
+
+void f3(char* out, ... /* args */)
+{
+ va_list args;
+ vprintf("test", args); // no warning
+}
+
+void f4(char *out, ... /* args */)
+{
+ const char *ch;
+ va_list args;
+ vscanf(ch, args); // expected-warning {{diagnostic behavior may be improved by adding the 'scanf' format attribute to the declaration of 'f4'}}
+ // CHECK-FIXES: __attribute__((format(scanf, 1, 2)))
+}
+
+void f5(va_list args)
+{
+ char *ch;
+ vscanf(ch, args); // no warning
+}
+
+void f6(char *out, va_list args)
+{
+ char *ch;
+ vscanf(ch, args); // expected-warning {{diagnostic behavior may be improved by adding the 'scanf' format attribute to the declaration of 'f6'}}
+ // CHECK-FIXES: __attribute__((format(scanf, 1, 0)))
+}
+
+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, 2)))
+ 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, 2)))
+}
\ No newline at end of file
More information about the cfe-commits
mailing list