diff options
author | YingChi Long <me@inclyc.cn> | 2022-08-03 10:07:31 +0800 |
---|---|---|
committer | YingChi Long <me@inclyc.cn> | 2022-08-04 21:07:30 +0800 |
commit | f417583f319bd60d1e32cdf9d0242e42f86101bf (patch) | |
tree | 000ade2999b3011d8c60c2e608f04bd066f88926 /clang/lib/Sema/SemaChecking.cpp | |
parent | b8985ba0adb5fd881bb301fbc79370b86396e658 (diff) | |
download | llvm-f417583f319bd60d1e32cdf9d0242e42f86101bf.zip llvm-f417583f319bd60d1e32cdf9d0242e42f86101bf.tar.gz llvm-f417583f319bd60d1e32cdf9d0242e42f86101bf.tar.bz2 |
[clang] format string checking for conpile-time evaluated str literal
This patch enhances clang's ability to check compile-time determinable
string literals as format strings, and can give FixIt hints at literals
(unlike gcc). Issue https://github.com/llvm/llvm-project/issues/55805
mentiond two compile-time string cases. And this patch partially fixes
one.
```
constexpr const char* foo() {
return "%s %d";
}
int main() {
printf(foo(), "abc", "def");
return 0;
}
```
This patch enables clang check format string for this:
```
<source>:4:24: warning: format specifies type 'int' but the argument has type 'const char *' [-Wformat]
printf(foo(), "abc", "def");
~~~~~ ^~~~~
<source>:2:42: note: format string is defined here
constexpr const char *foo() { return "%s %d"; }
^~
%s
1 warning generated.
```
Reviewed By: aaron.ballman
Signed-off-by: YingChi Long <me@inclyc.cn>
Differential Revision: https://reviews.llvm.org/D130906
Diffstat (limited to 'clang/lib/Sema/SemaChecking.cpp')
-rw-r--r-- | clang/lib/Sema/SemaChecking.cpp | 23 |
1 files changed, 22 insertions, 1 deletions
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 6b60312..6428be1 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -8473,6 +8473,9 @@ static void CheckFormatString( llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg, bool IgnoreStringsWithoutSpecifiers); +static const Expr *maybeConstEvalStringLiteral(ASTContext &Context, + const Expr *E); + // Determine if an expression is a string literal or constant string. // If this function returns false on the arguments to a function expecting a // format string, we will usually need to emit a warning. @@ -8713,7 +8716,11 @@ tryAgain: } } } - + if (const auto *SLE = maybeConstEvalStringLiteral(S.Context, E)) + return checkFormatStringExpr(S, SLE, Args, APK, format_idx, firstDataArg, + Type, CallType, /*InFunctionCall*/ false, + CheckedVarArgs, UncoveredArg, Offset, + IgnoreStringsWithoutSpecifiers); return SLCT_NotALiteral; } case Stmt::ObjCMessageExprClass: { @@ -8823,6 +8830,20 @@ tryAgain: } } +// If this expression can be evaluated at compile-time, +// check if the result is a StringLiteral and return it +// otherwise return nullptr +static const Expr *maybeConstEvalStringLiteral(ASTContext &Context, + const Expr *E) { + Expr::EvalResult Result; + if (E->EvaluateAsRValue(Result, Context) && Result.Val.isLValue()) { + const auto *LVE = Result.Val.getLValueBase().dyn_cast<const Expr *>(); + if (isa_and_nonnull<StringLiteral>(LVE)) + return LVE; + } + return nullptr; +} + Sema::FormatStringType Sema::GetFormatStringType(const FormatAttr *Format) { return llvm::StringSwitch<FormatStringType>(Format->getType()->getName()) .Case("scanf", FST_Scanf) |