clang-tools 10.0.0

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
9 #include "NotNullTerminatedResultCheck.h"
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
35 enum class LengthHandleKind { Increase, Decrease };
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
504 void NotNullTerminatedResultCheck::storeOptions(
505 ClangTidyOptions::OptionMap &Opts) {
506 Options.store(Opts, "WantToUseSafeFunctions", WantToUseSafeFunctions);
507 }
508
509 void NotNullTerminatedResultCheck::registerPPCallbacks(
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
543 void NotNullTerminatedResultCheck::registerMatchers(MatchFinder *Finder) {
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
790 void NotNullTerminatedResultCheck::check(
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")) &&
839 (isDestAndSrcEquals(Result) || isFixedGivenLengthAndUnknownSrc(Result)))
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);
855 lengthArgHandle(LengthHandleKind::Increase, 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 =
866 isGivenLengthEqualToSrcLength(Result) || isDestBasedOnGivenLength(Result);
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))
904 lengthArgPosHandle(1, LengthHandleKind::Increase, Result, Diag);
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
945 lengthArgHandle(LengthHandleKind::Increase, Result, Diag);
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);
956 lengthArgHandle(LengthHandleKind::Increase, 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
988 lengthArgHandle(LengthHandleKind::Decrease, Result, Diag);
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);
1002 lengthArgHandle(LengthHandleKind::Increase, Result, Diag);
1003 }
1004
1005 } // namespace bugprone
1006 } // namespace tidy
1007 } // namespace clang
clang::tidy::bugprone::NotNullTerminatedResultCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: NotNullTerminatedResultCheck.cpp:543
clang::tidy::bugprone::getLength
static unsigned getLength(const Expr *E, const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:62
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::bugprone::LengthHandleKind::Increase
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:93
clang::tidy::bugprone::LengthHandleKind::Decrease
clang::tidy::bugprone::isDestAndSrcEquals
static bool isDestAndSrcEquals(const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:185
clang::tidy::bugprone::renameFunc
static void renameFunc(StringRef NewFuncName, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
Definition: NotNullTerminatedResultCheck.cpp:422
clang::tidy::bugprone::getDestCapacityExpr
static const Expr * getDestCapacityExpr(const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:44
clang::tidy::bugprone::WrongLengthExprName
constexpr llvm::StringLiteral WrongLengthExprName
Definition: NotNullTerminatedResultCheck.cpp:32
clang::tidy::bugprone::isCorrectGivenLength
static bool isCorrectGivenLength(const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:247
clang::tidy::bugprone::isInjectUL
static bool isInjectUL(const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:163
clang::tidy::bugprone::isGivenLengthEqualToSrcLength
static bool isGivenLengthEqualToSrcLength(const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:220
clang::tidy::bugprone::SrcVarDeclName
constexpr llvm::StringLiteral SrcVarDeclName
Definition: NotNullTerminatedResultCheck.cpp:29
clang::tidy::bugprone::LengthExprName
constexpr llvm::StringLiteral LengthExprName
Definition: NotNullTerminatedResultCheck.cpp:31
clang::tidy::bugprone::removeArg
static void removeArg(int ArgPos, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
Definition: NotNullTerminatedResultCheck.cpp:407
clang::tidy::bugprone::lengthArgPosHandle
static void lengthArgPosHandle(unsigned ArgPos, LengthHandleKind LengthHandle, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
Definition: NotNullTerminatedResultCheck.cpp:371
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:47
clang::tidy::bugprone::isKnownDest
static bool isKnownDest(const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:168
clang::tidy::bugprone::exprToStr
static StringRef exprToStr(const Expr *E, const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:139
clang::tidy::ClangTidyCheck::getLangOpts
const LangOptions & getLangOpts() const
Returns the language options from the context.
Definition: ClangTidyCheck.h:188
clang::tidy::bugprone::lengthArgHandle
static void lengthArgHandle(LengthHandleKind LengthHandle, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
Definition: NotNullTerminatedResultCheck.cpp:364
clang::tidy::bugprone::getGivenLength
static int getGivenLength(const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:117
clang::tidy::bugprone::isDestBasedOnGivenLength
static bool isDestBasedOnGivenLength(const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:174
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:184
clang::tidy::bugprone::renameMemcpy
static void renameMemcpy(StringRef Name, bool IsCopy, bool IsSafe, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
Definition: NotNullTerminatedResultCheck.cpp:437
clang::tidy::bugprone::lengthExprHandle
static void lengthExprHandle(const Expr *LengthExpr, LengthHandleKind LengthHandle, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
Definition: NotNullTerminatedResultCheck.cpp:290
clang::tidy::bugprone::CastExprName
constexpr llvm::StringLiteral CastExprName
Definition: NotNullTerminatedResultCheck.cpp:23
clang::tidy::bugprone::SrcExprName
constexpr llvm::StringLiteral SrcExprName
Definition: NotNullTerminatedResultCheck.cpp:30
clang::tidy::bugprone::UnknownDestName
constexpr llvm::StringLiteral UnknownDestName
Definition: NotNullTerminatedResultCheck.cpp:24
clang::tidy::bugprone::isStringDataAndLength
static bool isStringDataAndLength(const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:195
clang::tidy::bugprone::isDestExprFix
static bool isDestExprFix(const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
Definition: NotNullTerminatedResultCheck.cpp:380
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:58
clang::tidy::bugprone::isDestCapacityFix
static bool isDestCapacityFix(const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
Definition: NotNullTerminatedResultCheck.cpp:397
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:790
clang::tidy::bugprone::DestArrayTyName
constexpr llvm::StringLiteral DestArrayTyName
Definition: NotNullTerminatedResultCheck.cpp:25
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:97
NotNullTerminatedResultCheck.h
clang::clangd::getTokenRange
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:227
Builder
CodeCompletionBuilder Builder
Definition: CodeCompletionStringsTests.cpp:35
clang::tidy::bugprone::FunctionExprName
constexpr llvm::StringLiteral FunctionExprName
Definition: NotNullTerminatedResultCheck.cpp:22
clang::tidy::bugprone::UnknownLengthName
constexpr llvm::StringLiteral UnknownLengthName
Definition: NotNullTerminatedResultCheck.cpp:33
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::bugprone::exprLocEnd
static SourceLocation exprLocEnd(const Expr *E, const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:150
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:28
clang::tidy::bugprone::getStrlenExpr
static const CallExpr * getStrlenExpr(const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:103
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:77
clang::tidy::bugprone::isDestCapacityOverflows
static bool isDestCapacityOverflows(const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:257
Length
unsigned Length
Definition: SourceCodeTests.cpp:87
E
const Expr * E
Definition: AvoidBindCheck.cpp:82
clang::tidy::bugprone::DestVarDeclName
constexpr llvm::StringLiteral DestVarDeclName
Definition: NotNullTerminatedResultCheck.cpp:26
clang::tidy::bugprone::insertNullTerminatorExpr
static void insertNullTerminatorExpr(StringRef Name, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
Definition: NotNullTerminatedResultCheck.cpp:468
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:504
clang::tidy::bugprone::LengthHandleKind
LengthHandleKind
Definition: NotNullTerminatedResultCheck.cpp:35
clang::tidy::bugprone::isFixedGivenLengthAndUnknownSrc
static bool isFixedGivenLengthAndUnknownSrc(const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:278
clang::tidy::bugprone::DestExprName
constexpr llvm::StringLiteral DestExprName
Definition: NotNullTerminatedResultCheck.cpp:28
clang::tidy::bugprone::DestMallocExprName
constexpr llvm::StringLiteral DestMallocExprName
Definition: NotNullTerminatedResultCheck.cpp:27
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidyCheck.cpp:21
clang::tidy::bugprone::getDestCapacity
static int getDestCapacity(const MatchFinder::MatchResult &Result)
Definition: NotNullTerminatedResultCheck.cpp:95
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:509
clang::tidy::readability::AST_MATCHER_P
AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl, ast_matchers::internal::Matcher< CXXMethodDecl >, InnerMatcher)
Definition: ConvertMemberFunctionsToStatic.cpp:52
clang::tidy::bugprone::insertDestCapacityArg
static void insertDestCapacityArg(bool IsOverflows, StringRef Name, const MatchFinder::MatchResult &Result, DiagnosticBuilder &Diag)
Definition: NotNullTerminatedResultCheck.cpp:447