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"
24 functionDecl(hasName(
"::memset"),
26 hasParameter(0, hasType(pointerType(pointee(voidType())))),
27 hasParameter(1, hasType(isInteger())),
28 hasParameter(2, hasType(isInteger())));
33 callee(MemsetDecl), argumentCountIs(3),
34 hasArgument(1, characterLiteral(equals(
static_cast<unsigned>(
'0')))
35 .bind(
"char-zero-fill")),
37 0, anyOf(hasType(pointsTo(isAnyCharacter())),
38 hasType(arrayType(hasElementType(isAnyCharacter()))))))),
44 callExpr(callee(MemsetDecl), argumentCountIs(3),
45 hasArgument(1, integerLiteral().bind(
"num-fill"))),
50 callExpr(callee(MemsetDecl), argumentCountIs(3),
51 unless(hasArgument(1, anyOf(characterLiteral(equals(
52 static_cast<unsigned>(
'0'))),
59 if (
const auto *CharZeroFill =
60 Result.Nodes.getNodeAs<CharacterLiteral>(
"char-zero-fill")) {
64 SourceRange CharRange = CharZeroFill->getSourceRange();
66 diag(CharZeroFill->getBeginLoc(),
"memset fill value is char '0', "
67 "potentially mistaken for int 0");
70 if (CharRange.getBegin().isMacroID())
72 Diag << FixItHint::CreateReplacement(
73 CharSourceRange::getTokenRange(CharRange),
"0");
76 else if (
const auto *NumFill =
77 Result.Nodes.getNodeAs<IntegerLiteral>(
"num-fill")) {
81 const auto UCharMax = (1 << Result.Context->getCharWidth()) - 1;
82 Expr::EvalResult EVResult;
83 if (!NumFill->EvaluateAsInt(EVResult, *Result.Context))
86 llvm::APSInt NumValue = EVResult.Val.getInt();
87 if (NumValue >= 0 && NumValue <= UCharMax)
90 diag(NumFill->getBeginLoc(),
"memset fill value is out of unsigned "
91 "character range, gets truncated");
94 else if (
const auto *Call = Result.Nodes.getNodeAs<CallExpr>(
"call")) {
98 const Expr *FillChar = Call->getArg(1);
99 const Expr *ByteCount = Call->getArg(2);
102 Expr::EvalResult Value2;
103 if (ByteCount->isValueDependent() ||
104 !ByteCount->EvaluateAsInt(Value2, *Result.Context) ||
105 Value2.Val.getInt() != 0)
111 Expr::EvalResult EVResult;
112 if (!FillChar->isValueDependent() &&
113 FillChar->EvaluateAsInt(EVResult, *Result.Context)) {
114 llvm::APSInt Value1 = EVResult.Val.getInt();
115 if (Value1 == 0 || Value1.isNegative())
122 auto D =
diag(Call->getBeginLoc(),
123 "memset of size zero, potentially swapped arguments");
124 StringRef RHSString = tooling::fixit::getText(*ByteCount, *Result.Context);
125 StringRef LHSString = tooling::fixit::getText(*FillChar, *Result.Context);
126 if (LHSString.empty() || RHSString.empty())
129 D << tooling::fixit::createReplacement(*FillChar, RHSString)
130 << tooling::fixit::createReplacement(*ByteCount, LHSString);
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.