1 //===--- StaticAssertCheck.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 "StaticAssertCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/AST/Expr.h" 13 #include "clang/ASTMatchers/ASTMatchFinder.h" 14 #include "clang/Frontend/CompilerInstance.h" 15 #include "clang/Lex/Lexer.h" 16 #include "llvm/ADT/SmallVector.h" 17 #include "llvm/ADT/StringRef.h" 18 #include "llvm/Support/Casting.h" 19 #include <string> 20 21 using namespace clang::ast_matchers; 22 23 namespace clang { 24 namespace tidy { 25 namespace misc { 26 27 StaticAssertCheck::StaticAssertCheck(StringRef Name, ClangTidyContext *Context) 28 : ClangTidyCheck(Name, Context) {} 29 30 void StaticAssertCheck::registerMatchers(MatchFinder *Finder) { 31 // This checker only makes sense for languages that have static assertion 32 // capabilities: C++11 and C11. 33 if (!(getLangOpts().CPlusPlus11 || getLangOpts().C11)) 34 return; 35 36 auto NegatedString = unaryOperator( 37 hasOperatorName("!"), hasUnaryOperand(ignoringImpCasts(stringLiteral()))); 38 auto IsAlwaysFalse = 39 expr(anyOf(cxxBoolLiteral(equals(false)), integerLiteral(equals(0)), 40 cxxNullPtrLiteralExpr(), gnuNullExpr(), NegatedString)) 41 .bind("isAlwaysFalse"); 42 auto IsAlwaysFalseWithCast = ignoringParenImpCasts(anyOf( 43 IsAlwaysFalse, cStyleCastExpr(has(ignoringParenImpCasts(IsAlwaysFalse))) 44 .bind("castExpr"))); 45 auto AssertExprRoot = anyOf( 46 binaryOperator( 47 anyOf(hasOperatorName("&&"), hasOperatorName("==")), 48 hasEitherOperand(ignoringImpCasts(stringLiteral().bind("assertMSG"))), 49 anyOf(binaryOperator(hasEitherOperand(IsAlwaysFalseWithCast)), 50 anything())) 51 .bind("assertExprRoot"), 52 IsAlwaysFalse); 53 auto NonConstexprFunctionCall = 54 callExpr(hasDeclaration(functionDecl(unless(isConstexpr())))); 55 auto AssertCondition = 56 expr( 57 anyOf(expr(ignoringParenCasts(anyOf( 58 AssertExprRoot, unaryOperator(hasUnaryOperand( 59 ignoringParenCasts(AssertExprRoot)))))), 60 anything()), 61 unless(findAll(NonConstexprFunctionCall))) 62 .bind("condition"); 63 auto Condition = 64 anyOf(ignoringParenImpCasts(callExpr( 65 hasDeclaration(functionDecl(hasName("__builtin_expect"))), 66 hasArgument(0, AssertCondition))), 67 AssertCondition); 68 69 Finder->addMatcher(conditionalOperator(hasCondition(Condition), 70 unless(isInTemplateInstantiation())) 71 .bind("condStmt"), 72 this); 73 74 Finder->addMatcher( 75 ifStmt(hasCondition(Condition), unless(isInTemplateInstantiation())) 76 .bind("condStmt"), 77 this); 78 } 79 80 void StaticAssertCheck::check(const MatchFinder::MatchResult &Result) { 81 const ASTContext *ASTCtx = Result.Context; 82 const LangOptions &Opts = ASTCtx->getLangOpts(); 83 const SourceManager &SM = ASTCtx->getSourceManager(); 84 const auto *CondStmt = Result.Nodes.getNodeAs<Stmt>("condStmt"); 85 const auto *Condition = Result.Nodes.getNodeAs<Expr>("condition"); 86 const auto *IsAlwaysFalse = Result.Nodes.getNodeAs<Expr>("isAlwaysFalse"); 87 const auto *AssertMSG = Result.Nodes.getNodeAs<StringLiteral>("assertMSG"); 88 const auto *AssertExprRoot = 89 Result.Nodes.getNodeAs<BinaryOperator>("assertExprRoot"); 90 const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("castExpr"); 91 SourceLocation AssertExpansionLoc = CondStmt->getLocStart(); 92 93 if (!AssertExpansionLoc.isValid() || !AssertExpansionLoc.isMacroID()) 94 return; 95 96 StringRef MacroName = 97 Lexer::getImmediateMacroName(AssertExpansionLoc, SM, Opts); 98 99 if (MacroName != "assert" || Condition->isValueDependent() || 100 Condition->isTypeDependent() || Condition->isInstantiationDependent() || 101 !Condition->isEvaluatable(*ASTCtx)) 102 return; 103 104 // False literal is not the result of macro expansion. 105 if (IsAlwaysFalse && (!CastExpr || CastExpr->getType()->isPointerType())) { 106 SourceLocation FalseLiteralLoc = 107 SM.getImmediateSpellingLoc(IsAlwaysFalse->getExprLoc()); 108 if (!FalseLiteralLoc.isMacroID()) 109 return; 110 111 StringRef FalseMacroName = 112 Lexer::getImmediateMacroName(FalseLiteralLoc, SM, Opts); 113 if (FalseMacroName.compare_lower("false") == 0 || 114 FalseMacroName.compare_lower("null") == 0) 115 return; 116 } 117 118 SourceLocation AssertLoc = SM.getImmediateMacroCallerLoc(AssertExpansionLoc); 119 120 SmallVector<FixItHint, 4> FixItHints; 121 SourceLocation LastParenLoc; 122 if (AssertLoc.isValid() && !AssertLoc.isMacroID() && 123 (LastParenLoc = getLastParenLoc(ASTCtx, AssertLoc)).isValid()) { 124 FixItHints.push_back( 125 FixItHint::CreateReplacement(SourceRange(AssertLoc), "static_assert")); 126 127 std::string StaticAssertMSG = ", \"\""; 128 if (AssertExprRoot) { 129 FixItHints.push_back(FixItHint::CreateRemoval( 130 SourceRange(AssertExprRoot->getOperatorLoc()))); 131 FixItHints.push_back(FixItHint::CreateRemoval( 132 SourceRange(AssertMSG->getLocStart(), AssertMSG->getLocEnd()))); 133 StaticAssertMSG = (Twine(", \"") + AssertMSG->getString() + "\"").str(); 134 } 135 136 FixItHints.push_back( 137 FixItHint::CreateInsertion(LastParenLoc, StaticAssertMSG)); 138 } 139 140 diag(AssertLoc, "found assert() that could be replaced by static_assert()") 141 << FixItHints; 142 } 143 144 SourceLocation StaticAssertCheck::getLastParenLoc(const ASTContext *ASTCtx, 145 SourceLocation AssertLoc) { 146 const LangOptions &Opts = ASTCtx->getLangOpts(); 147 const SourceManager &SM = ASTCtx->getSourceManager(); 148 149 llvm::MemoryBuffer *Buffer = SM.getBuffer(SM.getFileID(AssertLoc)); 150 if (!Buffer) 151 return SourceLocation(); 152 153 const char *BufferPos = SM.getCharacterData(AssertLoc); 154 155 Token Token; 156 Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(AssertLoc)), Opts, 157 Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd()); 158 159 // assert first left parenthesis 160 if (Lexer.LexFromRawLexer(Token) || Lexer.LexFromRawLexer(Token) || 161 !Token.is(tok::l_paren)) 162 return SourceLocation(); 163 164 unsigned int ParenCount = 1; 165 while (ParenCount && !Lexer.LexFromRawLexer(Token)) { 166 if (Token.is(tok::l_paren)) 167 ++ParenCount; 168 else if (Token.is(tok::r_paren)) 169 --ParenCount; 170 } 171 172 return Token.getLocation(); 173 } 174 175 } // namespace misc 176 } // namespace tidy 177 } // namespace clang 178