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"
21 using namespace clang::ast_matchers;
35 auto IsAlwaysFalse = expr(ignoringParenImpCasts(
36 expr(anyOf(cxxBoolLiteral(equals(
false)), integerLiteral(equals(0)),
37 cxxNullPtrLiteralExpr(), gnuNullExpr()))
38 .bind(
"isAlwaysFalse")));
39 auto IsAlwaysFalseWithCast = ignoringParenImpCasts(anyOf(
40 IsAlwaysFalse, cStyleCastExpr(has(IsAlwaysFalse)).bind(
"castExpr")));
41 auto AssertExprRoot = anyOf(
43 anyOf(hasOperatorName(
"&&"), hasOperatorName(
"==")),
44 hasEitherOperand(ignoringImpCasts(stringLiteral().bind(
"assertMSG"))),
45 anyOf(binaryOperator(hasEitherOperand(IsAlwaysFalseWithCast)),
47 .bind(
"assertExprRoot"),
49 auto NonConstexprFunctionCall =
50 callExpr(hasDeclaration(functionDecl(unless(isConstexpr()))));
51 auto AssertCondition =
53 anyOf(expr(ignoringParenCasts(anyOf(
54 AssertExprRoot, unaryOperator(hasUnaryOperand(
55 ignoringParenCasts(AssertExprRoot)))))),
57 unless(findAll(NonConstexprFunctionCall)))
60 anyOf(ignoringParenImpCasts(callExpr(
61 hasDeclaration(functionDecl(hasName(
"__builtin_expect"))),
62 hasArgument(0, AssertCondition))),
65 Finder->addMatcher(stmt(anyOf(conditionalOperator(hasCondition(Condition)),
66 ifStmt(hasCondition(Condition))),
67 unless(isInTemplateInstantiation()))
73 const ASTContext *ASTCtx = Result.Context;
74 const LangOptions &Opts = ASTCtx->getLangOpts();
75 const SourceManager &
SM = ASTCtx->getSourceManager();
76 const auto *CondStmt = Result.Nodes.getNodeAs<Stmt>(
"condStmt");
77 const auto *Condition = Result.Nodes.getNodeAs<Expr>(
"condition");
78 const auto *IsAlwaysFalse = Result.Nodes.getNodeAs<Expr>(
"isAlwaysFalse");
79 const auto *AssertMSG = Result.Nodes.getNodeAs<StringLiteral>(
"assertMSG");
80 const auto *AssertExprRoot =
81 Result.Nodes.getNodeAs<BinaryOperator>(
"assertExprRoot");
82 const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>(
"castExpr");
83 SourceLocation AssertExpansionLoc = CondStmt->getLocStart();
85 if (!AssertExpansionLoc.isValid() || !AssertExpansionLoc.isMacroID())
89 Lexer::getImmediateMacroName(AssertExpansionLoc, SM, Opts);
91 if (MacroName !=
"assert" || Condition->isValueDependent() ||
92 Condition->isTypeDependent() || Condition->isInstantiationDependent() ||
93 !Condition->isEvaluatable(*ASTCtx))
97 if (IsAlwaysFalse && (!CastExpr || CastExpr->getType()->isPointerType())) {
98 SourceLocation FalseLiteralLoc =
99 SM.getImmediateSpellingLoc(IsAlwaysFalse->getExprLoc());
100 if (!FalseLiteralLoc.isMacroID())
103 StringRef FalseMacroName =
104 Lexer::getImmediateMacroName(FalseLiteralLoc, SM, Opts);
105 if (FalseMacroName.compare_lower(
"false") == 0 ||
106 FalseMacroName.compare_lower(
"null") == 0)
110 SourceLocation AssertLoc = SM.getImmediateMacroCallerLoc(AssertExpansionLoc);
112 SmallVector<FixItHint, 4> FixItHints;
113 SourceLocation LastParenLoc;
114 if (AssertLoc.isValid() && !AssertLoc.isMacroID() &&
115 (LastParenLoc = getLastParenLoc(ASTCtx, AssertLoc)).isValid()) {
116 FixItHints.push_back(
117 FixItHint::CreateReplacement(SourceRange(AssertLoc),
"static_assert"));
119 std::string StaticAssertMSG =
", \"\"";
120 if (AssertExprRoot) {
121 FixItHints.push_back(FixItHint::CreateRemoval(
122 SourceRange(AssertExprRoot->getOperatorLoc())));
123 FixItHints.push_back(FixItHint::CreateRemoval(
124 SourceRange(AssertMSG->getLocStart(), AssertMSG->getLocEnd())));
125 StaticAssertMSG = (Twine(
", \"") + AssertMSG->getString() +
"\"").str();
128 FixItHints.push_back(
129 FixItHint::CreateInsertion(LastParenLoc, StaticAssertMSG));
132 diag(AssertLoc,
"found assert() that could be replaced by static_assert()")
136 SourceLocation StaticAssertCheck::getLastParenLoc(
const ASTContext *ASTCtx,
137 SourceLocation AssertLoc) {
138 const LangOptions &Opts = ASTCtx->getLangOpts();
139 const SourceManager &
SM = ASTCtx->getSourceManager();
141 llvm::MemoryBuffer *Buffer = SM.getBuffer(SM.getFileID(AssertLoc));
143 return SourceLocation();
145 const char *BufferPos = SM.getCharacterData(AssertLoc);
148 Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(AssertLoc)), Opts,
149 Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
152 if (Lexer.LexFromRawLexer(Token) || Lexer.LexFromRawLexer(Token) ||
153 !Token.is(tok::l_paren))
154 return SourceLocation();
156 unsigned int ParenCount = 1;
157 while (ParenCount && !Lexer.LexFromRawLexer(Token)) {
158 if (Token.is(tok::l_paren))
160 else if (Token.is(tok::r_paren))
164 return Token.getLocation();
LangOptions getLangOpts() const
Returns the language options from the context.
std::unique_ptr< ast_matchers::MatchFinder > Finder
Base class for all clang-tidy checks.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register ASTMatchers with Finder.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
ClangTidyContext & Context
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.