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