Skip to content

Commit e6020f5

Browse files
committed
[clang-tidy] new check: bugprone-posix-return
Summary: Checks if any calls to posix functions (except posix_openpt) expect negative return values. These functions return either 0 on success or an errno on failure, which is positive only. Reviewers: JonasToth, gribozavr, alexfh, hokein Reviewed By: gribozavr Subscribers: Eugene.Zelenko, lebedev.ri, llozano, george.burgess.iv, xazax.hun, srhines, mgorny, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D63623 Patch by Jian Cai. llvm-svn: 365007
1 parent 7264a47 commit e6020f5

File tree

8 files changed

+270
-0
lines changed

8 files changed

+270
-0
lines changed

clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "MoveForwardingReferenceCheck.h"
3232
#include "MultipleStatementMacroCheck.h"
3333
#include "ParentVirtualCallCheck.h"
34+
#include "PosixReturnCheck.h"
3435
#include "SizeofContainerCheck.h"
3536
#include "SizeofExpressionCheck.h"
3637
#include "StringConstructorCheck.h"
@@ -104,6 +105,8 @@ class BugproneModule : public ClangTidyModule {
104105
"bugprone-narrowing-conversions");
105106
CheckFactories.registerCheck<ParentVirtualCallCheck>(
106107
"bugprone-parent-virtual-call");
108+
CheckFactories.registerCheck<PosixReturnCheck>(
109+
"bugprone-posix-return");
107110
CheckFactories.registerCheck<SizeofContainerCheck>(
108111
"bugprone-sizeof-container");
109112
CheckFactories.registerCheck<SizeofExpressionCheck>(

clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ add_clang_library(clangTidyBugproneModule
2323
MoveForwardingReferenceCheck.cpp
2424
MultipleStatementMacroCheck.cpp
2525
ParentVirtualCallCheck.cpp
26+
PosixReturnCheck.cpp
2627
SizeofContainerCheck.cpp
2728
SizeofExpressionCheck.cpp
2829
StringConstructorCheck.cpp
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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+
14+
using namespace clang::ast_matchers;
15+
16+
namespace clang {
17+
namespace tidy {
18+
namespace bugprone {
19+
20+
static StringRef getFunctionSpelling(const MatchFinder::MatchResult &Result,
21+
const char *BindingStr) {
22+
const CallExpr *MatchedCall = cast<CallExpr>(
23+
(Result.Nodes.getNodeAs<BinaryOperator>(BindingStr))->getLHS());
24+
const SourceManager &SM = *Result.SourceManager;
25+
return Lexer::getSourceText(CharSourceRange::getTokenRange(
26+
MatchedCall->getCallee()->getSourceRange()),
27+
SM, Result.Context->getLangOpts());
28+
}
29+
30+
void PosixReturnCheck::registerMatchers(MatchFinder *Finder) {
31+
Finder->addMatcher(binaryOperator(hasOperatorName("<"),
32+
hasLHS(callExpr(callee(functionDecl(
33+
matchesName("^::posix_"),
34+
unless(hasName("::posix_openpt")))))),
35+
hasRHS(integerLiteral(equals(0))))
36+
.bind("ltzop"),
37+
this);
38+
Finder->addMatcher(binaryOperator(hasOperatorName(">="),
39+
hasLHS(callExpr(callee(functionDecl(
40+
matchesName("^::posix_"),
41+
unless(hasName("::posix_openpt")))))),
42+
hasRHS(integerLiteral(equals(0))))
43+
.bind("atop"),
44+
this);
45+
Finder->addMatcher(
46+
binaryOperator(
47+
anyOf(hasOperatorName("=="), hasOperatorName("!="),
48+
hasOperatorName("<="), hasOperatorName("<")),
49+
hasLHS(callExpr(callee(functionDecl(
50+
matchesName("^::posix_"), unless(hasName("::posix_openpt")))))),
51+
hasRHS(unaryOperator(hasOperatorName("-"),
52+
hasUnaryOperand(integerLiteral()))))
53+
.bind("binop"),
54+
this);
55+
}
56+
57+
void PosixReturnCheck::check(const MatchFinder::MatchResult &Result) {
58+
if (const auto *LessThanZeroOp =
59+
Result.Nodes.getNodeAs<BinaryOperator>("ltzop")) {
60+
SourceLocation OperatorLoc = LessThanZeroOp->getOperatorLoc();
61+
diag(OperatorLoc, "the comparison always evaluates to false because %0 "
62+
"always returns non-negative values")
63+
<< getFunctionSpelling(Result, "ltzop")
64+
<< FixItHint::CreateReplacement(OperatorLoc, Twine(">").str());
65+
return;
66+
}
67+
if (const auto *AlwaysTrueOp =
68+
Result.Nodes.getNodeAs<BinaryOperator>("atop")) {
69+
diag(AlwaysTrueOp->getOperatorLoc(),
70+
"the comparison always evaluates to true because %0 always returns "
71+
"non-negative values")
72+
<< getFunctionSpelling(Result, "atop");
73+
return;
74+
}
75+
const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("binop");
76+
diag(BinOp->getOperatorLoc(), "%0 only returns non-negative values")
77+
<< getFunctionSpelling(Result, "binop");
78+
}
79+
80+
} // namespace bugprone
81+
} // namespace tidy
82+
} // namespace clang
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===--- PosixReturnCheck.h - 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+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_POSIX_RETURN_CHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_POSIX_RETURN_CHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang {
15+
namespace tidy {
16+
namespace bugprone {
17+
18+
class PosixReturnCheck: public ClangTidyCheck {
19+
public:
20+
PosixReturnCheck(StringRef Name, ClangTidyContext *Context)
21+
: ClangTidyCheck(Name, Context) {}
22+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
23+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
24+
};
25+
26+
} // namespace bugprone
27+
} // namespace tidy
28+
} // namespace clang
29+
30+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_POSIX_RETURN_CHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ Improvements to clang-tidy
125125
repeated branches in ``switch`` statements and indentical true and false
126126
branches in conditional operators.
127127

128+
- New :doc:`bugprone-posix-return
129+
<clang-tidy/checks/android-posix-return>` check.
130+
131+
Checks if any calls to POSIX functions (except ``posix_openpt``) expect negative
132+
return values.
133+
128134
- New :doc:`fuchsia-default-arguments-calls
129135
<clang-tidy/checks/fuchsia-default-arguments-calls>` check.
130136

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.. title:: clang-tidy - bugprone-posix-return
2+
3+
bugprone-posix-return
4+
=====================
5+
6+
Checks if any calls to POSIX functions (except ``posix_openpt``) expect negative
7+
return values. These functions return either ``0`` on success or an ``errno`` on failure,
8+
which is positive only.
9+
10+
Example buggy usage looks like:
11+
12+
.. code-block:: c
13+
14+
if (posix_fadvise(...) < 0) {
15+
16+
This will never happen as the return value is always non-negative. A simple fix could be:
17+
18+
.. code-block:: c
19+
20+
if (posix_fadvise(...) > 0) {

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ Clang-Tidy Checks
5858
bugprone-move-forwarding-reference
5959
bugprone-multiple-statement-macro
6060
bugprone-parent-virtual-call
61+
bugprone-posix-return
6162
bugprone-sizeof-container
6263
bugprone-sizeof-expression
6364
bugprone-string-constructor
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// RUN: %check_clang_tidy %s bugprone-posix-return %t
2+
3+
#define NULL nullptr
4+
#define ZERO 0
5+
#define NEGATIVE_ONE -1
6+
7+
typedef long off_t;
8+
typedef decltype(sizeof(int)) size_t;
9+
typedef int pid_t;
10+
typedef struct __posix_spawn_file_actions* posix_spawn_file_actions_t;
11+
typedef struct __posix_spawnattr* posix_spawnattr_t;
12+
13+
extern "C" int posix_fadvise(int fd, off_t offset, off_t len, int advice);
14+
extern "C" int posix_fallocate(int fd, off_t offset, off_t len);
15+
extern "C" int posix_madvise(void *addr, size_t len, int advice);
16+
extern "C" int posix_memalign(void **memptr, size_t alignment, size_t size);
17+
extern "C" int posix_openpt(int flags);
18+
extern "C" int posix_spawn(pid_t *pid, const char *path,
19+
const posix_spawn_file_actions_t *file_actions,
20+
const posix_spawnattr_t *attrp,
21+
char *const argv[], char *const envp[]);
22+
extern "C" int posix_spawnp(pid_t *pid, const char *file,
23+
const posix_spawn_file_actions_t *file_actions,
24+
const posix_spawnattr_t *attrp,
25+
char *const argv[], char *const envp[]);
26+
27+
void warningLessThanZero() {
28+
if (posix_fadvise(0, 0, 0, 0) < 0) {}
29+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: the comparison always evaluates to false because posix_fadvise always returns non-negative values
30+
// CHECK-FIXES: posix_fadvise(0, 0, 0, 0) > 0
31+
if (posix_fallocate(0, 0, 0) < 0) {}
32+
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning:
33+
// CHECK-FIXES: posix_fallocate(0, 0, 0) > 0
34+
if (posix_madvise(NULL, 0, 0) < 0) {}
35+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning:
36+
// CHECK-FIXES: posix_madvise(NULL, 0, 0) > 0
37+
if (posix_memalign(NULL, 0, 0) < 0) {}
38+
// CHECK-MESSAGES: :[[@LINE-1]]:34: warning:
39+
// CHECK-FIXES: posix_memalign(NULL, 0, 0) > 0
40+
if (posix_spawn(NULL, NULL, NULL, NULL, {NULL}, {NULL}) < 0) {}
41+
// CHECK-MESSAGES: :[[@LINE-1]]:59: warning:
42+
// CHECK-FIXES: posix_spawn(NULL, NULL, NULL, NULL, {NULL}, {NULL}) > 0
43+
if (posix_spawnp(NULL, NULL, NULL, NULL, {NULL}, {NULL}) < 0) {}
44+
// CHECK-MESSAGES: :[[@LINE-1]]:60: warning:
45+
// CHECK-FIXES: posix_spawnp(NULL, NULL, NULL, NULL, {NULL}, {NULL}) > 0
46+
}
47+
48+
void warningAlwaysTrue() {
49+
if (posix_fadvise(0, 0, 0, 0) >= 0) {}
50+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: the comparison always evaluates to true because posix_fadvise always returns non-negative values
51+
}
52+
53+
void warningEqualsNegative() {
54+
if (posix_fadvise(0, 0, 0, 0) == -1) {}
55+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: posix_fadvise
56+
if (posix_fadvise(0, 0, 0, 0) != -1) {}
57+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning:
58+
if (posix_fadvise(0, 0, 0, 0) <= -1) {}
59+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning:
60+
if (posix_fadvise(0, 0, 0, 0) < -1) {}
61+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning:
62+
if (posix_fallocate(0, 0, 0) == -1) {}
63+
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning:
64+
if (posix_madvise(NULL, 0, 0) == -1) {}
65+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning:
66+
if (posix_memalign(NULL, 0, 0) == -1) {}
67+
// CHECK-MESSAGES: :[[@LINE-1]]:34: warning:
68+
if (posix_spawn(NULL, NULL, NULL, NULL, {NULL}, {NULL}) == -1) {}
69+
// CHECK-MESSAGES: :[[@LINE-1]]:59: warning:
70+
if (posix_spawnp(NULL, NULL, NULL, NULL, {NULL}, {NULL}) == -1) {}
71+
// CHECK-MESSAGES: :[[@LINE-1]]:60: warning:
72+
}
73+
74+
void WarningWithMacro() {
75+
if (posix_fadvise(0, 0, 0, 0) < ZERO) {}
76+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning:
77+
// CHECK-FIXES: posix_fadvise(0, 0, 0, 0) > ZERO
78+
if (posix_fadvise(0, 0, 0, 0) >= ZERO) {}
79+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning:
80+
if (posix_fadvise(0, 0, 0, 0) == NEGATIVE_ONE) {}
81+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning:
82+
if (posix_fadvise(0, 0, 0, 0) != NEGATIVE_ONE) {}
83+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning:
84+
if (posix_fadvise(0, 0, 0, 0) <= NEGATIVE_ONE) {}
85+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning:
86+
if (posix_fadvise(0, 0, 0, 0) < NEGATIVE_ONE) {}
87+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning:
88+
}
89+
90+
void noWarning() {
91+
if (posix_openpt(0) < 0) {}
92+
if (posix_openpt(0) <= 0) {}
93+
if (posix_openpt(0) == -1) {}
94+
if (posix_openpt(0) != -1) {}
95+
if (posix_openpt(0) <= -1) {}
96+
if (posix_openpt(0) < -1) {}
97+
if (posix_fadvise(0, 0, 0, 0) <= 0) {}
98+
if (posix_fadvise(0, 0, 0, 0) == 1) {}
99+
}
100+
101+
namespace i {
102+
int posix_fadvise(int fd, off_t offset, off_t len, int advice);
103+
104+
void noWarning() {
105+
if (posix_fadvise(0, 0, 0, 0) < 0) {}
106+
if (posix_fadvise(0, 0, 0, 0) >= 0) {}
107+
if (posix_fadvise(0, 0, 0, 0) == -1) {}
108+
if (posix_fadvise(0, 0, 0, 0) != -1) {}
109+
if (posix_fadvise(0, 0, 0, 0) <= -1) {}
110+
if (posix_fadvise(0, 0, 0, 0) < -1) {}
111+
}
112+
113+
} // namespace i
114+
115+
class G {
116+
public:
117+
int posix_fadvise(int fd, off_t offset, off_t len, int advice);
118+
119+
void noWarning() {
120+
if (posix_fadvise(0, 0, 0, 0) < 0) {}
121+
if (posix_fadvise(0, 0, 0, 0) >= 0) {}
122+
if (posix_fadvise(0, 0, 0, 0) == -1) {}
123+
if (posix_fadvise(0, 0, 0, 0) != -1) {}
124+
if (posix_fadvise(0, 0, 0, 0) <= -1) {}
125+
if (posix_fadvise(0, 0, 0, 0) < -1) {}
126+
}
127+
};

0 commit comments

Comments
 (0)