10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/Expr.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Frontend/CompilerInstance.h"
14 #include "clang/Lex/Lexer.h"
15 #include "llvm/ADT/SmallVector.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/Support/Casting.h"
30 auto NegatedString = unaryOperator(
31 hasOperatorName(
"!"), hasUnaryOperand(ignoringImpCasts(stringLiteral())));
33 expr(anyOf(cxxBoolLiteral(equals(
false)), integerLiteral(equals(0)),
34 cxxNullPtrLiteralExpr(), gnuNullExpr(), NegatedString))
35 .bind(
"isAlwaysFalse");
36 auto IsAlwaysFalseWithCast = ignoringParenImpCasts(anyOf(
37 IsAlwaysFalse, cStyleCastExpr(has(ignoringParenImpCasts(IsAlwaysFalse)))
39 auto AssertExprRoot = anyOf(
41 hasAnyOperatorName(
"&&",
"=="),
42 hasEitherOperand(ignoringImpCasts(stringLiteral().bind(
"assertMSG"))),
43 anyOf(binaryOperator(hasEitherOperand(IsAlwaysFalseWithCast)),
45 .bind(
"assertExprRoot"),
47 auto NonConstexprFunctionCall =
48 callExpr(hasDeclaration(functionDecl(unless(isConstexpr()))));
49 auto AssertCondition =
51 anyOf(expr(ignoringParenCasts(anyOf(
52 AssertExprRoot, unaryOperator(hasUnaryOperand(
53 ignoringParenCasts(AssertExprRoot)))))),
55 unless(findAll(NonConstexprFunctionCall)))
58 anyOf(ignoringParenImpCasts(callExpr(
59 hasDeclaration(functionDecl(hasName(
"__builtin_expect"))),
60 hasArgument(0, AssertCondition))),
63 Finder->addMatcher(conditionalOperator(hasCondition(
Condition),
64 unless(isInTemplateInstantiation()))
69 ifStmt(hasCondition(
Condition), unless(isInTemplateInstantiation()))
75 const ASTContext *ASTCtx = Result.Context;
76 const LangOptions &Opts = ASTCtx->getLangOpts();
77 const SourceManager &SM = ASTCtx->getSourceManager();
78 const auto *CondStmt = Result.Nodes.getNodeAs<Stmt>(
"condStmt");
79 const auto *
Condition = Result.Nodes.getNodeAs<Expr>(
"condition");
80 const auto *IsAlwaysFalse = Result.Nodes.getNodeAs<Expr>(
"isAlwaysFalse");
81 const auto *AssertMSG = Result.Nodes.getNodeAs<StringLiteral>(
"assertMSG");
82 const auto *AssertExprRoot =
83 Result.Nodes.getNodeAs<BinaryOperator>(
"assertExprRoot");
84 const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>(
"castExpr");
85 SourceLocation AssertExpansionLoc = CondStmt->getBeginLoc();
87 if (!AssertExpansionLoc.isValid() || !AssertExpansionLoc.isMacroID())
91 Lexer::getImmediateMacroName(AssertExpansionLoc, SM, Opts);
93 if (MacroName !=
"assert" ||
Condition->isValueDependent() ||
99 if (IsAlwaysFalse && (!CastExpr || CastExpr->getType()->isPointerType())) {
100 SourceLocation FalseLiteralLoc =
101 SM.getImmediateSpellingLoc(IsAlwaysFalse->getExprLoc());
102 if (!FalseLiteralLoc.isMacroID())
105 StringRef FalseMacroName =
106 Lexer::getImmediateMacroName(FalseLiteralLoc, SM, Opts);
107 if (FalseMacroName.compare_insensitive(
"false") == 0 ||
108 FalseMacroName.compare_insensitive(
"null") == 0)
112 SourceLocation AssertLoc = SM.getImmediateMacroCallerLoc(AssertExpansionLoc);
114 SmallVector<FixItHint, 4> FixItHints;
115 SourceLocation LastParenLoc;
116 if (AssertLoc.isValid() && !AssertLoc.isMacroID() &&
117 (LastParenLoc = getLastParenLoc(ASTCtx, AssertLoc)).
isValid()) {
118 FixItHints.push_back(
119 FixItHint::CreateReplacement(SourceRange(AssertLoc),
"static_assert"));
121 if (AssertExprRoot) {
122 FixItHints.push_back(FixItHint::CreateRemoval(
123 SourceRange(AssertExprRoot->getOperatorLoc())));
124 FixItHints.push_back(FixItHint::CreateRemoval(
125 SourceRange(AssertMSG->getBeginLoc(), AssertMSG->getEndLoc())));
126 FixItHints.push_back(FixItHint::CreateInsertion(
127 LastParenLoc, (Twine(
", \"") + AssertMSG->getString() +
"\"").str()));
128 }
else if (!Opts.CPlusPlus17) {
129 FixItHints.push_back(FixItHint::CreateInsertion(LastParenLoc,
", \"\""));
133 diag(AssertLoc,
"found assert() that could be replaced by static_assert()")
137 SourceLocation StaticAssertCheck::getLastParenLoc(
const ASTContext *ASTCtx,
138 SourceLocation AssertLoc) {
139 const LangOptions &Opts = ASTCtx->getLangOpts();
140 const SourceManager &SM = ASTCtx->getSourceManager();
142 llvm::Optional<llvm::MemoryBufferRef> Buffer =
143 SM.getBufferOrNone(SM.getFileID(AssertLoc));
145 return SourceLocation();
147 const char *BufferPos = SM.getCharacterData(AssertLoc);
150 Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(AssertLoc)), Opts,
151 Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
154 if (Lexer.LexFromRawLexer(Token) || Lexer.LexFromRawLexer(Token) ||
155 !Token.is(tok::l_paren))
156 return SourceLocation();
158 unsigned int ParenCount = 1;
159 while (ParenCount && !Lexer.LexFromRawLexer(Token)) {
160 if (Token.is(tok::l_paren))
162 else if (Token.is(tok::r_paren))
166 return Token.getLocation();