clang-tools  3.9.0
MemsetZeroLengthCheck.cpp
Go to the documentation of this file.
1 //===--- MemsetZeroLengthCheck.cpp - clang-tidy -------------------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "MemsetZeroLengthCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/Lex/Lexer.h"
15 
16 using namespace clang::ast_matchers;
17 
18 namespace clang {
19 namespace tidy {
20 namespace google {
21 namespace runtime {
22 
23 void
24 MemsetZeroLengthCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
25  // Look for memset(x, y, 0) as those is most likely an argument swap.
26  // TODO: Also handle other standard functions that suffer from the same
27  // problem, e.g. memchr.
28  Finder->addMatcher(
29  callExpr(callee(functionDecl(hasName("::memset"))), argumentCountIs(3),
30  unless(isInTemplateInstantiation())).bind("decl"),
31  this);
32 }
33 
34 /// \brief Get a StringRef representing a SourceRange.
35 static StringRef getAsString(const MatchFinder::MatchResult &Result,
36  SourceRange R) {
37  const SourceManager &SM = *Result.SourceManager;
38  // Don't even try to resolve macro or include contraptions. Not worth emitting
39  // a fixit for.
40  if (R.getBegin().isMacroID() ||
41  !SM.isWrittenInSameFile(R.getBegin(), R.getEnd()))
42  return StringRef();
43 
44  const char *Begin = SM.getCharacterData(R.getBegin());
45  const char *End = SM.getCharacterData(Lexer::getLocForEndOfToken(
46  R.getEnd(), 0, SM, Result.Context->getLangOpts()));
47 
48  return StringRef(Begin, End - Begin);
49 }
50 
51 void MemsetZeroLengthCheck::check(const MatchFinder::MatchResult &Result) {
52  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("decl");
53 
54  // Note, this is:
55  // void *memset(void *buffer, int fill_char, size_t byte_count);
56  // Arg1 is fill_char, Arg2 is byte_count.
57  const Expr *Arg1 = Call->getArg(1);
58  const Expr *Arg2 = Call->getArg(2);
59 
60  // Return if `byte_count` is not zero at compile time.
61  llvm::APSInt Value1, Value2;
62  if (Arg2->isValueDependent() ||
63  !Arg2->EvaluateAsInt(Value2, *Result.Context) || Value2 != 0)
64  return;
65 
66  // Return if `fill_char` is known to be zero or negative at compile
67  // time. In these cases, swapping the args would be a nop, or
68  // introduce a definite bug. The code is likely correct.
69  if (!Arg1->isValueDependent() &&
70  Arg1->EvaluateAsInt(Value1, *Result.Context) &&
71  (Value1 == 0 || Value1.isNegative()))
72  return;
73 
74  // `byte_count` is known to be zero at compile time, and `fill_char` is
75  // either not known or known to be a positive integer. Emit a warning
76  // and fix-its to swap the arguments.
77  auto D = diag(Call->getLocStart(),
78  "memset of size zero, potentially swapped arguments");
79  SourceRange LHSRange = Arg1->getSourceRange();
80  SourceRange RHSRange = Arg2->getSourceRange();
81  StringRef RHSString = getAsString(Result, RHSRange);
82  StringRef LHSString = getAsString(Result, LHSRange);
83  if (LHSString.empty() || RHSString.empty())
84  return;
85 
86  D << FixItHint::CreateReplacement(CharSourceRange::getTokenRange(LHSRange),
87  RHSString)
88  << FixItHint::CreateReplacement(CharSourceRange::getTokenRange(RHSRange),
89  LHSString);
90 }
91 
92 } // namespace runtime
93 } // namespace google
94 } // namespace tidy
95 } // namespace clang
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:210
SourceManager & SM
static StringRef getAsString(const MatchFinder::MatchResult &Result, SourceRange R)
Get a StringRef representing a SourceRange.
const NamedDecl * Result
Definition: USRFinder.cpp:137