1 //===--- ComparisonInTempFailureRetryCheck.cpp - clang-tidy----------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "../utils/Matchers.h" 11 #include "ComparisonInTempFailureRetryCheck.h" 12 #include "clang/AST/ASTContext.h" 13 #include "clang/ASTMatchers/ASTMatchFinder.h" 14 #include "clang/Lex/Lexer.h" 15 16 using namespace clang::ast_matchers; 17 18 namespace clang { 19 namespace tidy { 20 namespace android { 21 22 namespace { 23 AST_MATCHER(BinaryOperator, isRHSATempFailureRetryArg) { 24 if (!Node.getBeginLoc().isMacroID()) 25 return false; 26 27 const SourceManager &SM = Finder->getASTContext().getSourceManager(); 28 if (!SM.isMacroArgExpansion(Node.getRHS()->IgnoreParenCasts()->getBeginLoc())) 29 return false; 30 31 const LangOptions &Opts = Finder->getASTContext().getLangOpts(); 32 SourceLocation LocStart = Node.getBeginLoc(); 33 while (LocStart.isMacroID()) { 34 SourceLocation Invocation = SM.getImmediateMacroCallerLoc(LocStart); 35 Token Tok; 36 if (!Lexer::getRawToken(SM.getSpellingLoc(Invocation), Tok, SM, Opts, 37 /*IgnoreWhiteSpace=*/true)) { 38 if (Tok.getKind() == tok::raw_identifier && 39 Tok.getRawIdentifier() == "TEMP_FAILURE_RETRY") 40 return true; 41 } 42 43 LocStart = Invocation; 44 } 45 return false; 46 } 47 } // namespace 48 49 void ComparisonInTempFailureRetryCheck::registerMatchers(MatchFinder *Finder) { 50 // Both glibc's and Bionic's TEMP_FAILURE_RETRY macros structurally look like: 51 // 52 // #define TEMP_FAILURE_RETRY(x) ({ \ 53 // typeof(x) y; \ 54 // do y = (x); \ 55 // while (y == -1 && errno == EINTR); \ 56 // y; \ 57 // }) 58 // 59 // (glibc uses `long int` instead of `typeof(x)` for the type of y). 60 // 61 // It's unclear how to walk up the AST from inside the expansion of `x`, and 62 // we need to not complain about things like TEMP_FAILURE_RETRY(foo(x == 1)), 63 // so we just match the assignment of `y = (x)` and inspect `x` from there. 64 Finder->addMatcher( 65 binaryOperator( 66 hasOperatorName("="), 67 hasRHS(ignoringParenCasts( 68 binaryOperator(matchers::isComparisonOperator()).bind("binop"))), 69 isRHSATempFailureRetryArg()), 70 this); 71 } 72 73 void ComparisonInTempFailureRetryCheck::check( 74 const MatchFinder::MatchResult &Result) { 75 const auto &BinOp = *Result.Nodes.getNodeAs<BinaryOperator>("binop"); 76 diag(BinOp.getOperatorLoc(), "top-level comparison in TEMP_FAILURE_RETRY"); 77 78 // FIXME: FixIts would be nice, but potentially nontrivial when nested macros 79 // happen, e.g. `TEMP_FAILURE_RETRY(IS_ZERO(foo()))` 80 } 81 82 } // namespace android 83 } // namespace tidy 84 } // namespace clang 85