2015-03-02 13:46:43 +03:00
|
|
|
//===--- StaticAssertCheck.cpp - clang-tidy -------------------------------===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "StaticAssertCheck.h"
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
|
|
#include "clang/AST/Expr.h"
|
|
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
|
|
#include "clang/Lex/Lexer.h"
|
|
|
|
#include "llvm/ADT/SmallVector.h"
|
|
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
#include "llvm/Support/Casting.h"
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
using namespace clang::ast_matchers;
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace tidy {
|
2016-05-02 21:00:29 +03:00
|
|
|
namespace misc {
|
2015-03-02 13:46:43 +03:00
|
|
|
|
|
|
|
StaticAssertCheck::StaticAssertCheck(StringRef Name, ClangTidyContext *Context)
|
|
|
|
: ClangTidyCheck(Name, Context) {}
|
|
|
|
|
|
|
|
void StaticAssertCheck::registerMatchers(MatchFinder *Finder) {
|
2015-09-01 00:54:42 +03:00
|
|
|
// This checker only makes sense for languages that have static assertion
|
|
|
|
// capabilities: C++11 and C11.
|
|
|
|
if (!(getLangOpts().CPlusPlus11 || getLangOpts().C11))
|
2015-08-31 18:28:57 +03:00
|
|
|
return;
|
2015-03-02 13:46:43 +03:00
|
|
|
|
2015-08-31 18:28:57 +03:00
|
|
|
auto IsAlwaysFalse = expr(ignoringParenImpCasts(
|
2015-09-17 16:31:25 +03:00
|
|
|
expr(anyOf(cxxBoolLiteral(equals(false)), integerLiteral(equals(0)),
|
|
|
|
cxxNullPtrLiteralExpr(), gnuNullExpr()))
|
2015-08-31 18:28:57 +03:00
|
|
|
.bind("isAlwaysFalse")));
|
|
|
|
auto IsAlwaysFalseWithCast = ignoringParenImpCasts(anyOf(
|
|
|
|
IsAlwaysFalse, cStyleCastExpr(has(IsAlwaysFalse)).bind("castExpr")));
|
|
|
|
auto AssertExprRoot = anyOf(
|
|
|
|
binaryOperator(
|
|
|
|
anyOf(hasOperatorName("&&"), hasOperatorName("==")),
|
|
|
|
hasEitherOperand(ignoringImpCasts(stringLiteral().bind("assertMSG"))),
|
|
|
|
anyOf(binaryOperator(hasEitherOperand(IsAlwaysFalseWithCast)),
|
|
|
|
anything()))
|
|
|
|
.bind("assertExprRoot"),
|
|
|
|
IsAlwaysFalse);
|
|
|
|
auto NonConstexprFunctionCall =
|
|
|
|
callExpr(hasDeclaration(functionDecl(unless(isConstexpr()))));
|
|
|
|
auto AssertCondition =
|
|
|
|
expr(
|
|
|
|
anyOf(expr(ignoringParenCasts(anyOf(
|
|
|
|
AssertExprRoot, unaryOperator(hasUnaryOperand(
|
|
|
|
ignoringParenCasts(AssertExprRoot)))))),
|
|
|
|
anything()),
|
|
|
|
unless(findAll(NonConstexprFunctionCall)))
|
|
|
|
.bind("condition");
|
|
|
|
auto Condition =
|
|
|
|
anyOf(ignoringParenImpCasts(callExpr(
|
|
|
|
hasDeclaration(functionDecl(hasName("__builtin_expect"))),
|
|
|
|
hasArgument(0, AssertCondition))),
|
|
|
|
AssertCondition);
|
|
|
|
|
2016-05-03 23:11:09 +03:00
|
|
|
Finder->addMatcher(conditionalOperator(hasCondition(Condition),
|
|
|
|
unless(isInTemplateInstantiation()))
|
2015-08-31 18:28:57 +03:00
|
|
|
.bind("condStmt"),
|
|
|
|
this);
|
2016-05-03 23:11:09 +03:00
|
|
|
|
|
|
|
Finder->addMatcher(
|
|
|
|
ifStmt(hasCondition(Condition), unless(isInTemplateInstantiation()))
|
|
|
|
.bind("condStmt"),
|
|
|
|
this);
|
2015-03-02 13:46:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void StaticAssertCheck::check(const MatchFinder::MatchResult &Result) {
|
|
|
|
const ASTContext *ASTCtx = Result.Context;
|
|
|
|
const LangOptions &Opts = ASTCtx->getLangOpts();
|
|
|
|
const SourceManager &SM = ASTCtx->getSourceManager();
|
|
|
|
const auto *CondStmt = Result.Nodes.getNodeAs<Stmt>("condStmt");
|
|
|
|
const auto *Condition = Result.Nodes.getNodeAs<Expr>("condition");
|
|
|
|
const auto *IsAlwaysFalse = Result.Nodes.getNodeAs<Expr>("isAlwaysFalse");
|
|
|
|
const auto *AssertMSG = Result.Nodes.getNodeAs<StringLiteral>("assertMSG");
|
|
|
|
const auto *AssertExprRoot =
|
|
|
|
Result.Nodes.getNodeAs<BinaryOperator>("assertExprRoot");
|
2015-04-10 16:55:39 +03:00
|
|
|
const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("castExpr");
|
2015-03-02 13:46:43 +03:00
|
|
|
SourceLocation AssertExpansionLoc = CondStmt->getLocStart();
|
|
|
|
|
2015-08-28 22:27:19 +03:00
|
|
|
if (!AssertExpansionLoc.isValid() || !AssertExpansionLoc.isMacroID())
|
2015-03-02 13:46:43 +03:00
|
|
|
return;
|
|
|
|
|
|
|
|
StringRef MacroName =
|
|
|
|
Lexer::getImmediateMacroName(AssertExpansionLoc, SM, Opts);
|
|
|
|
|
2015-03-09 05:27:57 +03:00
|
|
|
if (MacroName != "assert" || Condition->isValueDependent() ||
|
|
|
|
Condition->isTypeDependent() || Condition->isInstantiationDependent() ||
|
|
|
|
!Condition->isEvaluatable(*ASTCtx))
|
2015-03-02 13:46:43 +03:00
|
|
|
return;
|
|
|
|
|
|
|
|
// False literal is not the result of macro expansion.
|
2015-04-10 16:55:39 +03:00
|
|
|
if (IsAlwaysFalse && (!CastExpr || CastExpr->getType()->isPointerType())) {
|
2015-03-15 05:19:37 +03:00
|
|
|
SourceLocation FalseLiteralLoc =
|
|
|
|
SM.getImmediateSpellingLoc(IsAlwaysFalse->getExprLoc());
|
|
|
|
if (!FalseLiteralLoc.isMacroID())
|
|
|
|
return;
|
|
|
|
|
|
|
|
StringRef FalseMacroName =
|
|
|
|
Lexer::getImmediateMacroName(FalseLiteralLoc, SM, Opts);
|
2015-04-10 16:55:39 +03:00
|
|
|
if (FalseMacroName.compare_lower("false") == 0 ||
|
|
|
|
FalseMacroName.compare_lower("null") == 0)
|
2015-03-15 05:19:37 +03:00
|
|
|
return;
|
|
|
|
}
|
2015-03-02 13:46:43 +03:00
|
|
|
|
|
|
|
SourceLocation AssertLoc = SM.getImmediateMacroCallerLoc(AssertExpansionLoc);
|
|
|
|
|
|
|
|
SmallVector<FixItHint, 4> FixItHints;
|
|
|
|
SourceLocation LastParenLoc;
|
|
|
|
if (AssertLoc.isValid() && !AssertLoc.isMacroID() &&
|
|
|
|
(LastParenLoc = getLastParenLoc(ASTCtx, AssertLoc)).isValid()) {
|
|
|
|
FixItHints.push_back(
|
|
|
|
FixItHint::CreateReplacement(SourceRange(AssertLoc), "static_assert"));
|
|
|
|
|
|
|
|
std::string StaticAssertMSG = ", \"\"";
|
|
|
|
if (AssertExprRoot) {
|
|
|
|
FixItHints.push_back(FixItHint::CreateRemoval(
|
|
|
|
SourceRange(AssertExprRoot->getOperatorLoc())));
|
|
|
|
FixItHints.push_back(FixItHint::CreateRemoval(
|
|
|
|
SourceRange(AssertMSG->getLocStart(), AssertMSG->getLocEnd())));
|
|
|
|
StaticAssertMSG = (Twine(", \"") + AssertMSG->getString() + "\"").str();
|
|
|
|
}
|
|
|
|
|
|
|
|
FixItHints.push_back(
|
|
|
|
FixItHint::CreateInsertion(LastParenLoc, StaticAssertMSG));
|
|
|
|
}
|
|
|
|
|
|
|
|
diag(AssertLoc, "found assert() that could be replaced by static_assert()")
|
|
|
|
<< FixItHints;
|
|
|
|
}
|
|
|
|
|
|
|
|
SourceLocation StaticAssertCheck::getLastParenLoc(const ASTContext *ASTCtx,
|
|
|
|
SourceLocation AssertLoc) {
|
|
|
|
const LangOptions &Opts = ASTCtx->getLangOpts();
|
|
|
|
const SourceManager &SM = ASTCtx->getSourceManager();
|
|
|
|
|
|
|
|
llvm::MemoryBuffer *Buffer = SM.getBuffer(SM.getFileID(AssertLoc));
|
|
|
|
if (!Buffer)
|
|
|
|
return SourceLocation();
|
|
|
|
|
|
|
|
const char *BufferPos = SM.getCharacterData(AssertLoc);
|
|
|
|
|
|
|
|
Token Token;
|
|
|
|
Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(AssertLoc)), Opts,
|
|
|
|
Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
|
|
|
|
|
|
|
|
// assert first left parenthesis
|
|
|
|
if (Lexer.LexFromRawLexer(Token) || Lexer.LexFromRawLexer(Token) ||
|
|
|
|
!Token.is(tok::l_paren))
|
|
|
|
return SourceLocation();
|
|
|
|
|
|
|
|
unsigned int ParenCount = 1;
|
|
|
|
while (ParenCount && !Lexer.LexFromRawLexer(Token)) {
|
|
|
|
if (Token.is(tok::l_paren))
|
|
|
|
++ParenCount;
|
|
|
|
else if (Token.is(tok::r_paren))
|
|
|
|
--ParenCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Token.getLocation();
|
|
|
|
}
|
|
|
|
|
2016-05-02 21:00:29 +03:00
|
|
|
} // namespace misc
|
2015-03-02 13:46:43 +03:00
|
|
|
} // namespace tidy
|
|
|
|
} // namespace clang
|