================
@@ -7001,6 +7005,85 @@ bool Sema::CheckFormatString(const FormatMatchesAttr
*Format,
return false;
}
+static void CheckMissingFormatAttributes(Sema *S, FormatStringType FormatType,
+ unsigned FormatIdx, unsigned FirstArg,
+ ArrayRef<const Expr *> Args,
+ Sema::FormatArgumentPassingKind APK,
+ unsigned CallerParamIdx,
+ SourceLocation Loc) {
+ const FunctionDecl *Caller = S->getCurFunctionDecl();
+ if (!Caller)
+ return;
+
+ // Find the offset to convert between attribute and parameter indexes.
+ unsigned CallerArgumentIndexOffset =
+ hasImplicitObjectParameter(Caller) ? 2 : 1;
+
+ unsigned FirstArgumentIndex = -1;
+ switch (APK) {
+ case Sema::FormatArgumentPassingKind::FAPK_Fixed:
+ case Sema::FormatArgumentPassingKind::FAPK_Variadic: {
+ // As an extension, clang allows the format attribute on non-variadic
+ // functions.
+ // Caller must have fixed arguments to pass them to a fixed or variadic
+ // function. Try to match caller and callee arguments. If successful, then
+ // emit a diag with the caller idx, otherwise we can't determine the callee
+ // arguments.
+ unsigned NumCalleeArgs = Args.size() - FirstArg;
+ if (NumCalleeArgs == 0 || Caller->getNumParams() < NumCalleeArgs) {
+ // There aren't enough arugments in the caller to pass to callee.
+ return;
+ }
+ for (unsigned CalleeIdx = Args.size() - 1,
+ CallerIdx = Caller->getNumParams() - 1;
+ CalleeIdx >= FirstArg; --CalleeIdx, --CallerIdx) {
+ const auto *Arg =
+ dyn_cast<DeclRefExpr>(Args[CalleeIdx]->IgnoreParenCasts());
+ if (!Arg)
+ return;
+ const auto *Param = dyn_cast<ParmVarDecl>(Arg->getDecl());
+ if (!Param || Param->getFunctionScopeIndex() != CallerIdx)
+ return;
+ }
+ FirstArgumentIndex =
+ Caller->getNumParams() + CallerArgumentIndexOffset - NumCalleeArgs;
+ break;
+ }
+ case Sema::FormatArgumentPassingKind::FAPK_VAList:
+ // Caller arguments are either variadic or a va_list.
+ FirstArgumentIndex =
+ Caller->isVariadic()
+ ? (Caller->getNumParams() + CallerArgumentIndexOffset)
+ : 0;
+ break;
+ case Sema::FormatArgumentPassingKind::FAPK_Elsewhere:
+ // Args are not passed to the callee.
----------------
vvuksanovic wrote:
Looking further into this, I can suggest `format_matches` in a diagnostic
message, but there are some problematic cases for creating the actual format
string parameter to `format_matches` from the call arguments. From the
attribute docs:
> While checks for the format attribute tolerate sone size mismatches that
> standard argument promotion renders immaterial (such as formatting an int
> with %hhd, which specifies a char-sized integer), checks for format_matches
> require specified argument sizes to match exactly.
In this case the actual format string might be "%d", but pass a char argument.
That is correct because of arg promotion, but based on the arg type, I would
deduce this as "%c".
> Format strings expecting a variable modifier (such as %*s) are incompatible
> with format strings that would itemize the variable modifiers (such as %i
> %s), even if the two specify ABI-compatible argument lists.
This is a bigger problem as it can't be deduced only based on the arguments.
> All pointer specifiers, modifiers aside, are mutually incompatible. For
> instance, %s is not compatible with %p, and %p is not compatible with %n, and
> %hhn is incompatible with %s, even if the pointers are ABI-compatible or
> identical on the selected platform.
Similarly, some pointer type specifiers can't be determined based on the
argument type. For example `const char *` could be printed as '%s' or '%p'.
Should I just emit a diagnostic without specific attribute parameters in this
case?
https://github.com/llvm/llvm-project/pull/166738
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits