150
|
1 //===--- MisplacedOperatorInStrlenInAllocCheck.cpp - clang-tidy------------===//
|
|
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 "MisplacedOperatorInStrlenInAllocCheck.h"
|
|
10 #include "clang/AST/ASTContext.h"
|
|
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
12
|
|
13 using namespace clang::ast_matchers;
|
|
14
|
|
15 namespace clang {
|
|
16 namespace tidy {
|
|
17 namespace bugprone {
|
|
18
|
|
19 void MisplacedOperatorInStrlenInAllocCheck::registerMatchers(
|
|
20 MatchFinder *Finder) {
|
|
21 const auto StrLenFunc = functionDecl(anyOf(
|
|
22 hasName("::strlen"), hasName("::std::strlen"), hasName("::strnlen"),
|
|
23 hasName("::std::strnlen"), hasName("::strnlen_s"),
|
|
24 hasName("::std::strnlen_s"), hasName("::wcslen"),
|
|
25 hasName("::std::wcslen"), hasName("::wcsnlen"), hasName("::std::wcsnlen"),
|
|
26 hasName("::wcsnlen_s"), hasName("std::wcsnlen_s")));
|
|
27
|
|
28 const auto BadUse =
|
|
29 callExpr(callee(StrLenFunc),
|
|
30 hasAnyArgument(ignoringImpCasts(
|
|
31 binaryOperator(
|
|
32 hasOperatorName("+"),
|
|
33 hasRHS(ignoringParenImpCasts(integerLiteral(equals(1)))))
|
|
34 .bind("BinOp"))))
|
|
35 .bind("StrLen");
|
|
36
|
|
37 const auto BadArg = anyOf(
|
|
38 allOf(unless(binaryOperator(
|
|
39 hasOperatorName("+"), hasLHS(BadUse),
|
|
40 hasRHS(ignoringParenImpCasts(integerLiteral(equals(1)))))),
|
|
41 hasDescendant(BadUse)),
|
|
42 BadUse);
|
|
43
|
|
44 const auto Alloc0Func =
|
|
45 functionDecl(anyOf(hasName("::malloc"), hasName("std::malloc"),
|
|
46 hasName("::alloca"), hasName("std::alloca")));
|
|
47 const auto Alloc1Func =
|
|
48 functionDecl(anyOf(hasName("::calloc"), hasName("std::calloc"),
|
|
49 hasName("::realloc"), hasName("std::realloc")));
|
|
50
|
|
51 const auto Alloc0FuncPtr =
|
|
52 varDecl(hasType(isConstQualified()),
|
|
53 hasInitializer(ignoringParenImpCasts(
|
|
54 declRefExpr(hasDeclaration(Alloc0Func)))));
|
|
55 const auto Alloc1FuncPtr =
|
|
56 varDecl(hasType(isConstQualified()),
|
|
57 hasInitializer(ignoringParenImpCasts(
|
|
58 declRefExpr(hasDeclaration(Alloc1Func)))));
|
|
59
|
173
|
60 Finder->addMatcher(
|
|
61 traverse(ast_type_traits::TK_AsIs,
|
|
62 callExpr(callee(decl(anyOf(Alloc0Func, Alloc0FuncPtr))),
|
|
63 hasArgument(0, BadArg))
|
|
64 .bind("Alloc")),
|
|
65 this);
|
150
|
66 Finder->addMatcher(
|
173
|
67 traverse(ast_type_traits::TK_AsIs,
|
|
68 callExpr(callee(decl(anyOf(Alloc1Func, Alloc1FuncPtr))),
|
|
69 hasArgument(1, BadArg))
|
|
70 .bind("Alloc")),
|
|
71 this);
|
|
72 Finder->addMatcher(
|
|
73 traverse(ast_type_traits::TK_AsIs,
|
|
74 cxxNewExpr(isArray(), hasArraySize(BadArg)).bind("Alloc")),
|
|
75 this);
|
150
|
76 }
|
|
77
|
|
78 void MisplacedOperatorInStrlenInAllocCheck::check(
|
|
79 const MatchFinder::MatchResult &Result) {
|
|
80 const Expr *Alloc = Result.Nodes.getNodeAs<CallExpr>("Alloc");
|
|
81 if (!Alloc)
|
|
82 Alloc = Result.Nodes.getNodeAs<CXXNewExpr>("Alloc");
|
173
|
83 assert(Alloc && "Matched node bound by 'Alloc' should be either 'CallExpr'"
|
150
|
84 " or 'CXXNewExpr'");
|
|
85
|
|
86 const auto *StrLen = Result.Nodes.getNodeAs<CallExpr>("StrLen");
|
|
87 const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("BinOp");
|
|
88
|
|
89 const StringRef StrLenText = Lexer::getSourceText(
|
|
90 CharSourceRange::getTokenRange(StrLen->getSourceRange()),
|
|
91 *Result.SourceManager, getLangOpts());
|
|
92 const StringRef Arg0Text = Lexer::getSourceText(
|
|
93 CharSourceRange::getTokenRange(StrLen->getArg(0)->getSourceRange()),
|
|
94 *Result.SourceManager, getLangOpts());
|
|
95 const StringRef StrLenBegin = StrLenText.substr(0, StrLenText.find(Arg0Text));
|
|
96 const StringRef StrLenEnd = StrLenText.substr(
|
|
97 StrLenText.find(Arg0Text) + Arg0Text.size(), StrLenText.size());
|
|
98
|
|
99 const StringRef LHSText = Lexer::getSourceText(
|
|
100 CharSourceRange::getTokenRange(BinOp->getLHS()->getSourceRange()),
|
|
101 *Result.SourceManager, getLangOpts());
|
|
102 const StringRef RHSText = Lexer::getSourceText(
|
|
103 CharSourceRange::getTokenRange(BinOp->getRHS()->getSourceRange()),
|
|
104 *Result.SourceManager, getLangOpts());
|
|
105
|
|
106 auto Hint = FixItHint::CreateReplacement(
|
|
107 StrLen->getSourceRange(),
|
|
108 (StrLenBegin + LHSText + StrLenEnd + " + " + RHSText).str());
|
|
109
|
|
110 diag(Alloc->getBeginLoc(),
|
|
111 "addition operator is applied to the argument of %0 instead of its "
|
|
112 "result")
|
|
113 << StrLen->getDirectCallee()->getName() << Hint;
|
|
114 }
|
|
115
|
|
116 } // namespace bugprone
|
|
117 } // namespace tidy
|
|
118 } // namespace clang
|