diff options
Diffstat (limited to 'clang-tools-extra/clang-tidy/misc')
6 files changed, 349 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/misc/AssertSideEffectCheck.cpp b/clang-tools-extra/clang-tidy/misc/AssertSideEffectCheck.cpp new file mode 100644 index 00000000000..9d21b499ee9 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/AssertSideEffectCheck.cpp @@ -0,0 +1,114 @@ +//===--- AssertSideEffectCheck.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 "AssertSideEffectCheck.h" +#include "clang/AST/ASTContext.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 <algorithm> +#include <string> + +using namespace clang::ast_matchers; + +namespace clang { +namespace ast_matchers { + +AST_MATCHER_P(Expr, hasSideEffect, bool, CheckFunctionCalls) { + const Expr *E = &Node; + + if (const auto *Op = dyn_cast<UnaryOperator>(E)) { + UnaryOperator::Opcode OC = Op->getOpcode(); + return OC == UO_PostInc || OC == UO_PostDec || OC == UO_PreInc || + OC == UO_PreDec; + } + + if (const auto *Op = dyn_cast<BinaryOperator>(E)) { + BinaryOperator::Opcode OC = Op->getOpcode(); + return OC == BO_Assign || OC == BO_MulAssign || OC == BO_DivAssign || + OC == BO_RemAssign || OC == BO_AddAssign || OC == BO_SubAssign || + OC == BO_ShlAssign || OC == BO_ShrAssign || OC == BO_AndAssign || + OC == BO_XorAssign || OC == BO_OrAssign; + } + + if (const auto *OpCallExpr = dyn_cast<CXXOperatorCallExpr>(E)) { + OverloadedOperatorKind OpKind = OpCallExpr->getOperator(); + return OpKind == OO_Equal || OpKind == OO_PlusEqual || + OpKind == OO_MinusEqual || OpKind == OO_StarEqual || + OpKind == OO_SlashEqual || OpKind == OO_AmpEqual || + OpKind == OO_PipeEqual || OpKind == OO_CaretEqual || + OpKind == OO_LessLessEqual || OpKind == OO_GreaterGreaterEqual || + OpKind == OO_PlusPlus || OpKind == OO_MinusMinus || + OpKind == OO_PercentEqual || OpKind == OO_New || + OpKind == OO_Delete || OpKind == OO_Array_New || + OpKind == OO_Array_Delete; + } + + if (const auto *CExpr = dyn_cast<CallExpr>(E)) { + bool Result = CheckFunctionCalls; + if (const auto *FuncDecl = CExpr->getDirectCallee()) + if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FuncDecl)) + Result &= !MethodDecl->isConst(); + return Result; + } + + return isa<CXXNewExpr>(E) || isa<CXXDeleteExpr>(E) || isa<CXXThrowExpr>(E); +} + +} // namespace ast_matchers + +namespace tidy { + +AssertSideEffectCheck::AssertSideEffectCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + CheckFunctionCalls(Options.get("CheckFunctionCalls", false)), + RawAssertList(Options.get("AssertMacros", "assert")) { + StringRef(RawAssertList).split(AssertMacros, ",", -1, false); +} + +// The options are explained in AssertSideEffectCheck.h. +void AssertSideEffectCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "CheckFunctionCalls", CheckFunctionCalls); + Options.store(Opts, "AssertMacros", RawAssertList); +} + +void AssertSideEffectCheck::registerMatchers(MatchFinder *Finder) { + auto ConditionWithSideEffect = + hasCondition(hasDescendant(expr(hasSideEffect(CheckFunctionCalls)))); + Finder->addMatcher( + stmt(anyOf(conditionalOperator(ConditionWithSideEffect), + ifStmt(ConditionWithSideEffect))).bind("condStmt"), + this); +} + +void AssertSideEffectCheck::check(const MatchFinder::MatchResult &Result) { + const ASTContext *ASTCtx = Result.Context; + const auto *CondStmt = Result.Nodes.getNodeAs<Stmt>("condStmt"); + SourceLocation Loc = CondStmt->getLocStart(); + + if (!Loc.isValid() || !Loc.isMacroID()) + return; + + StringRef MacroName = Lexer::getImmediateMacroName( + Loc, ASTCtx->getSourceManager(), ASTCtx->getLangOpts()); + + // Check if this macro is an assert. + if (std::find(AssertMacros.begin(), AssertMacros.end(), MacroName) == + AssertMacros.end()) + return; + + diag(Loc, "found " + MacroName.str() + "() with side effect"); +} + +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/misc/AssertSideEffectCheck.h b/clang-tools-extra/clang-tidy/misc/AssertSideEffectCheck.h new file mode 100644 index 00000000000..831435a463b --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/AssertSideEffectCheck.h @@ -0,0 +1,50 @@ +//===--- AssertSideEffectCheck.h - clang-tidy -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSERT_SIDE_EFFECT_CHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSERT_SIDE_EFFECT_CHECK_H + +#include "../ClangTidy.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include <string> + +namespace clang { +namespace tidy { + +/// \brief Finds \c assert() with side effect. +/// +/// The condition of \c assert() is evaluated only in debug builds so a +/// condition with side effect can cause different behaviour in debug / relesase +/// builds. +/// +/// There are two options: +/// - AssertMacros: AssertMacros: A comma-separated list of the names of assert +/// macros to be checked. +/// - CheckFunctionCalls: Whether to treat non-const member and non-member +/// functions as they produce side effects. Disabled by default +/// because it can increase the number of false positive warnings. + +class AssertSideEffectCheck : public ClangTidyCheck { +public: + AssertSideEffectCheck(StringRef Name, ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + const bool CheckFunctionCalls; + const std::string RawAssertList; + SmallVector<StringRef, 5> AssertMacros; +}; + +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSERT_SIDE_EFFECT_CHECK_H diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt index 1f2d9960a50..2664bbad703 100644 --- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt @@ -2,11 +2,13 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyMiscModule ArgumentCommentCheck.cpp + AssertSideEffectCheck.cpp AssignOperatorSignatureCheck.cpp BoolPointerImplicitConversion.cpp InaccurateEraseCheck.cpp InefficientAlgorithmCheck.cpp MiscTidyModule.cpp + StaticAssertCheck.cpp SwappedArgumentsCheck.cpp UndelegatedConstructor.cpp UnusedRAII.cpp diff --git a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp index a43efabcfd3..9c9ff70f43c 100644 --- a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp @@ -11,10 +11,12 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "ArgumentCommentCheck.h" +#include "AssertSideEffectCheck.h" #include "AssignOperatorSignatureCheck.h" #include "BoolPointerImplicitConversion.h" #include "InaccurateEraseCheck.h" #include "InefficientAlgorithmCheck.h" +#include "StaticAssertCheck.h" #include "SwappedArgumentsCheck.h" #include "UndelegatedConstructor.h" #include "UniqueptrResetRelease.h" @@ -28,6 +30,8 @@ class MiscModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck<ArgumentCommentCheck>("misc-argument-comment"); + CheckFactories.registerCheck<AssertSideEffectCheck>( + "misc-assert-side-effect"); CheckFactories.registerCheck<AssignOperatorSignatureCheck>( "misc-assign-operator-signature"); CheckFactories.registerCheck<BoolPointerImplicitConversion>( @@ -36,6 +40,8 @@ public: "misc-inaccurate-erase"); CheckFactories.registerCheck<InefficientAlgorithmCheck>( "misc-inefficient-algorithm"); + CheckFactories.registerCheck<StaticAssertCheck>( + "misc-static-assert"); CheckFactories.registerCheck<SwappedArgumentsCheck>( "misc-swapped-arguments"); CheckFactories.registerCheck<UndelegatedConstructorCheck>( 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 <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 diff --git a/clang-tools-extra/clang-tidy/misc/StaticAssertCheck.h b/clang-tools-extra/clang-tidy/misc/StaticAssertCheck.h new file mode 100644 index 00000000000..26a93943aa9 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/StaticAssertCheck.h @@ -0,0 +1,39 @@ +//===--- StaticAssertCheck.h - clang-tidy -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STATIC_ASSERT_CHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STATIC_ASSERT_CHECK_H + +#include "../ClangTidy.h" +#include "llvm/ADT/StringRef.h" +#include <string> + +namespace clang { +namespace tidy { + +/// \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. +class StaticAssertCheck : public ClangTidyCheck { +public: + StaticAssertCheck(StringRef Name, ClangTidyContext *Context); + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + SourceLocation getLastParenLoc(const ASTContext *ASTCtx, + SourceLocation AssertLoc); +}; + +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STATIC_ASSERT_CHECK_H |