[clang] [clang] Catch missing format attributes (PR #70024)
Budimir Aranđelović via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 24 03:20:12 PDT 2023
https://github.com/budimirarandjelovicsyrmia created https://github.com/llvm/llvm-project/pull/70024
Enable flag -Wmissing-format-attribute to catch missing attributes
>From 9d0b6ae67776a9adf9ca3eee64b2e01de9f07af4 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/include/clang/Basic/DiagnosticGroups.td | 2 +-
.../clang/Basic/DiagnosticSemaKinds.td | 3 +
clang/include/clang/Sema/Sema.h | 2 +
clang/lib/Sema/SemaAttr.cpp | 80 +++++++++++++++++++
clang/lib/Sema/SemaChecking.cpp | 4 +-
clang/test/Sema/attr-format-missing.c | 13 +++
6 files changed, 102 insertions(+), 2 deletions(-)
create mode 100644 clang/test/Sema/attr-format-missing.c
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 17fdcffa2d42740..b8b77df84beb2be 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 6d6f474f6dcdab9..7125de143c1710e 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<
+ "Function %0 might be candidate for %1 format attribute.">,
+ 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 741c2503127af7a..7041a309e5df220 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10615,6 +10615,8 @@ class Sema final {
ChangedStateAtExit
};
+ void DiagnoseMissingFormatAttributes(NamedDecl *FDecl, SourceLocation Loc);
+
void DiagnoseNonDefaultPragmaAlignPack(PragmaAlignPackDiagnoseKind Kind,
SourceLocation IncludeLoc);
void DiagnoseUnterminatedPragmaAlignPack();
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 42f582724564d06..0a61748f6e20176 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -435,6 +435,86 @@ bool Sema::ConstantFoldAttrArgs(const AttributeCommonInfo &CI,
return true;
}
+// Warn if parent function does not have builtin function format attribute.
+void Sema::DiagnoseMissingFormatAttributes(NamedDecl *FDecl,
+ SourceLocation Loc) {
+ if (!FDecl)
+ return;
+
+ auto *FD = dyn_cast_or_null<FunctionDecl>(FDecl);
+ if (!FD)
+ return;
+
+ unsigned BuiltinID = FD->getBuiltinID(/*ConsiderWrappers=*/true);
+
+ // Function is not builtin if it's builtin ID is 0.
+ if (!BuiltinID)
+ return;
+
+ // Check if function is one with format attribute.
+ switch (BuiltinID) {
+ case Builtin::BIprintf:
+ case Builtin::BIfprintf:
+ case Builtin::BIsprintf:
+ case Builtin::BIscanf:
+ case Builtin::BIfscanf:
+ case Builtin::BIsscanf:
+ case Builtin::BIvprintf:
+ case Builtin::BIvfprintf:
+ case Builtin::BIvsprintf:
+ break;
+ default: {
+ // In C99 mode check functions below.
+ if (!getLangOpts().C99)
+ return;
+ switch (BuiltinID) {
+ case Builtin::BIsnprintf:
+ case Builtin::BIvsnprintf:
+ case Builtin::BIvscanf:
+ case Builtin::BIvfscanf:
+ case Builtin::BIvsscanf:
+ break;
+ default:
+ return;
+ }
+ }
+ }
+
+ Scope *ParentScope = getCurScope() ? getCurScope()->getFnParent() : nullptr;
+ if (!ParentScope)
+ return;
+
+ DeclContext *ParentScopeEntity = ParentScope->getEntity();
+ if (!ParentScopeEntity)
+ return;
+ if (ParentScopeEntity->getDeclKind() != Decl::Kind::Function)
+ return;
+
+ FunctionDecl *ParentFuncDecl = static_cast<FunctionDecl *>(ParentScopeEntity);
+ if (!ParentFuncDecl)
+ return;
+ if (!ParentFuncDecl->isVariadic())
+ return;
+
+ // Iterate through builtin function format attributes. Then check
+ // if parent function has these attributes. If parent function does
+ // not have builtin function format attribut, emit warning.
+ for (const FormatAttr *Attr : FD->specific_attrs<FormatAttr>()) {
+ bool hasFormatAttr = false;
+ for (const FormatAttr *ParentAttr :
+ ParentFuncDecl->specific_attrs<FormatAttr>()) {
+ if (ParentAttr->getType() == Attr->getType()) {
+ hasFormatAttr = true;
+ break;
+ }
+ }
+ if (!hasFormatAttr) {
+ Diag(Loc, diag::warn_missing_format_attribute)
+ << ParentFuncDecl << Attr->getType();
+ }
+ }
+}
+
void Sema::DiagnoseNonDefaultPragmaAlignPack(PragmaAlignPackDiagnoseKind Kind,
SourceLocation IncludeLoc) {
if (Kind == PragmaAlignPackDiagnoseKind::NonDefaultStateAtInclude) {
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 4602284309491c1..f2dd455c667a43b 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(FDecl, Range.getBegin());
+ }
}
/// CheckConstructorCall - Check a constructor call for correctness and safety
diff --git a/clang/test/Sema/attr-format-missing.c b/clang/test/Sema/attr-format-missing.c
new file mode 100644
index 000000000000000..131d5d50d31d10b
--- /dev/null
+++ b/clang/test/Sema/attr-format-missing.c
@@ -0,0 +1,13 @@
+// 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>
+
+va_list args;
+
+__attribute__((__format__ (__scanf__, 1, 4)))
+void foo(char *out, const size_t len, const char *format, ... /* args */)
+{
+ vsnprintf(out, len, format, args); // expected-warning {{Function 'foo' might be candidate for 'printf' format attribute}}
+}
More information about the cfe-commits
mailing list