1 | //===--- PosixReturnCheck.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 "PosixReturnCheck.h" |
10 | #include "../utils/Matchers.h" |
11 | #include "clang/AST/ASTContext.h" |
12 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
13 | #include "clang/Lex/Lexer.h" |
14 | |
15 | using namespace clang::ast_matchers; |
16 | |
17 | namespace clang::tidy::bugprone { |
18 | |
19 | static StringRef getFunctionSpelling(const MatchFinder::MatchResult &Result, |
20 | const char *BindingStr) { |
21 | const CallExpr *MatchedCall = cast<CallExpr>( |
22 | Val: (Result.Nodes.getNodeAs<BinaryOperator>(ID: BindingStr))->getLHS()); |
23 | const SourceManager &SM = *Result.SourceManager; |
24 | return Lexer::getSourceText(Range: CharSourceRange::getTokenRange( |
25 | MatchedCall->getCallee()->getSourceRange()), |
26 | SM, LangOpts: Result.Context->getLangOpts()); |
27 | } |
28 | |
29 | void PosixReturnCheck::registerMatchers(MatchFinder *Finder) { |
30 | Finder->addMatcher( |
31 | NodeMatch: binaryOperator( |
32 | hasOperatorName(Name: "<" ), |
33 | hasLHS(InnerMatcher: callExpr(callee(InnerMatcher: functionDecl( |
34 | anyOf(matchesName(RegExp: "^::posix_" ), matchesName(RegExp: "^::pthread_" )), |
35 | unless(hasName(Name: "::posix_openpt" )))))), |
36 | hasRHS(InnerMatcher: integerLiteral(equals(Value: 0)))) |
37 | .bind(ID: "ltzop" ), |
38 | Action: this); |
39 | Finder->addMatcher( |
40 | NodeMatch: binaryOperator( |
41 | hasOperatorName(Name: ">=" ), |
42 | hasLHS(InnerMatcher: callExpr(callee(InnerMatcher: functionDecl( |
43 | anyOf(matchesName(RegExp: "^::posix_" ), matchesName(RegExp: "^::pthread_" )), |
44 | unless(hasName(Name: "::posix_openpt" )))))), |
45 | hasRHS(InnerMatcher: integerLiteral(equals(Value: 0)))) |
46 | .bind(ID: "atop" ), |
47 | Action: this); |
48 | Finder->addMatcher( |
49 | NodeMatch: binaryOperator( |
50 | hasAnyOperatorName("==" , "!=" , "<=" , "<" ), |
51 | hasLHS(InnerMatcher: callExpr(callee(InnerMatcher: functionDecl( |
52 | anyOf(matchesName(RegExp: "^::posix_" ), matchesName(RegExp: "^::pthread_" )), |
53 | unless(hasName(Name: "::posix_openpt" )))))), |
54 | hasRHS(InnerMatcher: unaryOperator(hasOperatorName(Name: "-" ), |
55 | hasUnaryOperand(InnerMatcher: integerLiteral())))) |
56 | .bind(ID: "binop" ), |
57 | Action: this); |
58 | } |
59 | |
60 | void PosixReturnCheck::check(const MatchFinder::MatchResult &Result) { |
61 | if (const auto *LessThanZeroOp = |
62 | Result.Nodes.getNodeAs<BinaryOperator>(ID: "ltzop" )) { |
63 | SourceLocation OperatorLoc = LessThanZeroOp->getOperatorLoc(); |
64 | diag(Loc: OperatorLoc, Description: "the comparison always evaluates to false because %0 " |
65 | "always returns non-negative values" ) |
66 | << getFunctionSpelling(Result, BindingStr: "ltzop" ) |
67 | << FixItHint::CreateReplacement(RemoveRange: OperatorLoc, Code: Twine(">" ).str()); |
68 | return; |
69 | } |
70 | if (const auto *AlwaysTrueOp = |
71 | Result.Nodes.getNodeAs<BinaryOperator>(ID: "atop" )) { |
72 | diag(Loc: AlwaysTrueOp->getOperatorLoc(), |
73 | Description: "the comparison always evaluates to true because %0 always returns " |
74 | "non-negative values" ) |
75 | << getFunctionSpelling(Result, BindingStr: "atop" ); |
76 | return; |
77 | } |
78 | const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>(ID: "binop" ); |
79 | diag(Loc: BinOp->getOperatorLoc(), Description: "%0 only returns non-negative values" ) |
80 | << getFunctionSpelling(Result, BindingStr: "binop" ); |
81 | } |
82 | |
83 | } // namespace clang::tidy::bugprone |
84 | |