Index: clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -20,6 +20,7 @@ #include "DanglingHandleCheck.h" #include "DynamicStaticInitializersCheck.h" #include "EasilySwappableParametersCheck.h" +#include "EnumToIntCheck.h" #include "ExceptionEscapeCheck.h" #include "FoldInitTypeCheck.h" #include "ForwardDeclarationNamespaceCheck.h" @@ -100,6 +101,7 @@ "bugprone-dynamic-static-initializers"); CheckFactories.registerCheck( "bugprone-easily-swappable-parameters"); + CheckFactories.registerCheck("bugprone-enum-to-int"); CheckFactories.registerCheck( "bugprone-exception-escape"); CheckFactories.registerCheck("bugprone-fold-init-type"); Index: clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt =================================================================== --- clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -15,6 +15,7 @@ DanglingHandleCheck.cpp DynamicStaticInitializersCheck.cpp EasilySwappableParametersCheck.cpp + EnumToIntCheck.cpp ExceptionEscapeCheck.cpp FoldInitTypeCheck.cpp ForwardDeclarationNamespaceCheck.cpp Index: clang-tools-extra/clang-tidy/bugprone/EnumToIntCheck.h =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/bugprone/EnumToIntCheck.h @@ -0,0 +1,34 @@ +//===--- EnumToIntCheck.h - clang-tidy --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ENUMTOINTCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ENUMTOINTCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace bugprone { + +/// Diagnoses instances of an enum implicitly converted to an integer +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/enum-to-int.html +class EnumToIntCheck : public ClangTidyCheck { +public: + EnumToIntCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace bugprone +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ENUMTOINTCHECK_H Index: clang-tools-extra/clang-tidy/bugprone/EnumToIntCheck.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/bugprone/EnumToIntCheck.cpp @@ -0,0 +1,56 @@ +//===--- EnumToIntCheck.cpp - clang-tidy ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "EnumToIntCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Type.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace bugprone { + +void EnumToIntCheck::registerMatchers(MatchFinder *Finder) { + auto ImplicitEnumToInt = implicitCastExpr( + hasCastKind(CK_IntegralCast), + hasSourceExpression(expr(hasType(enumType()))), + anyOf(hasParent(callExpr()), hasParent(cxxConstructExpr()), + hasParent(returnStmt()))); + Finder->addMatcher(ImplicitEnumToInt.bind("x"), this); +} + +void EnumToIntCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *MatchedExpr = Result.Nodes.getNodeAs("x")) { + diag(MatchedExpr->getBeginLoc(), + "enum is implictly converted to an integral") + << MatchedExpr->getSourceRange(); + auto Note = diag(MatchedExpr->getBeginLoc(), "insert an explicit cast", + DiagnosticIDs::Note); + std::string TypeName = MatchedExpr->getType().getAsString(); + if (Result.Context->getLangOpts().CPlusPlus11) { + SourceManager &SM = *Result.SourceManager; + Note << FixItHint::CreateInsertion(MatchedExpr->getBeginLoc(), + "static_cast<" + TypeName + ">("); + Note << FixItHint::CreateInsertion( + Lexer::getLocForEndOfToken(MatchedExpr->getEndLoc(), 0, SM, + getLangOpts()), + ")"); + } else { + Note << FixItHint::CreateInsertion(MatchedExpr->getBeginLoc(), + "(" + TypeName + ")"); + } + } +} + +} // namespace bugprone +} // namespace tidy +} // namespace clang Index: clang-tools-extra/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/docs/ReleaseNotes.rst +++ clang-tools-extra/docs/ReleaseNotes.rst @@ -104,6 +104,11 @@ Warns when a struct or class uses const or reference (lvalue or rvalue) data members. +- New :doc:`bugprone-enum-to-int + ` check. + + Finds implicit conversion of enum to an integral type. + New check aliases ^^^^^^^^^^^^^^^^^ Index: clang-tools-extra/docs/clang-tidy/checks/bugprone/enum-to-int.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/bugprone/enum-to-int.rst @@ -0,0 +1,24 @@ +.. title:: clang-tidy - bugprone-enum-to-int + +bugprone-enum-to-int +==================== + +This check diagnoses instances where an enum is implicitly converted to an +integer. In C++11, enums can be defined as ``enum class`` which will prevent +such implicit conversion, however, ``enum`` provides no such guarantees to +prevent bugs. There can be many reasons why ``enum`` cannot be replaced with +``enum class`` such as compatibility with C or legacy libraries. + +This check will diagnose similiar implicit conversions whne using ``enum`` to +find the same class of bugs. Currently it will only warn on function or +constructor calls, or return statements as such conversions are not always +clear to the user, but this could be expanded in the future. + +Examples: + +.. code-block:: c++ + + void foo(int i); + void f() { + foo(e1); // e1 is implictly converted to an int + } Index: clang-tools-extra/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -86,6 +86,7 @@ `bugprone-dangling-handle `_, `bugprone-dynamic-static-initializers `_, `bugprone-easily-swappable-parameters `_, + `bugprone-enum-to-int `_, `bugprone-exception-escape `_, `bugprone-fold-init-type `_, `bugprone-forward-declaration-namespace `_, Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/enum-to-int.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/bugprone/enum-to-int.cpp @@ -0,0 +1,73 @@ +// RUN: %check_clang_tidy -std=c++11-or-later --fix-notes %s bugprone-enum-to-int %t + +enum A { + e1, + e2 +}; + +enum B : unsigned int { + e3, + e4 +}; + +struct bar { + bar(int); +}; +void foo(int i); +void foo_unsigned(unsigned int i); +void f1() { + foo(e1); + // CHECK-NOTES: :[[@LINE-1]]:7: warning: enum is implictly converted to an integral [bugprone-enum-to-int] + // CHECK-NOTES: :[[@LINE-2]]:7: note: insert an explicit cast + // CHECK-NOTES: static_cast( ) + // CHECK-FIXES: foo(static_cast(e1)); +} +void f2() { + foo(static_cast(e2)); +} +void f3() { + int i = e1; + foo(i); +} +void f4() { + bar a(e1); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: enum is implictly converted to an integral [bugprone-enum-to-int] + // CHECK-NOTES: :[[@LINE-2]]:9: note: insert an explicit cast + // CHECK-NOTES: static_cast( ) + // CHECK-FIXES: bar a(static_cast(e1)); +} +void f5() { + auto a = bar{e1}; + // CHECK-NOTES: :[[@LINE-1]]:16: warning: enum is implictly converted to an integral [bugprone-enum-to-int] + // CHECK-NOTES: :[[@LINE-2]]:16: note: insert an explicit cast + // CHECK-NOTES: static_cast( ) + // CHECK-FIXES: auto a = bar{static_cast(e1)}; +} +int f6() { + return e1; + // CHECK-NOTES: :[[@LINE-1]]:10: warning: enum is implictly converted to an integral [bugprone-enum-to-int] + // CHECK-NOTES: :[[@LINE-2]]:10: note: insert an explicit cast + // CHECK-NOTES: static_cast( ) + // CHECK-FIXES: return static_cast(e1); +} +void f7() { + foo_unsigned(e1); + // CHECK-NOTES: :[[@LINE-1]]:16: warning: enum is implictly converted to an integral [bugprone-enum-to-int] + // CHECK-NOTES: :[[@LINE-2]]:16: note: insert an explicit cast + // CHECK-NOTES: static_cast( ) + // CHECK-FIXES: foo_unsigned(static_cast(e1)); +} +void f8() { + foo(e4); + // CHECK-NOTES: :[[@LINE-1]]:7: warning: enum is implictly converted to an integral [bugprone-enum-to-int] + // CHECK-NOTES: :[[@LINE-2]]:7: note: insert an explicit cast + // CHECK-NOTES: static_cast( ) + // CHECK-FIXES: foo(static_cast(e4)); +} +void f9() { + foo_unsigned(e4); + // CHECK-NOTES: :[[@LINE-1]]:16: warning: enum is implictly converted to an integral [bugprone-enum-to-int] + // CHECK-NOTES: :[[@LINE-2]]:16: note: insert an explicit cast + // CHECK-NOTES: static_cast( ) + // CHECK-FIXES: foo_unsigned(static_cast(e4)); +}