10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/Lex/Lexer.h"
14 #include "clang/Tooling/FixIt.h"
22 void SuspiciousMemsetUsageCheck::registerMatchers(MatchFinder *Finder) {
26 functionDecl(hasName(
"::memset"),
28 hasParameter(0, hasType(pointerType(pointee(voidType())))),
29 hasParameter(1, hasType(isInteger())),
30 hasParameter(2, hasType(isInteger())));
36 hasArgument(1, characterLiteral(equals(static_cast<unsigned>(
'0')))
37 .bind(
"char-zero-fill")),
39 eachOf(hasArgument(0, anyOf(hasType(pointsTo(isAnyCharacter())),
40 hasType(arrayType(hasElementType(
41 isAnyCharacter()))))),
42 isInTemplateInstantiation()))),
47 Finder->addMatcher(callExpr(callee(MemsetDecl),
48 hasArgument(1, integerLiteral().bind(
"num-fill")),
49 unless(isInTemplateInstantiation())),
54 callExpr(callee(MemsetDecl),
55 unless(hasArgument(1, anyOf(characterLiteral(equals(
56 static_cast<unsigned>(
'0'))),
58 unless(isInTemplateInstantiation()))
63 void SuspiciousMemsetUsageCheck::check(
const MatchFinder::MatchResult &Result) {
64 if (
const auto *CharZeroFill =
65 Result.Nodes.getNodeAs<CharacterLiteral>(
"char-zero-fill")) {
69 SourceRange CharRange = CharZeroFill->getSourceRange();
71 diag(CharZeroFill->getBeginLoc(),
"memset fill value is char '0', "
72 "potentially mistaken for int 0");
75 if (CharRange.getBegin().isMacroID())
77 Diag << FixItHint::CreateReplacement(
78 CharSourceRange::getTokenRange(CharRange),
"0");
81 else if (
const auto *NumFill =
82 Result.Nodes.getNodeAs<IntegerLiteral>(
"num-fill")) {
86 const auto UCharMax = (1 << Result.Context->getCharWidth()) - 1;
87 Expr::EvalResult EVResult;
88 if (!NumFill->EvaluateAsInt(EVResult, *Result.Context))
91 llvm::APSInt NumValue = EVResult.Val.getInt();
92 if (NumValue >= 0 && NumValue <= UCharMax)
95 diag(NumFill->getBeginLoc(),
"memset fill value is out of unsigned "
96 "character range, gets truncated");
99 else if (
const auto *Call = Result.Nodes.getNodeAs<CallExpr>(
"call")) {
103 const Expr *FillChar = Call->getArg(1);
104 const Expr *ByteCount = Call->getArg(2);
107 Expr::EvalResult Value2;
108 if (ByteCount->isValueDependent() ||
109 !ByteCount->EvaluateAsInt(Value2, *Result.Context) ||
110 Value2.Val.getInt() != 0)
116 Expr::EvalResult EVResult;
117 if (!FillChar->isValueDependent() &&
118 FillChar->EvaluateAsInt(EVResult, *Result.Context)) {
119 llvm::APSInt Value1 = EVResult.Val.getInt();
120 if (Value1 == 0 || Value1.isNegative())
127 auto D = diag(Call->getBeginLoc(),
128 "memset of size zero, potentially swapped arguments");
129 StringRef RHSString = tooling::fixit::getText(*ByteCount, *Result.Context);
130 StringRef LHSString = tooling::fixit::getText(*FillChar, *Result.Context);
131 if (LHSString.empty() || RHSString.empty())
134 D << tooling::fixit::createReplacement(*FillChar, RHSString)
135 << tooling::fixit::createReplacement(*ByteCount, LHSString);