From 1ca3b832558a9843241f940fbd1ed90ec10e5f70 Mon Sep 17 00:00:00 2001 From: Alexander Kornienko Date: Mon, 2 Mar 2015 10:46:43 +0000 Subject: [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 --- .../clang-tidy/misc/StaticAssertCheck.cpp | 138 +++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/misc/StaticAssertCheck.cpp (limited to 'clang-tools-extra/clang-tidy/misc/StaticAssertCheck.cpp') 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 00000000000..47d6df7bf66 --- /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 + +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("condStmt"); + const auto *Condition = Result.Nodes.getNodeAs("condition"); + const auto *IsAlwaysFalse = Result.Nodes.getNodeAs("isAlwaysFalse"); + const auto *AssertMSG = Result.Nodes.getNodeAs("assertMSG"); + const auto *AssertExprRoot = + Result.Nodes.getNodeAs("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 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 -- cgit v1.2.3