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"
36enum class LengthHandleKind { Increase, Decrease };
39static Preprocessor *
PP;
48 if (
const auto *DestVAT =
50 return DestVAT->getSizeExpr();
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();
63 const MatchFinder::MatchResult &Result) {
67 E = E->IgnoreImpCasts();
69 if (
const auto *LengthDRE = dyn_cast<DeclRefExpr>(E))
70 if (
const auto *LengthVD = dyn_cast<VarDecl>(LengthDRE->getDecl());
71 LengthVD && !isa<ParmVarDecl>(LengthVD))
72 if (
const Expr *LengthInit = LengthVD->getInit();
73 LengthInit && !LengthInit->isValueDependent()) {
74 Expr::EvalResult Length;
75 if (LengthInit->EvaluateAsInt(Length, *Result.Context))
76 return Length.Val.getInt().getZExtValue();
79 if (
const auto *LengthIL = dyn_cast<IntegerLiteral>(E))
80 return LengthIL->getValue().getZExtValue();
82 if (
const auto *StrDRE = dyn_cast<DeclRefExpr>(E))
83 if (
const auto *StrVD = dyn_cast<VarDecl>(StrDRE->getDecl()))
84 if (
const Expr *StrInit = StrVD->getInit())
85 if (
const auto *StrSL =
86 dyn_cast<StringLiteral>(StrInit->IgnoreImpCasts()))
87 return StrSL->getLength();
89 if (
const auto *SrcSL = dyn_cast<StringLiteral>(E))
90 return SrcSL->getLength();
99 return getLength(DestCapacityExpr, Result);
105static const CallExpr *
getStrlenExpr(
const MatchFinder::MatchResult &Result) {
106 if (
const auto *StrlenExpr =
108 if (
const Decl *D = StrlenExpr->getCalleeDecl())
109 if (
const FunctionDecl *FD = D->getAsFunction())
110 if (
const IdentifierInfo *II = FD->getIdentifier();
111 II && (II->isStr(
"strlen") || II->isStr(
"wcslen")))
123 if (
const int Length =
127 if (
const int Length =
133 if (
const Expr *Arg = StrlenCE->getArg(0)->IgnoreImpCasts())
134 if (
const int ArgLength =
getLength(Arg, Result))
142 const MatchFinder::MatchResult &Result) {
146 return Lexer::getSourceText(
147 CharSourceRange::getTokenRange(E->getSourceRange()),
148 *Result.SourceManager, Result.Context->getLangOpts(),
nullptr);
153 const MatchFinder::MatchResult &Result) {
154 return Lexer::getLocForEndOfToken(E->getEndLoc(), 0, *Result.SourceManager,
155 Result.Context->getLangOpts());
165static bool isInjectUL(
const MatchFinder::MatchResult &Result) {
177 const StringRef DestCapacityExprStr =
179 const StringRef LengthExprStr =
182 return !DestCapacityExprStr.empty() && !LengthExprStr.empty() &&
183 DestCapacityExprStr.contains(LengthExprStr);
188 if (
const auto *DestDRE = Result.Nodes.getNodeAs<DeclRefExpr>(
DestExprName))
189 if (
const auto *SrcDRE = Result.Nodes.getNodeAs<DeclRefExpr>(
SrcExprName))
190 return DestDRE->getDecl()->getCanonicalDecl() ==
191 SrcDRE->getDecl()->getCanonicalDecl();
198 const auto *DestExpr =
199 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
DestExprName);
200 const auto *SrcExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
SrcExprName);
201 const auto *LengthExpr =
204 StringRef DestStr =
"", SrcStr =
"", LengthStr =
"";
206 if (
const CXXMethodDecl *DestMD = DestExpr->getMethodDecl())
207 DestStr = DestMD->getName();
210 if (
const CXXMethodDecl *SrcMD = SrcExpr->getMethodDecl())
211 SrcStr = SrcMD->getName();
214 if (
const CXXMethodDecl *LengthMD = LengthExpr->getMethodDecl())
215 LengthStr = LengthMD->getName();
217 return (LengthStr ==
"length" || LengthStr ==
"size") &&
218 (SrcStr ==
"data" || DestStr ==
"data");
230 const int SrcLength =
233 if (GivenLength != 0 && SrcLength != 0 && GivenLength == SrcLength)
236 if (
const auto *LengthExpr = Result.Nodes.getNodeAs<Expr>(
LengthExprName);
237 LengthExpr && isa<BinaryOperator>(LengthExpr->IgnoreParenImpCasts()))
242 if (
const auto *ArgDRE =
243 dyn_cast<DeclRefExpr>(StrlenCE->getArg(0)->IgnoreImpCasts()))
244 if (
const auto *SrcVD = Result.Nodes.getNodeAs<VarDecl>(
SrcVarDeclName))
245 return dyn_cast<VarDecl>(ArgDRE->getDecl()) == SrcVD;
265 const int DestCapacity =
getLength(DestCapacityExpr, Result);
268 if (GivenLength != 0 && DestCapacity != 0)
273 const StringRef DestCapacityExprStr =
exprToStr(DestCapacityExpr, Result);
274 if (DestCapacityExprStr.contains(
"+1") || DestCapacityExprStr.contains(
"+ 1"))
294 LengthHandleKind LengthHandle,
295 const MatchFinder::MatchResult &Result,
296 const DiagnosticBuilder &Diag) {
297 LengthExpr = LengthExpr->IgnoreParenImpCasts();
300 const StringRef LengthExprStr =
exprToStr(LengthExpr, Result);
301 const bool IsMacroDefinition = llvm::any_of(
PP->macros(), [=](
const auto &M) {
302 return M.first->getName() == LengthExprStr;
306 if (!IsMacroDefinition) {
307 if (
const auto *LengthIL = dyn_cast<IntegerLiteral>(LengthExpr)) {
308 const uint64_t NewLength =
309 LengthIL->getValue().getZExtValue() +
310 (LengthHandle == LengthHandleKind::Increase ? 1 : -1);
312 const auto NewLengthFix = FixItHint::CreateReplacement(
313 LengthIL->getSourceRange(),
314 (Twine(NewLength) + (
isInjectUL(Result) ?
"UL" :
"")).str());
315 Diag << NewLengthFix;
321 const auto *BO = dyn_cast<BinaryOperator>(LengthExpr);
322 if (BO && BO->getOpcode() == BO_Add &&
323 LengthHandle == LengthHandleKind::Decrease) {
324 const Expr *LhsExpr = BO->getLHS()->IgnoreImpCasts();
325 const Expr *RhsExpr = BO->getRHS()->IgnoreImpCasts();
327 if (
const auto *LhsIL = dyn_cast<IntegerLiteral>(LhsExpr);
328 LhsIL && LhsIL->getValue().getZExtValue() == 1) {
329 Diag << FixItHint::CreateRemoval(
330 {LhsIL->getBeginLoc(), RhsExpr->getBeginLoc().getLocWithOffset(-1)});
334 if (
const auto *RhsIL = dyn_cast<IntegerLiteral>(RhsExpr);
335 RhsIL && RhsIL->getValue().getZExtValue() == 1) {
336 Diag << FixItHint::CreateRemoval(
337 {LhsExpr->getEndLoc().getLocWithOffset(1), RhsIL->getEndLoc()});
343 const bool NeedInnerParen = BO && BO->getOpcode() != BO_Add;
346 Diag << FixItHint::CreateInsertion(LengthExpr->getBeginLoc(),
"(");
348 SmallString<8> Injection;
351 Injection += LengthHandle == LengthHandleKind::Increase ?
" + 1" :
" - 1";
355 Diag << FixItHint::CreateInsertion(
exprLocEnd(LengthExpr, Result), Injection);
359 const MatchFinder::MatchResult &Result,
360 const DiagnosticBuilder &Diag) {
361 const auto *LengthExpr = Result.Nodes.getNodeAs<Expr>(
LengthExprName);
366 const MatchFinder::MatchResult &Result,
367 const DiagnosticBuilder &Diag) {
368 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(
FunctionExprName);
375 const DiagnosticBuilder &Diag) {
376 const auto *Dest = Result.Nodes.getNodeAs<Expr>(
DestExprName);
380 const std::string TempTyStr = Dest->getType().getAsString();
381 const StringRef TyStr = TempTyStr;
382 if (TyStr.starts_with(
"char") || TyStr.starts_with(
"wchar_t"))
385 Diag << FixItHint::CreateInsertion(Dest->getBeginLoc(),
"(char *)");
392 const DiagnosticBuilder &Diag) {
401static void removeArg(
int ArgPos,
const MatchFinder::MatchResult &Result,
402 const DiagnosticBuilder &Diag) {
407 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(
FunctionExprName);
408 const Expr *ArgToRemove = FunctionExpr->getArg(ArgPos);
409 const Expr *LHSArg = FunctionExpr->getArg(ArgPos - 1);
410 const auto RemoveArgFix = FixItHint::CreateRemoval(
412 exprLocEnd(ArgToRemove, Result).getLocWithOffset(-1)));
413 Diag << RemoveArgFix;
417 const MatchFinder::MatchResult &Result,
418 const DiagnosticBuilder &Diag) {
419 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(
FunctionExprName);
420 const int FuncNameLength =
421 FunctionExpr->getDirectCallee()->getIdentifier()->getLength();
422 const SourceRange FuncNameRange(
423 FunctionExpr->getBeginLoc(),
424 FunctionExpr->getBeginLoc().getLocWithOffset(FuncNameLength - 1));
426 const auto FuncNameFix =
427 FixItHint::CreateReplacement(FuncNameRange, NewFuncName);
432 const MatchFinder::MatchResult &Result,
433 const DiagnosticBuilder &Diag) {
434 SmallString<10> NewFuncName;
435 NewFuncName = (Name[0] !=
'w') ?
"str" :
"wcs";
436 NewFuncName += IsCopy ?
"cpy" :
"ncpy";
437 NewFuncName += IsSafe ?
"_s" :
"";
442 const MatchFinder::MatchResult &Result,
443 const DiagnosticBuilder &Diag) {
444 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(
FunctionExprName);
445 SmallString<64> NewSecondArg;
448 NewSecondArg = Twine(IsOverflows ? DestLength + 1 : DestLength).str();
452 (IsOverflows ? (!
isInjectUL(Result) ?
" + 1" :
" + 1UL") :
""))
456 NewSecondArg +=
", ";
457 const auto InsertNewArgFix = FixItHint::CreateInsertion(
458 FunctionExpr->getArg(1)->getBeginLoc(), NewSecondArg);
459 Diag << InsertNewArgFix;
463 const MatchFinder::MatchResult &Result,
464 const DiagnosticBuilder &Diag) {
465 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(
FunctionExprName);
466 const int FuncLocStartColumn = Result.SourceManager->getPresumedColumnNumber(
467 FunctionExpr->getBeginLoc());
468 const SourceRange SpaceRange(
469 FunctionExpr->getBeginLoc().getLocWithOffset(-FuncLocStartColumn + 1),
470 FunctionExpr->getBeginLoc());
471 const StringRef SpaceBeforeStmtStr = Lexer::getSourceText(
472 CharSourceRange::getCharRange(SpaceRange), *Result.SourceManager,
473 Result.Context->getLangOpts(),
nullptr);
475 SmallString<128> NewAddNullTermExprStr;
476 NewAddNullTermExprStr =
477 (Twine(
'\n') + SpaceBeforeStmtStr +
480 "] = " + ((Name[0] !=
'w') ? R
"('\0';)" : R"(L'\0';)"))
483 const auto AddNullTerminatorExprFix = FixItHint::CreateInsertion(
484 exprLocEnd(FunctionExpr, Result).getLocWithOffset(1),
485 NewAddNullTermExprStr);
486 Diag << AddNullTerminatorExprFix;
496 WantToUseSafeFunctions(Options.get(
"WantToUseSafeFunctions", true)) {}
500 Options.store(Opts,
"WantToUseSafeFunctions", WantToUseSafeFunctions);
504 const SourceManager &SM, Preprocessor *Pp, Preprocessor *ModuleExpanderPP) {
509AST_MATCHER_P(Expr, hasDefinition, ast_matchers::internal::Matcher<Expr>,
511 const Expr *SimpleNode = &Node;
512 SimpleNode = SimpleNode->IgnoreParenImpCasts();
514 if (InnerMatcher.matches(*SimpleNode, Finder, Builder))
517 const auto DREHasInit = ignoringImpCasts(
518 declRefExpr(to(varDecl(hasInitializer(ignoringImpCasts(InnerMatcher))))));
520 if (DREHasInit.matches(*SimpleNode, Finder, Builder))
523 const char *
const VarDeclName =
"variable-declaration";
524 const auto DREHasDefinition = ignoringImpCasts(declRefExpr(
525 to(varDecl().bind(VarDeclName)),
526 hasAncestor(compoundStmt(hasDescendant(binaryOperator(
527 hasLHS(declRefExpr(to(varDecl(equalsBoundNode(VarDeclName))))),
528 hasRHS(ignoringImpCasts(InnerMatcher))))))));
530 if (DREHasDefinition.matches(*SimpleNode, Finder, Builder))
539 binaryOperator(hasOperatorName(
"+"),
540 hasEitherOperand(ignoringParenImpCasts(integerLiteral())));
543 binaryOperator(hasOperatorName(
"-"),
544 hasEitherOperand(ignoringParenImpCasts(integerLiteral())));
546 const auto HasIncOp = anyOf(ignoringImpCasts(IncOp), hasDescendant(IncOp));
547 const auto HasDecOp = anyOf(ignoringImpCasts(DecOp), hasDescendant(DecOp));
549 const auto Container = ignoringImpCasts(cxxMemberCallExpr(
550 hasDescendant(declRefExpr(hasType(hasUnqualifiedDesugaredType(
551 recordType(hasDeclaration(recordDecl(hasAnyName(
552 "::std::vector",
"::std::list",
"::std::deque"))))))))));
554 const auto StringTy = type(hasUnqualifiedDesugaredType(recordType(
555 hasDeclaration(cxxRecordDecl(hasName(
"::std::basic_string"))))));
557 const auto AnyOfStringTy =
558 anyOf(hasType(StringTy), hasType(qualType(pointsTo(StringTy))));
560 auto CharTyArray = hasType(qualType(hasCanonicalType(
563 auto CharTyPointer = hasType(
564 qualType(hasCanonicalType(pointerType(pointee(isAnyCharacter())))));
566 const auto AnyOfCharTy = anyOf(CharTyArray, CharTyPointer);
574 callExpr(callee(functionDecl(hasAnyName(
"::strlen",
"::wcslen"))))
579 cxxMemberCallExpr(on(expr(AnyOfStringTy).bind(
"Foo")),
580 has(memberExpr(member(hasAnyName(
"size",
"length")))))
584 auto SizeOfCharExpr = unaryExprOrTypeTraitExpr(has(expr(AnyOfCharTy)));
587 ignoringImpCasts(anyOf(Strlen, SizeOrLength, hasDescendant(Strlen),
588 hasDescendant(SizeOrLength)));
592 ignoringImpCasts(declRefExpr(to(varDecl(hasInitializer(WrongLength)))));
594 auto AnyOfCallOrDREWithoutInc = anyOf(DREWithoutInc, WrongLength);
597 auto CallExprReturnWithoutInc = ignoringImpCasts(callExpr(callee(functionDecl(
598 hasBody(has(returnStmt(hasReturnValue(AnyOfCallOrDREWithoutInc))))))));
601 auto DREHasReturnWithoutInc = ignoringImpCasts(
602 declRefExpr(to(varDecl(hasInitializer(CallExprReturnWithoutInc)))));
604 const auto AnyOfWrongLengthInit =
605 anyOf(WrongLength, AnyOfCallOrDREWithoutInc, CallExprReturnWithoutInc,
606 DREHasReturnWithoutInc);
614 auto SizeExpr = anyOf(SizeOfCharExpr, integerLiteral(equals(1)));
616 const auto MallocLengthExpr = allOf(
618 hasAnyName(
"::alloca",
"::calloc",
"malloc",
"realloc"))),
622 auto DestMalloc = anyOf(callExpr(MallocLengthExpr),
623 hasDescendant(callExpr(MallocLengthExpr)));
626 auto DestCXXNewExpr = ignoringImpCasts(
629 auto AnyOfDestInit = anyOf(DestMalloc, DestCXXNewExpr);
632 auto DestArrayTyDecl = declRefExpr(
637 auto DestUnknownDecl =
642 const auto AnyOfDestDecl = ignoringImpCasts(
643 anyOf(allOf(hasDefinition(anyOf(AnyOfDestInit, DestArrayTyDecl,
644 hasDescendant(DestArrayTyDecl))),
646 anyOf(DestUnknownDecl, hasDescendant(DestUnknownDecl))));
648 const auto NullTerminatorExpr = binaryOperator(
649 hasLHS(anyOf(hasDescendant(declRefExpr(to(varDecl(
651 hasDescendant(declRefExpr(
653 hasRHS(ignoringImpCasts(
654 anyOf(characterLiteral(equals(0U)), integerLiteral(equals(0))))));
658 anyOf(hasAncestor(cxxMemberCallExpr().bind(
SrcExprName)),
661 const auto AnyOfSrcDecl =
662 ignoringImpCasts(anyOf(stringLiteral().bind(
SrcExprName),
664 SrcDecl, hasDescendant(SrcDecl)));
671 CallContext(StringRef Name, std::optional<unsigned> DestinationPos,
672 std::optional<unsigned> SourcePos,
unsigned LengthPos,
674 : Name(Name), DestinationPos(DestinationPos), SourcePos(SourcePos),
675 LengthPos(LengthPos), WithIncrease(WithIncrease) {}
678 std::optional<unsigned> DestinationPos;
679 std::optional<unsigned> SourcePos;
684 const auto MatchDestination = [=](CallContext CC) {
685 return hasArgument(*CC.DestinationPos,
687 unless(hasAncestor(compoundStmt(
688 hasDescendant(NullTerminatorExpr)))),
692 const auto MatchSource = [=](CallContext CC) {
693 return hasArgument(*CC.SourcePos, AnyOfSrcDecl);
696 const auto MatchGivenLength = [=](CallContext CC) {
701 allOf(unless(hasDefinition(SizeOfCharExpr)),
702 allOf(CC.WithIncrease
703 ? ignoringImpCasts(hasDefinition(HasIncOp))
705 allOf(unless(hasDefinition(HasIncOp)),
706 hasDefinition(optionally(
707 binaryOperator().bind(
709 AnyOfWrongLengthInit))),
713 const auto MatchCall = [=](CallContext CC) {
714 const std::string CharHandlerFuncName =
"::" + CC.Name.str();
717 const std::string WcharHandlerFuncName =
718 "::" + (CC.Name.starts_with(
"mem") ?
"w" + CC.Name.str()
719 :
"wcs" + CC.Name.substr(3).str());
721 return allOf(callee(functionDecl(
722 hasAnyName(CharHandlerFuncName, WcharHandlerFuncName))),
723 MatchGivenLength(CC));
726 const auto Match = [=](CallContext CC) {
727 if (CC.DestinationPos && CC.SourcePos)
728 return allOf(MatchCall(CC), MatchDestination(CC), MatchSource(CC));
730 if (CC.DestinationPos && !CC.SourcePos)
731 return allOf(MatchCall(CC), MatchDestination(CC),
732 hasArgument(*CC.DestinationPos, anything()));
734 if (!CC.DestinationPos && CC.SourcePos)
735 return allOf(MatchCall(CC), MatchSource(CC),
736 hasArgument(*CC.SourcePos, anything()));
738 llvm_unreachable(
"Unhandled match");
742 auto Memcpy = Match({
"memcpy", 0, 1, 2,
false});
745 auto MemcpyS = Match({
"memcpy_s", 0, 2, 3,
false});
748 const auto Memchr = Match({
"memchr", std::nullopt, 0, 2,
false});
751 auto Memmove = Match({
"memmove", 0, 1, 2,
false});
754 auto MemmoveS = Match({
"memmove_s", 0, 2, 3,
false});
757 auto StrncmpRHS = Match({
"strncmp", std::nullopt, 1, 2,
true});
758 auto StrncmpLHS = Match({
"strncmp", std::nullopt, 0, 2,
true});
761 auto Strxfrm = Match({
"strxfrm", 0, 1, 2,
false});
764 auto StrerrorS = Match({
"strerror_s", 0, std::nullopt, 1,
false});
766 const auto AnyOfMatchers = anyOf(Memcpy, MemcpyS, Memmove, MemmoveS,
767 StrncmpRHS, StrncmpLHS, Strxfrm, StrerrorS);
774 unless(hasAncestor(castExpr(unless(implicitCastExpr())))))
778 castExpr(allOf(unless(implicitCastExpr()),
785 const MatchFinder::MatchResult &Result) {
786 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(
FunctionExprName);
787 if (FunctionExpr->getBeginLoc().isMacroID())
790 if (WantToUseSafeFunctions &&
PP->isMacroDefined(
"__STDC_LIB_EXT1__")) {
791 std::optional<bool> AreSafeFunctionsWanted;
792 for (
const auto &M :
PP->macros()) {
793 if (M.first->getName() !=
"__STDC_WANT_LIB_EXT1__")
795 const auto *MI =
PP->getMacroInfo(M.first);
799 const auto &T = MI->tokens().back();
800 if (T.isLiteral() && T.getLiteralData()) {
801 const StringRef ValueStr(T.getLiteralData(), T.getLength());
802 llvm::APInt IntValue;
803 ValueStr.getAsInteger(10, IntValue);
804 AreSafeFunctionsWanted = IntValue.getZExtValue();
809 if (AreSafeFunctionsWanted)
810 UseSafeFunctions = *AreSafeFunctionsWanted;
813 const StringRef Name = FunctionExpr->getDirectCallee()->getName();
814 if (Name.starts_with(
"mem") || Name.starts_with(
"wmem"))
815 memoryHandlerFunctionFix(Name, Result);
816 else if (Name ==
"strerror_s")
817 strerrorSFix(Result);
818 else if (Name.ends_with(
"ncmp"))
819 ncmpFix(Name, Result);
820 else if (Name.ends_with(
"xfrm"))
821 xfrmFix(Name, Result);
824void NotNullTerminatedResultCheck::memoryHandlerFunctionFix(
825 StringRef Name,
const MatchFinder::MatchResult &Result) {
829 if (Name.ends_with(
"chr")) {
830 memchrFix(Name, Result);
834 if ((Name.contains(
"cpy") || Name.contains(
"move")) &&
840 "the result from calling '%0' is not null-terminated")
843 if (Name.ends_with(
"cpy")) {
844 memcpyFix(Name, Result, Diag);
845 }
else if (Name.ends_with(
"cpy_s")) {
846 memcpySFix(Name, Result, Diag);
847 }
else if (Name.ends_with(
"move")) {
848 memmoveFix(Name, Result, Diag);
849 }
else if (Name.ends_with(
"move_s")) {
855void NotNullTerminatedResultCheck::memcpyFix(
856 StringRef Name,
const MatchFinder::MatchResult &Result,
857 DiagnosticBuilder &Diag) {
864 const bool IsSafe = UseSafeFunctions && IsOverflows &&
isKnownDest(Result) &&
867 const bool IsDestLengthNotRequired =
868 IsSafe && getLangOpts().CPlusPlus &&
873 if (IsSafe && !IsDestLengthNotRequired)
879 if (!IsCopy && !IsSafe)
883void NotNullTerminatedResultCheck::memcpySFix(
884 StringRef Name,
const MatchFinder::MatchResult &Result,
885 DiagnosticBuilder &Diag) {
889 const bool RemoveDestLength =
890 getLangOpts().CPlusPlus &&
893 const bool IsSafe = IsOverflows;
897 if (!IsSafe || (IsSafe && RemoveDestLength))
905 if (!IsCopy && !IsSafe)
909void NotNullTerminatedResultCheck::memchrFix(
910 StringRef Name,
const MatchFinder::MatchResult &Result) {
911 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(
FunctionExprName);
912 if (
const auto *GivenCL = dyn_cast<CharacterLiteral>(FunctionExpr->getArg(1));
913 GivenCL && GivenCL->getValue() != 0)
917 diag(FunctionExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(),
918 "the length is too short to include the null terminator");
920 if (
const auto *CastExpr = Result.Nodes.getNodeAs<Expr>(
CastExprName)) {
921 const auto CastRemoveFix = FixItHint::CreateRemoval(
922 SourceRange(CastExpr->getBeginLoc(),
923 FunctionExpr->getBeginLoc().getLocWithOffset(-1)));
924 Diag << CastRemoveFix;
927 const StringRef NewFuncName = (Name[0] !=
'w') ?
"strchr" :
"wcschr";
932void NotNullTerminatedResultCheck::memmoveFix(
933 StringRef Name,
const MatchFinder::MatchResult &Result,
934 DiagnosticBuilder &Diag)
const {
938 renameFunc((Name[0] !=
'w') ?
"memmove_s" :
"wmemmove_s", Result, Diag);
945void NotNullTerminatedResultCheck::strerrorSFix(
946 const MatchFinder::MatchResult &Result) {
949 "the result from calling 'strerror_s' is not null-terminated and "
950 "missing the last character of the error message");
956void NotNullTerminatedResultCheck::ncmpFix(
957 StringRef Name,
const MatchFinder::MatchResult &Result) {
958 const auto *FunctionExpr = Result.Nodes.getNodeAs<CallExpr>(
FunctionExprName);
959 const Expr *FirstArgExpr = FunctionExpr->getArg(0)->IgnoreImpCasts();
960 const Expr *SecondArgExpr = FunctionExpr->getArg(1)->IgnoreImpCasts();
961 bool IsLengthTooLong =
false;
964 const Expr *LengthExprArg = StrlenExpr->getArg(0);
965 const StringRef FirstExprStr =
exprToStr(FirstArgExpr, Result).trim();
966 const StringRef SecondExprStr =
exprToStr(SecondArgExpr, Result).trim();
967 const StringRef LengthArgStr =
exprToStr(LengthExprArg, Result).trim();
969 LengthArgStr == FirstExprStr || LengthArgStr == SecondExprStr;
971 const int SrcLength =
974 if (SrcLength != 0 && GivenLength != 0)
975 IsLengthTooLong = GivenLength > SrcLength;
982 diag(FunctionExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(),
983 "comparison length is too long and might lead to a "
989void NotNullTerminatedResultCheck::xfrmFix(
990 StringRef Name,
const MatchFinder::MatchResult &Result) {
996 "the result from calling '%0' is not null-terminated")
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
NotNullTerminatedResultCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
static bool isStringDataAndLength(const MatchFinder::MatchResult &Result)
static bool isCorrectGivenLength(const MatchFinder::MatchResult &Result)
static const CallExpr * getStrlenExpr(const MatchFinder::MatchResult &Result)
static void insertDestCapacityArg(bool IsOverflows, StringRef Name, const MatchFinder::MatchResult &Result, const DiagnosticBuilder &Diag)
constexpr StringRef UnknownLengthName
static bool isKnownDest(const MatchFinder::MatchResult &Result)
static bool isDestAndSrcEquals(const MatchFinder::MatchResult &Result)
constexpr StringRef SrcVarDeclName
constexpr StringRef DestExprName
static SourceLocation exprLocEnd(const Expr *E, const MatchFinder::MatchResult &Result)
static void lengthArgPosHandle(unsigned ArgPos, LengthHandleKind LengthHandle, const MatchFinder::MatchResult &Result, const DiagnosticBuilder &Diag)
static int getGivenLength(const MatchFinder::MatchResult &Result)
constexpr StringRef DestMallocExprName
static void insertNullTerminatorExpr(StringRef Name, const MatchFinder::MatchResult &Result, const DiagnosticBuilder &Diag)
static void removeArg(int ArgPos, const MatchFinder::MatchResult &Result, const DiagnosticBuilder &Diag)
static void renameMemcpy(StringRef Name, bool IsCopy, bool IsSafe, const MatchFinder::MatchResult &Result, const DiagnosticBuilder &Diag)
static bool isDestBasedOnGivenLength(const MatchFinder::MatchResult &Result)
static const Expr * getDestCapacityExpr(const MatchFinder::MatchResult &Result)
constexpr StringRef LengthExprName
static bool isGivenLengthEqualToSrcLength(const MatchFinder::MatchResult &Result)
constexpr StringRef DestArrayTyName
constexpr StringRef FunctionExprName
constexpr StringRef SrcExprName
static int getDestCapacity(const MatchFinder::MatchResult &Result)
static bool isDestCapacityFix(const MatchFinder::MatchResult &Result, const DiagnosticBuilder &Diag)
static bool isDestCapacityOverflows(const MatchFinder::MatchResult &Result)
static void lengthExprHandle(const Expr *LengthExpr, LengthHandleKind LengthHandle, const MatchFinder::MatchResult &Result, const DiagnosticBuilder &Diag)
static bool isInjectUL(const MatchFinder::MatchResult &Result)
static bool isFixedGivenLengthAndUnknownSrc(const MatchFinder::MatchResult &Result)
static bool isDestExprFix(const MatchFinder::MatchResult &Result, const DiagnosticBuilder &Diag)
static StringRef exprToStr(const Expr *E, const MatchFinder::MatchResult &Result)
static void renameFunc(StringRef NewFuncName, const MatchFinder::MatchResult &Result, const DiagnosticBuilder &Diag)
static unsigned getLength(const Expr *E, const MatchFinder::MatchResult &Result)
constexpr StringRef CastExprName
constexpr StringRef WrongLengthExprName
static void lengthArgHandle(LengthHandleKind LengthHandle, const MatchFinder::MatchResult &Result, const DiagnosticBuilder &Diag)
constexpr StringRef DestVarDeclName
constexpr StringRef UnknownDestName
llvm::StringMap< ClangTidyValue > OptionMap