clang-tools  10.0.0git
NotNullTerminatedResultCheck.cpp
Go to the documentation of this file.
1 //===--- NotNullTerminatedResultCheck.cpp - clang-tidy ----------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Frontend/CompilerInstance.h"
13 #include "clang/Lex/Lexer.h"
14 #include "clang/Lex/PPCallbacks.h"
15 
16 using namespace clang::ast_matchers;
17 
18 namespace clang {
19 namespace tidy {
20 namespace bugprone {
21 
22 constexpr llvm::StringLiteral FunctionExprName = "FunctionExpr";
23 constexpr llvm::StringLiteral CastExprName = "CastExpr";
24 constexpr llvm::StringLiteral UnknownDestName = "UnknownDest";
25 constexpr llvm::StringLiteral DestArrayTyName = "DestArrayTy";
26 constexpr llvm::StringLiteral DestVarDeclName = "DestVarDecl";
27 constexpr llvm::StringLiteral DestMallocExprName = "DestMalloc";
28 constexpr llvm::StringLiteral DestExprName = "DestExpr";
29 constexpr llvm::StringLiteral SrcVarDeclName = "SrcVarDecl";
30 constexpr llvm::StringLiteral SrcExprName = "SrcExpr";
31 constexpr llvm::StringLiteral LengthExprName = "LengthExpr";
32 constexpr llvm::StringLiteral WrongLengthExprName = "WrongLength";
33 constexpr llvm::StringLiteral UnknownLengthName = "UnknownLength";
34 
36 
37 namespace {
38 static Preprocessor *PP;
39 } // namespace
40 
41 // Returns the expression of destination's capacity which is part of a
42 // 'VariableArrayType', 'ConstantArrayTypeLoc' or an argument of a 'malloc()'
43 // family function call.
44 static const Expr *getDestCapacityExpr(const MatchFinder::MatchResult &Result) {
45  if (const auto *DestMalloc = Result.Nodes.getNodeAs<Expr>(DestMallocExprName))
46  return DestMalloc;
47 
48  if (const auto *DestVAT =
49  Result.Nodes.getNodeAs<VariableArrayType>(DestArrayTyName))
50  return DestVAT->getSizeExpr();
51 
52  if (const auto *DestVD = Result.Nodes.getNodeAs<VarDecl>(DestVarDeclName))
53  if (const TypeLoc DestTL = DestVD->getTypeSourceInfo()->getTypeLoc())
54  if (const auto DestCTL = DestTL.getAs<ConstantArrayTypeLoc>())
55  return DestCTL.getSizeExpr();
56 
57  return nullptr;
58 }
59 
60 // Returns the length of \p E as an 'IntegerLiteral' or a 'StringLiteral'
61 // without the null-terminator.
62 static unsigned getLength(const Expr *E,
63  const MatchFinder::MatchResult &Result) {
64  if (!E)
65  return 0;
66 
67  Expr::EvalResult Length;
68  E = E->IgnoreImpCasts();
69 
70  if (const auto *LengthDRE = dyn_cast<DeclRefExpr>(E))
71  if (const auto *LengthVD = dyn_cast<VarDecl>(LengthDRE->getDecl()))
72  if (!isa<ParmVarDecl>(LengthVD))
73  if (const Expr *LengthInit = LengthVD->getInit())
74  if (LengthInit->EvaluateAsInt(Length, *Result.Context))
75  return Length.Val.getInt().getZExtValue();
76 
77  if (const auto *LengthIL = dyn_cast<IntegerLiteral>(E))
78  return LengthIL->getValue().getZExtValue();
79 
80  if (const auto *StrDRE = dyn_cast<DeclRefExpr>(E))
81  if (const auto *StrVD = dyn_cast<VarDecl>(StrDRE->getDecl()))
82  if (const Expr *StrInit = StrVD->getInit())
83  if (const auto *StrSL =
84  dyn_cast<StringLiteral>(StrInit->IgnoreImpCasts()))
85  return StrSL->getLength();
86 
87  if (const auto *SrcSL = dyn_cast<StringLiteral>(E))
88  return SrcSL->getLength();
89 
90  return 0;
91 }
92 
93 // Returns the capacity of the destination array.
94 // For example in 'char dest[13]; memcpy(dest, ...)' it returns 13.
95 static int getDestCapacity(const MatchFinder::MatchResult &Result) {
96  if (const auto *DestCapacityExpr = getDestCapacityExpr(Result))
97  return getLength(DestCapacityExpr, Result);
98 
99  return 0;
100 }
101 
102 // Returns the 'strlen()' if it is the given length.
103 static const CallExpr *getStrlenExpr(const MatchFinder::MatchResult &Result) {
104  if (const auto *StrlenExpr =
105  Result.Nodes.getNodeAs<CallExpr>(WrongLengthExprName))
106  if (const Decl *D = StrlenExpr->getCalleeDecl())
107  if (const FunctionDecl *FD = D->getAsFunction())
108  if (const IdentifierInfo *II = FD->getIdentifier())
109  if (II->isStr("strlen") || II->isStr("wcslen"))
110  return StrlenExpr;
111 
112  return nullptr;
113 }
114 
115 // Returns the length which is given in the memory/string handler function.
116 // For example in 'memcpy(dest, "foobar", 3)' it returns 3.
117 static int getGivenLength(const MatchFinder::MatchResult &Result) {
118  if (Result.Nodes.getNodeAs<Expr>(UnknownLengthName))
119  return 0;
120 
121  if (int Length =
122  getLength(Result.Nodes.getNodeAs<Expr>(WrongLengthExprName), Result))
123  return Length;
124 
125  if (int Length =
126  getLength(Result.Nodes.getNodeAs<Expr>(LengthExprName), Result))
127  return Length;
128 
129  // Special case, for example 'strlen("foo")'.
130  if (const CallExpr *StrlenCE = getStrlenExpr(Result))
131  if (const Expr *Arg = StrlenCE->getArg(0)->IgnoreImpCasts())
132  if (int ArgLength = getLength(Arg, Result))
133  return ArgLength;
134 
135  return 0;
136 }
137 
138 // Returns a string representation of \p E.
139 static StringRef exprToStr(const Expr *E,
140  const MatchFinder::MatchResult &Result) {
141  if (!E)
142  return "";
143 
144  return Lexer::getSourceText(
145  CharSourceRange::getTokenRange(E->getSourceRange()),
146  *Result.SourceManager, Result.Context->getLangOpts(), 0);
147 }
148 
149 // Returns the proper token based end location of \p E.
150 static SourceLocation exprLocEnd(const Expr *E,
151  const MatchFinder::MatchResult &Result) {
152  return Lexer::getLocForEndOfToken(E->getEndLoc(), 0, *Result.SourceManager,
153  Result.Context->getLangOpts());
154 }
155 
156 //===----------------------------------------------------------------------===//
157 // Rewrite decision helper functions.
158 //===----------------------------------------------------------------------===//
159 
160 // Increment by integer '1' can result in overflow if it is the maximal value.
161 // After that it would be extended to 'size_t' and its value would be wrong,
162 // therefore we have to inject '+ 1UL' instead.
163 static bool isInjectUL(const MatchFinder::MatchResult &Result) {
164  return getGivenLength(Result) == std::numeric_limits<int>::max();
165 }
166 
167 // If the capacity of the destination array is unknown it is denoted as unknown.
168 static bool isKnownDest(const MatchFinder::MatchResult &Result) {
169  return !Result.Nodes.getNodeAs<Expr>(UnknownDestName);
170 }
171 
172 // True if the capacity of the destination array is based on the given length,
173 // therefore we assume that it cannot overflow (e.g. 'malloc(given_length + 1)'
174 static bool isDestBasedOnGivenLength(const MatchFinder::MatchResult &Result) {
175  StringRef DestCapacityExprStr =
176  exprToStr(getDestCapacityExpr(Result), Result).trim();
177  StringRef LengthExprStr =
178  exprToStr(Result.Nodes.getNodeAs<Expr>(LengthExprName), Result).trim();
179 
180  return DestCapacityExprStr != "" && LengthExprStr != "" &&
181  DestCapacityExprStr.contains(LengthExprStr);
182 }
183 
184 // Writing and reading from the same memory cannot remove the null-terminator.
185 static bool isDestAndSrcEquals(const MatchFinder::MatchResult &Result) {
186  if (const auto *DestDRE = Result.Nodes.getNodeAs<DeclRefExpr>(DestExprName))
187  if (const auto *SrcDRE = Result.Nodes.getNodeAs<DeclRefExpr>(SrcExprName))
188  return DestDRE->getDecl()->getCanonicalDecl() ==
189  SrcDRE->getDecl()->getCanonicalDecl();
190 
191  return false;
192 }
193 
194 // For example 'std::string str = "foo"; memcpy(dst, str.data(), str.length())'.
195 static bool isStringDataAndLength(const MatchFinder::MatchResult &Result) {
196  const auto *DestExpr =
197  Result.Nodes.getNodeAs<CXXMemberCallExpr>(DestExprName);
198  const auto *SrcExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>(SrcExprName);
199  const auto *LengthExpr =
200  Result.Nodes.getNodeAs<CXXMemberCallExpr>(WrongLengthExprName);
201 
202  StringRef DestStr = "", SrcStr = "", LengthStr = "";
203  if (DestExpr)
204  if (const CXXMethodDecl *DestMD = DestExpr->getMethodDecl())
205  DestStr = DestMD->getName();
206 
207  if (SrcExpr)
208  if (const CXXMethodDecl *SrcMD = SrcExpr->getMethodDecl())
209  SrcStr = SrcMD->getName();
210 
211  if (LengthExpr)
212  if (const CXXMethodDecl *LengthMD = LengthExpr->getMethodDecl())
213  LengthStr = LengthMD->getName();
214 
215  return (LengthStr == "length" || LengthStr == "size") &&
216  (SrcStr == "data" || DestStr == "data");
217 }
218 
219 static bool
220 isGivenLengthEqualToSrcLength(const MatchFinder::MatchResult &Result) {
221  if (Result.Nodes.getNodeAs<Expr>(UnknownLengthName))
222  return false;
223 
224  if (isStringDataAndLength(Result))
225  return true;
226 
227  int GivenLength = getGivenLength(Result);
228  int SrcLength = getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result);
229 
230  if (GivenLength != 0 && SrcLength != 0 && GivenLength == SrcLength)
231  return true;
232 
233  if (const auto *LengthExpr = Result.Nodes.getNodeAs<Expr>(LengthExprName))
234  if (dyn_cast<BinaryOperator>(LengthExpr->IgnoreParenImpCasts()))
235  return false;
236 
237  // Check the strlen()'s argument's 'VarDecl' is equal to the source 'VarDecl'.
238  if (const CallExpr *StrlenCE = getStrlenExpr(Result))
239  if (const auto *ArgDRE =
240  dyn_cast<DeclRefExpr>(StrlenCE->getArg(0)->IgnoreImpCasts()))
241  if (const auto *SrcVD = Result.Nodes.getNodeAs<VarDecl>(SrcVarDeclName))
242  return dyn_cast<VarDecl>(ArgDRE->getDecl()) == SrcVD;
243 
244  return false;
245 }
246 
247 static bool isCorrectGivenLength(const MatchFinder::MatchResult &Result) {
248  if (Result.Nodes.getNodeAs<Expr>(UnknownLengthName))
249  return false;
250 
251  return !isGivenLengthEqualToSrcLength(Result);
252 }
253 
254 // If we rewrite the function call we need to create extra space to hold the
255 // null terminator. The new necessary capacity overflows without that '+ 1'
256 // size and we need to correct the given capacity.
257 static bool isDestCapacityOverflows(const MatchFinder::MatchResult &Result) {
258  if (!isKnownDest(Result))
259  return true;
260 
261  const Expr *DestCapacityExpr = getDestCapacityExpr(Result);
262  int DestCapacity = getLength(DestCapacityExpr, Result);
263  int GivenLength = getGivenLength(Result);
264 
265  if (GivenLength != 0 && DestCapacity != 0)
266  return isGivenLengthEqualToSrcLength(Result) && DestCapacity == GivenLength;
267 
268  // Assume that the destination array's capacity cannot overflow if the
269  // expression of the memory allocation contains '+ 1'.
270  StringRef DestCapacityExprStr = exprToStr(DestCapacityExpr, Result);
271  if (DestCapacityExprStr.contains("+1") || DestCapacityExprStr.contains("+ 1"))
272  return false;
273 
274  return true;
275 }
276 
277 static bool
278 isFixedGivenLengthAndUnknownSrc(const MatchFinder::MatchResult &Result) {
279  if (Result.Nodes.getNodeAs<IntegerLiteral>(WrongLengthExprName))
280  return !getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result);
281 
282  return false;
283 }
284 
285 //===----------------------------------------------------------------------===//
286 // Code injection functions.
287 //===----------------------------------------------------------------------===//
288 
289 // Increase or decrease \p LengthExpr by one.
290 static void lengthExprHandle(const Expr *LengthExpr,
291  LengthHandleKind LengthHandle,
292  const MatchFinder::MatchResult &Result,
293  DiagnosticBuilder &Diag) {
294  LengthExpr = LengthExpr->IgnoreParenImpCasts();
295 
296  // See whether we work with a macro.
297  bool IsMacroDefinition = false;
298  StringRef LengthExprStr = exprToStr(LengthExpr, Result);
299  Preprocessor::macro_iterator It = PP->macro_begin();
300  while (It != PP->macro_end() && !IsMacroDefinition) {
301  if (It->first->getName() == LengthExprStr)
302  IsMacroDefinition = true;
303 
304  ++It;
305  }
306 
307  // Try to obtain an 'IntegerLiteral' and adjust it.
308  if (!IsMacroDefinition) {
309  if (const auto *LengthIL = dyn_cast<IntegerLiteral>(LengthExpr)) {
310  size_t NewLength = LengthIL->getValue().getZExtValue() +
311  (LengthHandle == LengthHandleKind::Increase
312  ? (isInjectUL(Result) ? 1UL : 1)
313  : -1);
314 
315  const auto NewLengthFix = FixItHint::CreateReplacement(
316  LengthIL->getSourceRange(),
317  (Twine(NewLength) + (isInjectUL(Result) ? "UL" : "")).str());
318  Diag << NewLengthFix;
319  return;
320  }
321  }
322 
323  // Try to obtain and remove the '+ 1' string as a decrement fix.
324  const auto *BO = dyn_cast<BinaryOperator>(LengthExpr);
325  if (BO && BO->getOpcode() == BO_Add &&
326  LengthHandle == LengthHandleKind::Decrease) {
327  const Expr *LhsExpr = BO->getLHS()->IgnoreImpCasts();
328  const Expr *RhsExpr = BO->getRHS()->IgnoreImpCasts();
329 
330  if (const auto *LhsIL = dyn_cast<IntegerLiteral>(LhsExpr)) {
331  if (LhsIL->getValue().getZExtValue() == 1) {
332  Diag << FixItHint::CreateRemoval(
333  {LhsIL->getBeginLoc(),
334  RhsExpr->getBeginLoc().getLocWithOffset(-1)});
335  return;
336  }
337  }
338 
339  if (const auto *RhsIL = dyn_cast<IntegerLiteral>(RhsExpr)) {
340  if (RhsIL->getValue().getZExtValue() == 1) {
341  Diag << FixItHint::CreateRemoval(
342  {LhsExpr->getEndLoc().getLocWithOffset(1), RhsIL->getEndLoc()});
343  return;
344  }
345  }
346  }
347 
348  // Try to inject the '+ 1'/'- 1' string.
349  bool NeedInnerParen = BO && BO->getOpcode() != BO_Add;
350 
351  if (NeedInnerParen)
352  Diag << FixItHint::CreateInsertion(LengthExpr->getBeginLoc(), "(");
353 
354  SmallString<8> Injection;
355  if (NeedInnerParen)
356  Injection += ')';
357  Injection += LengthHandle == LengthHandleKind::Increase ? " + 1" : " - 1";
358  if (isInjectUL(Result))
359  Injection += "UL";
360 
361  Diag << FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), Injection);
362 }
363 
364 static void lengthArgHandle(LengthHandleKind LengthHandle,
365  const MatchFinder::MatchResult &Result,
366  DiagnosticBuilder &Diag) {
367  const auto *LengthExpr = Result.Nodes.getNodeAs<Expr>(LengthExprName);
368  lengthExprHandle(LengthExpr, LengthHandle, Result, Diag);
369 }
370 
371 static void lengthArgPosHandle(unsigned ArgPos, LengthHandleKind LengthHandle,
372  const MatchFinder::MatchResult &Result,
373  DiagnosticBuilder &Diag) {
374  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);
375  lengthExprHandle(FunctionExpr->getArg(ArgPos), LengthHandle, Result, Diag);
376 }
377 
378 // The string handler functions are only operates with plain 'char'/'wchar_t'
379 // without 'unsigned/signed', therefore we need to cast it.
380 static bool isDestExprFix(const MatchFinder::MatchResult &Result,
381  DiagnosticBuilder &Diag) {
382  const auto *Dest = Result.Nodes.getNodeAs<Expr>(DestExprName);
383  if (!Dest)
384  return false;
385 
386  std::string TempTyStr = Dest->getType().getAsString();
387  StringRef TyStr = TempTyStr;
388  if (TyStr.startswith("char") || TyStr.startswith("wchar_t"))
389  return false;
390 
391  Diag << FixItHint::CreateInsertion(Dest->getBeginLoc(), "(char *)");
392  return true;
393 }
394 
395 // If the destination array is the same length as the given length we have to
396 // increase the capacity by one to create space for the null terminator.
397 static bool isDestCapacityFix(const MatchFinder::MatchResult &Result,
398  DiagnosticBuilder &Diag) {
399  bool IsOverflows = isDestCapacityOverflows(Result);
400  if (IsOverflows)
401  if (const Expr *CapacityExpr = getDestCapacityExpr(Result))
402  lengthExprHandle(CapacityExpr, LengthHandleKind::Increase, Result, Diag);
403 
404  return IsOverflows;
405 }
406 
407 static void removeArg(int ArgPos, const MatchFinder::MatchResult &Result,
408  DiagnosticBuilder &Diag) {
409  // This is the following structure: (src, '\0', strlen(src))
410  // ArgToRemove: ~~~~~~~~~~~
411  // LHSArg: ~~~~
412  // RemoveArgFix: ~~~~~~~~~~~~~
413  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);
414  const Expr *ArgToRemove = FunctionExpr->getArg(ArgPos);
415  const Expr *LHSArg = FunctionExpr->getArg(ArgPos - 1);
416  const auto RemoveArgFix = FixItHint::CreateRemoval(
417  SourceRange(exprLocEnd(LHSArg, Result),
418  exprLocEnd(ArgToRemove, Result).getLocWithOffset(-1)));
419  Diag << RemoveArgFix;
420 }
421 
422 static void renameFunc(StringRef NewFuncName,
423  const MatchFinder::MatchResult &Result,
424  DiagnosticBuilder &Diag) {
425  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);
426  int FuncNameLength =
427  FunctionExpr->getDirectCallee()->getIdentifier()->getLength();
428  SourceRange FuncNameRange(
429  FunctionExpr->getBeginLoc(),
430  FunctionExpr->getBeginLoc().getLocWithOffset(FuncNameLength - 1));
431 
432  const auto FuncNameFix =
433  FixItHint::CreateReplacement(FuncNameRange, NewFuncName);
434  Diag << FuncNameFix;
435 }
436 
437 static void renameMemcpy(StringRef Name, bool IsCopy, bool IsSafe,
438  const MatchFinder::MatchResult &Result,
439  DiagnosticBuilder &Diag) {
440  SmallString<10> NewFuncName;
441  NewFuncName = (Name[0] != 'w') ? "str" : "wcs";
442  NewFuncName += IsCopy ? "cpy" : "ncpy";
443  NewFuncName += IsSafe ? "_s" : "";
444  renameFunc(NewFuncName, Result, Diag);
445 }
446 
447 static void insertDestCapacityArg(bool IsOverflows, StringRef Name,
448  const MatchFinder::MatchResult &Result,
449  DiagnosticBuilder &Diag) {
450  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);
451  SmallString<64> NewSecondArg;
452 
453  if (int DestLength = getDestCapacity(Result)) {
454  NewSecondArg = Twine(IsOverflows ? DestLength + 1 : DestLength).str();
455  } else {
456  NewSecondArg =
457  (Twine(exprToStr(getDestCapacityExpr(Result), Result)) +
458  (IsOverflows ? (!isInjectUL(Result) ? " + 1" : " + 1UL") : ""))
459  .str();
460  }
461 
462  NewSecondArg += ", ";
463  const auto InsertNewArgFix = FixItHint::CreateInsertion(
464  FunctionExpr->getArg(1)->getBeginLoc(), NewSecondArg);
465  Diag << InsertNewArgFix;
466 }
467 
468 static void insertNullTerminatorExpr(StringRef Name,
469  const MatchFinder::MatchResult &Result,
470  DiagnosticBuilder &Diag) {
471  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);
472  int FuncLocStartColumn = Result.SourceManager->getPresumedColumnNumber(
473  FunctionExpr->getBeginLoc());
474  SourceRange SpaceRange(
475  FunctionExpr->getBeginLoc().getLocWithOffset(-FuncLocStartColumn + 1),
476  FunctionExpr->getBeginLoc());
477  StringRef SpaceBeforeStmtStr = Lexer::getSourceText(
478  CharSourceRange::getCharRange(SpaceRange), *Result.SourceManager,
479  Result.Context->getLangOpts(), 0);
480 
481  SmallString<128> NewAddNullTermExprStr;
482  NewAddNullTermExprStr =
483  (Twine('\n') + SpaceBeforeStmtStr +
484  exprToStr(Result.Nodes.getNodeAs<Expr>(DestExprName), Result) + "[" +
485  exprToStr(Result.Nodes.getNodeAs<Expr>(LengthExprName), Result) +
486  "] = " + ((Name[0] != 'w') ? "\'\\0\';" : "L\'\\0\';"))
487  .str();
488 
489  const auto AddNullTerminatorExprFix = FixItHint::CreateInsertion(
490  exprLocEnd(FunctionExpr, Result).getLocWithOffset(1),
491  NewAddNullTermExprStr);
492  Diag << AddNullTerminatorExprFix;
493 }
494 
495 //===----------------------------------------------------------------------===//
496 // Checker logic with the matchers.
497 //===----------------------------------------------------------------------===//
498 
499 NotNullTerminatedResultCheck::NotNullTerminatedResultCheck(
500  StringRef Name, ClangTidyContext *Context)
501  : ClangTidyCheck(Name, Context),
502  WantToUseSafeFunctions(Options.get("WantToUseSafeFunctions", 1)) {}
503 
506  Options.store(Opts, "WantToUseSafeFunctions", WantToUseSafeFunctions);
507 }
508 
510  const SourceManager &SM, Preprocessor *pp, Preprocessor *ModuleExpanderPP) {
511  PP = pp;
512 }
513 
514 namespace {
515 AST_MATCHER_P(Expr, hasDefinition, ast_matchers::internal::Matcher<Expr>,
516  InnerMatcher) {
517  const Expr *SimpleNode = &Node;
518  SimpleNode = SimpleNode->IgnoreParenImpCasts();
519 
520  if (InnerMatcher.matches(*SimpleNode, Finder, Builder))
521  return true;
522 
523  auto DREHasInit = ignoringImpCasts(
524  declRefExpr(to(varDecl(hasInitializer(ignoringImpCasts(InnerMatcher))))));
525 
526  if (DREHasInit.matches(*SimpleNode, Finder, Builder))
527  return true;
528 
529  const char *const VarDeclName = "variable-declaration";
530  auto DREHasDefinition = ignoringImpCasts(declRefExpr(
531  allOf(to(varDecl().bind(VarDeclName)),
532  hasAncestor(compoundStmt(hasDescendant(binaryOperator(
533  hasLHS(declRefExpr(to(varDecl(equalsBoundNode(VarDeclName))))),
534  hasRHS(ignoringImpCasts(InnerMatcher)))))))));
535 
536  if (DREHasDefinition.matches(*SimpleNode, Finder, Builder))
537  return true;
538 
539  return false;
540 }
541 } // namespace
542 
544  auto IncOp =
545  binaryOperator(hasOperatorName("+"),
546  hasEitherOperand(ignoringParenImpCasts(integerLiteral())));
547 
548  auto DecOp =
549  binaryOperator(hasOperatorName("-"),
550  hasEitherOperand(ignoringParenImpCasts(integerLiteral())));
551 
552  auto HasIncOp = anyOf(ignoringImpCasts(IncOp), hasDescendant(IncOp));
553  auto HasDecOp = anyOf(ignoringImpCasts(DecOp), hasDescendant(DecOp));
554 
555  auto Container = ignoringImpCasts(cxxMemberCallExpr(hasDescendant(declRefExpr(
556  hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(recordDecl(
557  hasAnyName("::std::vector", "::std::list", "::std::deque"))))))))));
558 
559  auto StringTy = type(hasUnqualifiedDesugaredType(recordType(
560  hasDeclaration(cxxRecordDecl(hasName("::std::basic_string"))))));
561 
562  auto AnyOfStringTy =
563  anyOf(hasType(StringTy), hasType(qualType(pointsTo(StringTy))));
564 
565  auto CharTyArray = hasType(qualType(hasCanonicalType(
566  arrayType(hasElementType(isAnyCharacter())).bind(DestArrayTyName))));
567 
568  auto CharTyPointer = hasType(
569  qualType(hasCanonicalType(pointerType(pointee(isAnyCharacter())))));
570 
571  auto AnyOfCharTy = anyOf(CharTyArray, CharTyPointer);
572 
573  //===--------------------------------------------------------------------===//
574  // The following six cases match problematic length expressions.
575  //===--------------------------------------------------------------------===//
576 
577  // - Example: char src[] = "foo"; strlen(src);
578  auto Strlen =
579  callExpr(callee(functionDecl(hasAnyName("::strlen", "::wcslen"))))
580  .bind(WrongLengthExprName);
581 
582  // - Example: std::string str = "foo"; str.size();
583  auto SizeOrLength =
584  cxxMemberCallExpr(
585  allOf(on(expr(AnyOfStringTy).bind("Foo")),
586  has(memberExpr(member(hasAnyName("size", "length"))))))
587  .bind(WrongLengthExprName);
588 
589  // - Example: char src[] = "foo"; sizeof(src);
590  auto SizeOfCharExpr = unaryExprOrTypeTraitExpr(has(expr(AnyOfCharTy)));
591 
592  auto WrongLength =
593  ignoringImpCasts(anyOf(Strlen, SizeOrLength, hasDescendant(Strlen),
594  hasDescendant(SizeOrLength)));
595 
596  // - Example: length = strlen(src);
597  auto DREWithoutInc =
598  ignoringImpCasts(declRefExpr(to(varDecl(hasInitializer(WrongLength)))));
599 
600  auto AnyOfCallOrDREWithoutInc = anyOf(DREWithoutInc, WrongLength);
601 
602  // - Example: int getLength(const char *str) { return strlen(str); }
603  auto CallExprReturnWithoutInc = ignoringImpCasts(callExpr(callee(functionDecl(
604  hasBody(has(returnStmt(hasReturnValue(AnyOfCallOrDREWithoutInc))))))));
605 
606  // - Example: int length = getLength(src);
607  auto DREHasReturnWithoutInc = ignoringImpCasts(
608  declRefExpr(to(varDecl(hasInitializer(CallExprReturnWithoutInc)))));
609 
610  auto AnyOfWrongLengthInit =
611  anyOf(WrongLength, AnyOfCallOrDREWithoutInc, CallExprReturnWithoutInc,
612  DREHasReturnWithoutInc);
613 
614  //===--------------------------------------------------------------------===//
615  // The following five cases match the 'destination' array length's
616  // expression which is used in 'memcpy()' and 'memmove()' matchers.
617  //===--------------------------------------------------------------------===//
618 
619  // Note: Sometimes the size of char is explicitly written out.
620  auto SizeExpr = anyOf(SizeOfCharExpr, integerLiteral(equals(1)));
621 
622  auto MallocLengthExpr = allOf(
623  callee(functionDecl(
624  hasAnyName("::alloca", "::calloc", "malloc", "realloc"))),
625  hasAnyArgument(allOf(unless(SizeExpr), expr().bind(DestMallocExprName))));
626 
627  // - Example: (char *)malloc(length);
628  auto DestMalloc = anyOf(callExpr(MallocLengthExpr),
629  hasDescendant(callExpr(MallocLengthExpr)));
630 
631  // - Example: new char[length];
632  auto DestCXXNewExpr = ignoringImpCasts(
633  cxxNewExpr(hasArraySize(expr().bind(DestMallocExprName))));
634 
635  auto AnyOfDestInit = anyOf(DestMalloc, DestCXXNewExpr);
636 
637  // - Example: char dest[13]; or char dest[length];
638  auto DestArrayTyDecl = declRefExpr(
639  to(anyOf(varDecl(CharTyArray).bind(DestVarDeclName),
640  varDecl(hasInitializer(AnyOfDestInit)).bind(DestVarDeclName))));
641 
642  // - Example: foo[bar[baz]].qux; (or just ParmVarDecl)
643  auto DestUnknownDecl =
644  declRefExpr(allOf(to(varDecl(AnyOfCharTy).bind(DestVarDeclName)),
645  expr().bind(UnknownDestName)))
646  .bind(DestExprName);
647 
648  auto AnyOfDestDecl = ignoringImpCasts(
649  anyOf(allOf(hasDefinition(anyOf(AnyOfDestInit, DestArrayTyDecl,
650  hasDescendant(DestArrayTyDecl))),
651  expr().bind(DestExprName)),
652  anyOf(DestUnknownDecl, hasDescendant(DestUnknownDecl))));
653 
654  auto NullTerminatorExpr = binaryOperator(
655  hasLHS(anyOf(hasDescendant(declRefExpr(
656  to(varDecl(equalsBoundNode(DestVarDeclName))))),
657  hasDescendant(declRefExpr(equalsBoundNode(DestExprName))))),
658  hasRHS(ignoringImpCasts(
659  anyOf(characterLiteral(equals(0U)), integerLiteral(equals(0))))));
660 
661  auto SrcDecl = declRefExpr(
662  allOf(to(decl().bind(SrcVarDeclName)),
663  anyOf(hasAncestor(cxxMemberCallExpr().bind(SrcExprName)),
664  expr().bind(SrcExprName))));
665 
666  auto AnyOfSrcDecl =
667  ignoringImpCasts(anyOf(stringLiteral().bind(SrcExprName),
668  hasDescendant(stringLiteral().bind(SrcExprName)),
669  SrcDecl, hasDescendant(SrcDecl)));
670 
671  //===--------------------------------------------------------------------===//
672  // Match the problematic function calls.
673  //===--------------------------------------------------------------------===//
674 
675  struct CallContext {
676  CallContext(StringRef Name, Optional<unsigned> DestinationPos,
677  Optional<unsigned> SourcePos, unsigned LengthPos,
678  bool WithIncrease)
679  : Name(Name), DestinationPos(DestinationPos), SourcePos(SourcePos),
680  LengthPos(LengthPos), WithIncrease(WithIncrease){};
681 
682  StringRef Name;
683  Optional<unsigned> DestinationPos;
684  Optional<unsigned> SourcePos;
685  unsigned LengthPos;
686  bool WithIncrease;
687  };
688 
689  auto MatchDestination = [=](CallContext CC) {
690  return hasArgument(*CC.DestinationPos,
691  allOf(AnyOfDestDecl,
692  unless(hasAncestor(compoundStmt(
693  hasDescendant(NullTerminatorExpr)))),
694  unless(Container)));
695  };
696 
697  auto MatchSource = [=](CallContext CC) {
698  return hasArgument(*CC.SourcePos, AnyOfSrcDecl);
699  };
700 
701  auto MatchGivenLength = [=](CallContext CC) {
702  return hasArgument(
703  CC.LengthPos,
704  allOf(
705  anyOf(
706  ignoringImpCasts(integerLiteral().bind(WrongLengthExprName)),
707  allOf(unless(hasDefinition(SizeOfCharExpr)),
708  allOf(CC.WithIncrease
709  ? ignoringImpCasts(hasDefinition(HasIncOp))
710  : ignoringImpCasts(allOf(
711  unless(hasDefinition(HasIncOp)),
712  anyOf(hasDefinition(binaryOperator().bind(
713  UnknownLengthName)),
714  hasDefinition(anything())))),
715  AnyOfWrongLengthInit))),
716  expr().bind(LengthExprName)));
717  };
718 
719  auto MatchCall = [=](CallContext CC) {
720  std::string CharHandlerFuncName = "::" + CC.Name.str();
721 
722  // Try to match with 'wchar_t' based function calls.
723  std::string WcharHandlerFuncName =
724  "::" + (CC.Name.startswith("mem") ? "w" + CC.Name.str()
725  : "wcs" + CC.Name.substr(3).str());
726 
727  return allOf(callee(functionDecl(
728  hasAnyName(CharHandlerFuncName, WcharHandlerFuncName))),
729  MatchGivenLength(CC));
730  };
731 
732  auto Match = [=](CallContext CC) {
733  if (CC.DestinationPos && CC.SourcePos)
734  return allOf(MatchCall(CC), MatchDestination(CC), MatchSource(CC));
735 
736  if (CC.DestinationPos && !CC.SourcePos)
737  return allOf(MatchCall(CC), MatchDestination(CC),
738  hasArgument(*CC.DestinationPos, anything()));
739 
740  if (!CC.DestinationPos && CC.SourcePos)
741  return allOf(MatchCall(CC), MatchSource(CC),
742  hasArgument(*CC.SourcePos, anything()));
743 
744  llvm_unreachable("Unhandled match");
745  };
746 
747  // void *memcpy(void *dest, const void *src, size_t count)
748  auto Memcpy = Match({"memcpy", 0, 1, 2, false});
749 
750  // errno_t memcpy_s(void *dest, size_t ds, const void *src, size_t count)
751  auto Memcpy_s = Match({"memcpy_s", 0, 2, 3, false});
752 
753  // void *memchr(const void *src, int c, size_t count)
754  auto Memchr = Match({"memchr", None, 0, 2, false});
755 
756  // void *memmove(void *dest, const void *src, size_t count)
757  auto Memmove = Match({"memmove", 0, 1, 2, false});
758 
759  // errno_t memmove_s(void *dest, size_t ds, const void *src, size_t count)
760  auto Memmove_s = Match({"memmove_s", 0, 2, 3, false});
761 
762  // int strncmp(const char *str1, const char *str2, size_t count);
763  auto StrncmpRHS = Match({"strncmp", None, 1, 2, true});
764  auto StrncmpLHS = Match({"strncmp", None, 0, 2, true});
765 
766  // size_t strxfrm(char *dest, const char *src, size_t count);
767  auto Strxfrm = Match({"strxfrm", 0, 1, 2, false});
768 
769  // errno_t strerror_s(char *buffer, size_t bufferSize, int errnum);
770  auto Strerror_s = Match({"strerror_s", 0, None, 1, false});
771 
772  auto AnyOfMatchers = anyOf(Memcpy, Memcpy_s, Memmove, Memmove_s, StrncmpRHS,
773  StrncmpLHS, Strxfrm, Strerror_s);
774 
775  Finder->addMatcher(callExpr(AnyOfMatchers).bind(FunctionExprName), this);
776 
777  // Need to remove the CastExpr from 'memchr()' as 'strchr()' returns 'char *'.
778  Finder->addMatcher(
779  callExpr(Memchr,
780  unless(hasAncestor(castExpr(unless(implicitCastExpr())))))
781  .bind(FunctionExprName),
782  this);
783  Finder->addMatcher(
784  castExpr(allOf(unless(implicitCastExpr()),
785  has(callExpr(Memchr).bind(FunctionExprName))))
786  .bind(CastExprName),
787  this);
788 }
789 
791  const MatchFinder::MatchResult &Result) {
792  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);
793  if (FunctionExpr->getBeginLoc().isMacroID())
794  return;
795 
796  if (WantToUseSafeFunctions && PP->isMacroDefined("__STDC_LIB_EXT1__")) {
797  Optional<bool> AreSafeFunctionsWanted;
798 
799  Preprocessor::macro_iterator It = PP->macro_begin();
800  while (It != PP->macro_end() && !AreSafeFunctionsWanted.hasValue()) {
801  if (It->first->getName() == "__STDC_WANT_LIB_EXT1__") {
802  const auto *MI = PP->getMacroInfo(It->first);
803  const auto &T = MI->tokens().back();
804  StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength());
805  llvm::APInt IntValue;
806  ValueStr.getAsInteger(10, IntValue);
807  AreSafeFunctionsWanted = IntValue.getZExtValue();
808  }
809 
810  ++It;
811  }
812 
813  if (AreSafeFunctionsWanted.hasValue())
814  UseSafeFunctions = AreSafeFunctionsWanted.getValue();
815  }
816 
817  StringRef Name = FunctionExpr->getDirectCallee()->getName();
818  if (Name.startswith("mem") || Name.startswith("wmem"))
819  memoryHandlerFunctionFix(Name, Result);
820  else if (Name == "strerror_s")
821  strerror_sFix(Result);
822  else if (Name.endswith("ncmp"))
823  ncmpFix(Name, Result);
824  else if (Name.endswith("xfrm"))
825  xfrmFix(Name, Result);
826 }
827 
828 void NotNullTerminatedResultCheck::memoryHandlerFunctionFix(
829  StringRef Name, const MatchFinder::MatchResult &Result) {
830  if (isCorrectGivenLength(Result))
831  return;
832 
833  if (Name.endswith("chr")) {
834  memchrFix(Name, Result);
835  return;
836  }
837 
838  if ((Name.contains("cpy") || Name.contains("move")) &&
840  return;
841 
842  auto Diag =
843  diag(Result.Nodes.getNodeAs<CallExpr>(FunctionExprName)->getBeginLoc(),
844  "the result from calling '%0' is not null-terminated")
845  << Name;
846 
847  if (Name.endswith("cpy")) {
848  memcpyFix(Name, Result, Diag);
849  } else if (Name.endswith("cpy_s")) {
850  memcpy_sFix(Name, Result, Diag);
851  } else if (Name.endswith("move")) {
852  memmoveFix(Name, Result, Diag);
853  } else if (Name.endswith("move_s")) {
854  isDestCapacityFix(Result, Diag);
856  }
857 }
858 
859 void NotNullTerminatedResultCheck::memcpyFix(
860  StringRef Name, const MatchFinder::MatchResult &Result,
861  DiagnosticBuilder &Diag) {
862  bool IsOverflows = isDestCapacityFix(Result, Diag);
863  bool IsDestFixed = isDestExprFix(Result, Diag);
864 
865  bool IsCopy =
867 
868  bool IsSafe = UseSafeFunctions && IsOverflows && isKnownDest(Result) &&
869  !isDestBasedOnGivenLength(Result);
870 
871  bool IsDestLengthNotRequired =
872  IsSafe && getLangOpts().CPlusPlus &&
873  Result.Nodes.getNodeAs<ArrayType>(DestArrayTyName) && !IsDestFixed;
874 
875  renameMemcpy(Name, IsCopy, IsSafe, Result, Diag);
876 
877  if (IsSafe && !IsDestLengthNotRequired)
878  insertDestCapacityArg(IsOverflows, Name, Result, Diag);
879 
880  if (IsCopy)
881  removeArg(2, Result, Diag);
882 
883  if (!IsCopy && !IsSafe)
884  insertNullTerminatorExpr(Name, Result, Diag);
885 }
886 
887 void NotNullTerminatedResultCheck::memcpy_sFix(
888  StringRef Name, const MatchFinder::MatchResult &Result,
889  DiagnosticBuilder &Diag) {
890  bool IsOverflows = isDestCapacityFix(Result, Diag);
891  bool IsDestFixed = isDestExprFix(Result, Diag);
892 
893  bool RemoveDestLength = getLangOpts().CPlusPlus &&
894  Result.Nodes.getNodeAs<ArrayType>(DestArrayTyName) &&
895  !IsDestFixed;
896  bool IsCopy = isGivenLengthEqualToSrcLength(Result);
897  bool IsSafe = IsOverflows;
898 
899  renameMemcpy(Name, IsCopy, IsSafe, Result, Diag);
900 
901  if (!IsSafe || (IsSafe && RemoveDestLength))
902  removeArg(1, Result, Diag);
903  else if (IsOverflows && isKnownDest(Result))
905 
906  if (IsCopy)
907  removeArg(3, Result, Diag);
908 
909  if (!IsCopy && !IsSafe)
910  insertNullTerminatorExpr(Name, Result, Diag);
911 }
912 
913 void NotNullTerminatedResultCheck::memchrFix(
914  StringRef Name, const MatchFinder::MatchResult &Result) {
915  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);
916  if (const auto GivenCL = dyn_cast<CharacterLiteral>(FunctionExpr->getArg(1)))
917  if (GivenCL->getValue() != 0)
918  return;
919 
920  auto Diag = diag(FunctionExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(),
921  "the length is too short to include the null terminator");
922 
923  if (const auto *CastExpr = Result.Nodes.getNodeAs<Expr>(CastExprName)) {
924  const auto CastRemoveFix = FixItHint::CreateRemoval(
925  SourceRange(CastExpr->getBeginLoc(),
926  FunctionExpr->getBeginLoc().getLocWithOffset(-1)));
927  Diag << CastRemoveFix;
928  }
929 
930  StringRef NewFuncName = (Name[0] != 'w') ? "strchr" : "wcschr";
931  renameFunc(NewFuncName, Result, Diag);
932  removeArg(2, Result, Diag);
933 }
934 
935 void NotNullTerminatedResultCheck::memmoveFix(
936  StringRef Name, const MatchFinder::MatchResult &Result,
937  DiagnosticBuilder &Diag) {
938  bool IsOverflows = isDestCapacityFix(Result, Diag);
939 
940  if (UseSafeFunctions && isKnownDest(Result)) {
941  renameFunc((Name[0] != 'w') ? "memmove_s" : "wmemmove_s", Result, Diag);
942  insertDestCapacityArg(IsOverflows, Name, Result, Diag);
943  }
944 
946 }
947 
948 void NotNullTerminatedResultCheck::strerror_sFix(
949  const MatchFinder::MatchResult &Result) {
950  auto Diag =
951  diag(Result.Nodes.getNodeAs<CallExpr>(FunctionExprName)->getBeginLoc(),
952  "the result from calling 'strerror_s' is not null-terminated and "
953  "missing the last character of the error message");
954 
955  isDestCapacityFix(Result, Diag);
957 }
958 
959 void NotNullTerminatedResultCheck::ncmpFix(
960  StringRef Name, const MatchFinder::MatchResult &Result) {
961  const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(FunctionExprName);
962  const Expr *FirstArgExpr = FunctionExpr->getArg(0)->IgnoreImpCasts();
963  const Expr *SecondArgExpr = FunctionExpr->getArg(1)->IgnoreImpCasts();
964  bool IsLengthTooLong = false;
965 
966  if (const CallExpr *StrlenExpr = getStrlenExpr(Result)) {
967  const Expr *LengthExprArg = StrlenExpr->getArg(0);
968  StringRef FirstExprStr = exprToStr(FirstArgExpr, Result).trim();
969  StringRef SecondExprStr = exprToStr(SecondArgExpr, Result).trim();
970  StringRef LengthArgStr = exprToStr(LengthExprArg, Result).trim();
971  IsLengthTooLong =
972  LengthArgStr == FirstExprStr || LengthArgStr == SecondExprStr;
973  } else {
974  int SrcLength =
975  getLength(Result.Nodes.getNodeAs<Expr>(SrcExprName), Result);
976  int GivenLength = getGivenLength(Result);
977  if (SrcLength != 0 && GivenLength != 0)
978  IsLengthTooLong = GivenLength > SrcLength;
979  }
980 
981  if (!IsLengthTooLong && !isStringDataAndLength(Result))
982  return;
983 
984  auto Diag = diag(FunctionExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(),
985  "comparison length is too long and might lead to a "
986  "buffer overflow");
987 
989 }
990 
991 void NotNullTerminatedResultCheck::xfrmFix(
992  StringRef Name, const MatchFinder::MatchResult &Result) {
993  if (!isDestCapacityOverflows(Result))
994  return;
995 
996  auto Diag =
997  diag(Result.Nodes.getNodeAs<CallExpr>(FunctionExprName)->getBeginLoc(),
998  "the result from calling '%0' is not null-terminated")
999  << Name;
1000 
1001  isDestCapacityFix(Result, Diag);
1003 }
1004 
1005 } // namespace bugprone
1006 } // namespace tidy
1007 } // namespace clang
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static unsigned getLength(const Expr *E, const MatchFinder::MatchResult &Result)
const FunctionDecl * Decl
static bool isDestAndSrcEquals(const MatchFinder::MatchResult &Result)
static void renameFunc(StringRef NewFuncName, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
static const Expr * getDestCapacityExpr(const MatchFinder::MatchResult &Result)
constexpr llvm::StringLiteral WrongLengthExprName
static bool isCorrectGivenLength(const MatchFinder::MatchResult &Result)
static bool isInjectUL(const MatchFinder::MatchResult &Result)
static bool isGivenLengthEqualToSrcLength(const MatchFinder::MatchResult &Result)
constexpr llvm::StringLiteral SrcVarDeclName
constexpr llvm::StringLiteral LengthExprName
static void removeArg(int ArgPos, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
static void lengthArgPosHandle(unsigned ArgPos, LengthHandleKind LengthHandle, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
Base class for all clang-tidy checks.
static bool isKnownDest(const MatchFinder::MatchResult &Result)
static StringRef exprToStr(const Expr *E, const MatchFinder::MatchResult &Result)
const LangOptions & getLangOpts() const
Returns the language options from the context.
static void lengthArgHandle(LengthHandleKind LengthHandle, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
static int getGivenLength(const MatchFinder::MatchResult &Result)
static bool isDestBasedOnGivenLength(const MatchFinder::MatchResult &Result)
static void renameMemcpy(StringRef Name, bool IsCopy, bool IsSafe, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
static void lengthExprHandle(const Expr *LengthExpr, LengthHandleKind LengthHandle, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
constexpr llvm::StringLiteral CastExprName
constexpr llvm::StringLiteral SrcExprName
constexpr llvm::StringLiteral UnknownDestName
static bool isStringDataAndLength(const MatchFinder::MatchResult &Result)
static bool isDestExprFix(const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
static bool isDestCapacityFix(const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
constexpr llvm::StringLiteral DestArrayTyName
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:227
CodeCompletionBuilder Builder
constexpr llvm::StringLiteral FunctionExprName
constexpr llvm::StringLiteral UnknownLengthName
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static SourceLocation exprLocEnd(const Expr *E, const MatchFinder::MatchResult &Result)
static const CallExpr * getStrlenExpr(const MatchFinder::MatchResult &Result)
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static bool isDestCapacityOverflows(const MatchFinder::MatchResult &Result)
unsigned Length
const Expr * E
constexpr llvm::StringLiteral DestVarDeclName
static void insertNullTerminatorExpr(StringRef Name, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
static bool isFixedGivenLengthAndUnknownSrc(const MatchFinder::MatchResult &Result)
constexpr llvm::StringLiteral DestExprName
constexpr llvm::StringLiteral DestMallocExprName
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
static int getDestCapacity(const MatchFinder::MatchResult &Result)
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl, ast_matchers::internal::Matcher< CXXMethodDecl >, InnerMatcher)
static void insertDestCapacityArg(bool IsOverflows, StringRef Name, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)