1 //===--- ComparisonInTempFailureRetryCheck.cpp - clang-tidy----------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "../utils/Matchers.h"
10 #include "ComparisonInTempFailureRetryCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
15 using namespace clang::ast_matchers
;
17 namespace clang::tidy::android
{
19 ComparisonInTempFailureRetryCheck::ComparisonInTempFailureRetryCheck(
20 StringRef Name
, ClangTidyContext
*Context
)
21 : ClangTidyCheck(Name
, Context
),
22 RawRetryList(Options
.get("RetryMacros", "TEMP_FAILURE_RETRY")) {
23 StringRef(RawRetryList
).split(RetryMacros
, ",", -1, false);
26 void ComparisonInTempFailureRetryCheck::storeOptions(
27 ClangTidyOptions::OptionMap
&Opts
) {
28 Options
.store(Opts
, "RetryMacros", RawRetryList
);
31 void ComparisonInTempFailureRetryCheck::registerMatchers(MatchFinder
*Finder
) {
32 // Both glibc's and Bionic's TEMP_FAILURE_RETRY macros structurally look like:
34 // #define TEMP_FAILURE_RETRY(x) ({ \
37 // while (y == -1 && errno == EINTR); \
41 // (glibc uses `long int` instead of `typeof(x)` for the type of y).
43 // It's unclear how to walk up the AST from inside the expansion of `x`, and
44 // we need to not complain about things like TEMP_FAILURE_RETRY(foo(x == 1)),
45 // so we just match the assignment of `y = (x)` and inspect `x` from there.
47 binaryOperator(hasOperatorName("="),
48 hasRHS(ignoringParenCasts(
49 binaryOperator(isComparisonOperator()).bind("inner"))))
54 void ComparisonInTempFailureRetryCheck::check(
55 const MatchFinder::MatchResult
&Result
) {
56 StringRef RetryMacroName
;
57 const auto &Node
= *Result
.Nodes
.getNodeAs
<BinaryOperator
>("outer");
58 if (!Node
.getBeginLoc().isMacroID())
61 const SourceManager
&SM
= *Result
.SourceManager
;
62 if (!SM
.isMacroArgExpansion(Node
.getRHS()->IgnoreParenCasts()->getBeginLoc()))
65 const LangOptions
&Opts
= Result
.Context
->getLangOpts();
66 SourceLocation LocStart
= Node
.getBeginLoc();
67 while (LocStart
.isMacroID()) {
68 SourceLocation Invocation
= SM
.getImmediateMacroCallerLoc(LocStart
);
70 if (!Lexer::getRawToken(SM
.getSpellingLoc(Invocation
), Tok
, SM
, Opts
,
71 /*IgnoreWhiteSpace=*/true)) {
72 if (Tok
.getKind() == tok::raw_identifier
&&
73 llvm::is_contained(RetryMacros
, Tok
.getRawIdentifier())) {
74 RetryMacroName
= Tok
.getRawIdentifier();
79 LocStart
= Invocation
;
81 if (RetryMacroName
.empty())
84 const auto &Inner
= *Result
.Nodes
.getNodeAs
<BinaryOperator
>("inner");
85 diag(Inner
.getOperatorLoc(), "top-level comparison in %0") << RetryMacroName
;
87 // FIXME: FixIts would be nice, but potentially nontrivial when nested macros
88 // happen, e.g. `TEMP_FAILURE_RETRY(IS_ZERO(foo()))`
91 } // namespace clang::tidy::android