diff options
author | Alexander Kornienko <alexfh@google.com> | 2015-03-02 10:46:43 +0000 |
---|---|---|
committer | Alexander Kornienko <alexfh@google.com> | 2015-03-02 10:46:43 +0000 |
commit | 1ca3b832558a9843241f940fbd1ed90ec10e5f70 (patch) | |
tree | 618cbc0d0f5c19d79a0a93b133940d0839df938c /clang-tools-extra/clang-tidy/misc/StaticAssertCheck.cpp | |
parent | e662316994ff7f90263c98e5213230fa7823e8d2 (diff) | |
download | llvm-1ca3b832558a9843241f940fbd1ed90ec10e5f70.zip llvm-1ca3b832558a9843241f940fbd1ed90ec10e5f70.tar.gz llvm-1ca3b832558a9843241f940fbd1ed90ec10e5f70.tar.bz2 |
[clang-tidy] Assert related checkers
This patch contains two assert related checkers. These checkers are the part of
those that is being open sourced by Ericsson
(http://lists.cs.uiuc.edu/pipermail/cfe-dev/2014-December/040520.html).
The checkers:
AssertSideEffect:
/// \brief Finds \c assert() with side effect.
///
/// The conition of \c assert() is evaluated only in debug builds so a condition
/// with side effect can cause different behaviour in debug / relesase builds.
StaticAssert:
/// \brief Replaces \c assert() with \c static_assert() if the condition is
/// evaluatable at compile time.
///
/// The condition of \c static_assert() is evaluated at compile time which is
/// safer and more efficient.
http://reviews.llvm.org/D7375
Patch by Szabolcs Sipos!
llvm-svn: 230943
Diffstat (limited to 'clang-tools-extra/clang-tidy/misc/StaticAssertCheck.cpp')
-rw-r--r-- | clang-tools-extra/clang-tidy/misc/StaticAssertCheck.cpp | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/misc/StaticAssertCheck.cpp b/clang-tools-extra/clang-tidy/misc/StaticAssertCheck.cpp new file mode 100644 index 0000000..47d6df7 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/StaticAssertCheck.cpp @@ -0,0 +1,138 @@ +//===--- 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 { + +StaticAssertCheck::StaticAssertCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + +void StaticAssertCheck::registerMatchers(MatchFinder *Finder) { + auto IsAlwaysFalse = ignoringParenImpCasts( + anyOf(boolLiteral(equals(false)).bind("isAlwaysFalse"), + integerLiteral(equals(0)).bind("isAlwaysFalse"))); + auto AssertExprRoot = anyOf( + binaryOperator( + hasOperatorName("&&"), + hasEitherOperand(ignoringImpCasts(stringLiteral().bind("assertMSG"))), + anyOf(binaryOperator(hasEitherOperand(IsAlwaysFalse)), anything())) + .bind("assertExprRoot"), + IsAlwaysFalse); + auto Condition = expr(anyOf( + expr(ignoringParenCasts(anyOf( + AssertExprRoot, + unaryOperator(hasUnaryOperand(ignoringParenCasts(AssertExprRoot)))))), + anything())); + + Finder->addMatcher( + stmt(anyOf(conditionalOperator(hasCondition(Condition.bind("condition"))), + ifStmt(hasCondition(Condition.bind("condition")))), + unless(isInTemplateInstantiation())).bind("condStmt"), + this); +} + +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"); + SourceLocation AssertExpansionLoc = CondStmt->getLocStart(); + + if (!Opts.CPlusPlus11 || !AssertExpansionLoc.isValid() || + !AssertExpansionLoc.isMacroID()) + return; + + StringRef MacroName = + Lexer::getImmediateMacroName(AssertExpansionLoc, SM, Opts); + + if (MacroName != "assert" || !Condition->isEvaluatable(*ASTCtx)) + return; + + // False literal is not the result of macro expansion. + if (IsAlwaysFalse && + !SM.getImmediateSpellingLoc(IsAlwaysFalse->getExprLoc()).isMacroID()) + return; + + 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(); +} + +} // namespace tidy +} // namespace clang |