Index: clang-tidy/cppcoreguidelines/CMakeLists.txt =================================================================== --- clang-tidy/cppcoreguidelines/CMakeLists.txt +++ clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -4,6 +4,7 @@ CppCoreGuidelinesTidyModule.cpp InterfacesGlobalInitCheck.cpp NoMallocCheck.cpp + OwningMemoryCheck.cpp ProBoundsArrayToPointerDecayCheck.cpp ProBoundsConstantArrayIndexCheck.cpp ProBoundsPointerArithmeticCheck.cpp Index: clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp =================================================================== --- clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -13,6 +13,7 @@ #include "../misc/UnconventionalAssignOperatorCheck.h" #include "InterfacesGlobalInitCheck.h" #include "NoMallocCheck.h" +#include "OwningMemoryCheck.h" #include "ProBoundsArrayToPointerDecayCheck.h" #include "ProBoundsConstantArrayIndexCheck.h" #include "ProBoundsPointerArithmeticCheck.h" @@ -37,6 +38,8 @@ CheckFactories.registerCheck( "cppcoreguidelines-interfaces-global-init"); CheckFactories.registerCheck("cppcoreguidelines-no-malloc"); + CheckFactories.registerCheck( + "cppcoreguidelines-owning-memory"); CheckFactories.registerCheck( "cppcoreguidelines-pro-bounds-array-to-pointer-decay"); CheckFactories.registerCheck( Index: clang-tidy/cppcoreguidelines/OwningMemoryCheck.h =================================================================== --- /dev/null +++ clang-tidy/cppcoreguidelines/OwningMemoryCheck.h @@ -0,0 +1,44 @@ +//===--- OwningMemoryCheck.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_CPPCOREGUIDELINES_OWNING_MEMORY_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_OWNING_MEMORY_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +/// Checks for common use cases for gsl::owner and enforces the unique owner +/// nature of it whenever possible. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-owning-memory.html +class OwningMemoryCheck : public ClangTidyCheck { +public: + OwningMemoryCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + bool handleDeletion(const ast_matchers::BoundNodes &Nodes); + bool handleExpectedOwner(const ast_matchers::BoundNodes &Nodes); + bool handleAssignmentAndInit(const ast_matchers::BoundNodes &Nodes); + bool handleAssignmentFromNewOwner(const ast_matchers::BoundNodes &Nodes); + bool handleReturnValues(const ast_matchers::BoundNodes &Nodes); + bool handleOwnerMembers(const ast_matchers::BoundNodes &Nodes); +}; + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_OWNING_MEMORY_H Index: clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp @@ -0,0 +1,313 @@ +//===--- OwningMemoryCheck.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 "OwningMemoryCheck.h" +#include "../utils/Matchers.h" +#include "../utils/OptionsUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include +#include + +using namespace clang::ast_matchers; +using namespace clang::ast_matchers::internal; + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +/// Match common cases, where the owner semantic is relevant, like function +/// calls, delete expressions and others. +void OwningMemoryCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus11) + return; + + const auto OwnerDecl = typeAliasTemplateDecl(hasName("::gsl::owner")); + const auto IsOwnerType = hasType(OwnerDecl); + const auto CreatesOwner = + anyOf(cxxNewExpr(), callExpr(callee(functionDecl( + returns(qualType(hasDeclaration(OwnerDecl))))))); + const auto ConsideredOwner = anyOf(IsOwnerType, CreatesOwner); + + // Find delete expressions that delete non-owners. + Finder->addMatcher( + cxxDeleteExpr( + hasDescendant( + declRefExpr(unless(ConsideredOwner)).bind("deleted_variable"))) + .bind("delete_expr"), + this); + + // Matching assignment to owners, with the rhs not being an owner nor creating + // one. + Finder->addMatcher(binaryOperator(allOf(matchers::isAssignmentOperator(), + hasLHS(IsOwnerType), + hasRHS(unless(ConsideredOwner)))) + .bind("owner_assignment"), + this); + + // Matching initialization of owners with non-owners, nor creating owners. + Finder->addMatcher( + namedDecl( + varDecl(allOf(hasInitializer(unless(ConsideredOwner)), IsOwnerType)) + .bind("owner_initialization")), + this); + + // Match class member initialization that expects owners, but does not get + // them. + Finder->addMatcher( + cxxRecordDecl(has(cxxConstructorDecl(forEachConstructorInitializer( + cxxCtorInitializer( + allOf( + isMemberInitializer(), forField(IsOwnerType), + withInitializer( + // Avoid templatesdeclaration with avoiding parenListExpr. + allOf(unless(ConsideredOwner), unless(parenListExpr()))))) + .bind("owner_member_initializer"))))), + this); + + // Matching on assignment operations where the RHS is a newly created owner, + // but the LHS is not an owner. + Finder->addMatcher( + binaryOperator(allOf(matchers::isAssignmentOperator(), + hasLHS(unless(IsOwnerType)), hasRHS(CreatesOwner))) + .bind("bad_owner_creation_assignment"), + this); + + // Matching on initialization operations where the initial value is a newly + // created owner, but the LHS is not an owner. + Finder->addMatcher( + namedDecl(varDecl(eachOf(allOf(hasInitializer(CreatesOwner), + unless(IsOwnerType)), + allOf(hasInitializer(ConsideredOwner), + hasType(autoType().bind("deduced_type"))))) + .bind("bad_owner_creation_variable")), + this); + + // Match on all function calls that expect owners as arguments, but didn't + // get them. + Finder->addMatcher( + callExpr(forEachArgumentWithParam( + expr(unless(ConsideredOwner)).bind("expected_owner_argument"), + parmVarDecl(IsOwnerType))), + this); + + // Matching for function calls where one argument is a created owner, but the + // parameter type is not an owner. + Finder->addMatcher(callExpr(forEachArgumentWithParam( + expr(CreatesOwner).bind("bad_owner_creation_argument"), + parmVarDecl(unless(IsOwnerType)) + .bind("bad_owner_creation_parameter"))), + this); + + // Matching on functions, that return an owner/resource, but don't declare + // their return type as owner. + Finder->addMatcher( + functionDecl( + allOf(hasDescendant(returnStmt(hasReturnValue(ConsideredOwner)) + .bind("bad_owner_return")), + unless(returns(qualType(hasDeclaration(OwnerDecl)))))) + .bind("function_decl"), + this); + + // Match on classes that have an owner as member, but don't declare a + // destructor to properly release the owner. + Finder->addMatcher( + cxxRecordDecl( + allOf(has(fieldDecl(IsOwnerType).bind("undestructed_owner_member")), + anyOf(unless(has(cxxDestructorDecl())), + has(cxxDestructorDecl(isDefaulted()))))) + .bind("non_destructor_class"), + this); +} + +void OwningMemoryCheck::check(const MatchFinder::MatchResult &Result) { + const auto &Nodes = Result.Nodes; + + bool CheckExecuted = false; + CheckExecuted |= handleDeletion(Nodes); + CheckExecuted |= handleExpectedOwner(Nodes); + CheckExecuted |= handleAssignmentAndInit(Nodes); + CheckExecuted |= handleAssignmentFromNewOwner(Nodes); + CheckExecuted |= handleReturnValues(Nodes); + CheckExecuted |= handleOwnerMembers(Nodes); + + assert(CheckExecuted == true && + "Non of the subroutines did execute, logic error in matcher!"); +} + +bool OwningMemoryCheck::handleDeletion(const BoundNodes &Nodes) { + // Result of delete matchers. + const auto *DeleteStmt = Nodes.getNodeAs("delete_expr"); + const auto *DeletedVariable = + Nodes.getNodeAs("deleted_variable"); + + // Deletion of non-owners, with `delete variable;` + if (DeleteStmt) { + diag(DeleteStmt->getLocStart(), + "deleting a pointer through a type that is " + "not marked 'gsl::owner<>'; consider using a " + "smart pointer instead") + << DeletedVariable->getSourceRange(); + return true; + } + return false; +} + +bool OwningMemoryCheck::handleExpectedOwner(const BoundNodes &Nodes) { + // Result of function call matchers. + const auto *ExpectedOwner = Nodes.getNodeAs("expected_owner_argument"); + + // Expected function argument to be owner. + if (ExpectedOwner) { + diag(ExpectedOwner->getLocStart(), + "expected argument of type 'gsl::owner<>'; got %0") + << ExpectedOwner->getType() << ExpectedOwner->getSourceRange(); + return true; + } + return false; +} + +/// Assignment and initialization of owner variables. +bool OwningMemoryCheck::handleAssignmentAndInit(const BoundNodes &Nodes) { + const auto *OwnerAssignment = + Nodes.getNodeAs("owner_assignment"); + const auto *OwnerInitialization = + Nodes.getNodeAs("owner_initialization"); + const auto *OwnerInitializer = + Nodes.getNodeAs("owner_member_initializer"); + + // Assignments to owners. + if (OwnerAssignment) { + diag(OwnerAssignment->getLocStart(), + "expected assignment source to be of type 'gsl::owner<>'; got %0") + << OwnerAssignment->getRHS()->getType() + << OwnerAssignment->getSourceRange(); + return true; + } + + // Initialization of owners. + if (OwnerInitialization) { + diag(OwnerInitialization->getLocStart(), + "expected initialization with value of type 'gsl::owner<>'; got %0") + << OwnerInitialization->getAnyInitializer()->getType() + << OwnerInitialization->getSourceRange(); + return true; + } + + // Initializer of class constructors that initialize owners. + if (OwnerInitializer) { + diag(OwnerInitializer->getSourceLocation(), + "expected initializing of owner member variable with value of type " + "'gsl::owner<>'; got %0") + // FIXME: the expression from getInit has type 'void', but the type + // of the supplied argument would be of interest. + << OwnerInitializer->getInit()->getType() + << OwnerInitializer->getSourceRange(); + return true; + } + return false; +} + +/// Problematic assignment and initializations, since the assigned value is a +/// newly created owner. +bool OwningMemoryCheck::handleAssignmentFromNewOwner(const BoundNodes &Nodes) { + const auto *BadOwnerAssignment = + Nodes.getNodeAs("bad_owner_creation_assignment"); + const auto *BadOwnerInitialization = + Nodes.getNodeAs("bad_owner_creation_variable"); + + const auto *BadOwnerArgument = + Nodes.getNodeAs("bad_owner_creation_argument"); + const auto *BadOwnerParameter = + Nodes.getNodeAs("bad_owner_creation_parameter"); + + // Bad assignments to non-owners, where the RHS is a newly created owner. + if (BadOwnerAssignment) { + diag(BadOwnerAssignment->getLocStart(), + "assigning newly created 'gsl::owner<>' to non-owner %0") + << BadOwnerAssignment->getLHS()->getType() + << BadOwnerAssignment->getSourceRange(); + return true; + } + + // Bad initialization of non-owners, where the RHS is a newly create owner. + if (BadOwnerInitialization) { + diag(BadOwnerInitialization->getLocStart(), + "initializing non-owner %0 with a newly created 'gsl::owner<>'") + << BadOwnerInitialization->getType() + << BadOwnerInitialization->getSourceRange(); + // FIXME: FixitHint to rewrite the type if possible. + + // If the type of the variable was deduced, the wrapping owner typedef is + // eliminated, therefore the check emits a special note for that case. + if (Nodes.getNodeAs("deduced_type")) { + diag(BadOwnerInitialization->getLocStart(), + "type deduction did not result in an owner", DiagnosticIDs::Note); + } + return true; + } + + // Function call, where one arguments is a newly created owner + if (BadOwnerArgument) { + assert(BadOwnerParameter && + "parameter for the problematic argument not found"); + diag(BadOwnerArgument->getLocStart(), "initializing non-owner argument of " + "type %0 with a newly created " + "'gsl::owner<>'") + << BadOwnerParameter->getType() << BadOwnerArgument->getSourceRange(); + return true; + } + return false; +} + +bool OwningMemoryCheck::handleReturnValues(const BoundNodes &Nodes) { + // Function return statements, that are owners/resources, but the function + // declaration does not declare its return value as owner. + const auto *BadReturnType = Nodes.getNodeAs("bad_owner_return"); + const auto *Function = Nodes.getNodeAs("function_decl"); + + // Function return values, that should be owners but aren't. + if (BadReturnType) { + diag(BadReturnType->getLocStart(), "returning a 'gsl::owner<>' from a " + "function but not declaring it; return " + "type is %0") + << Function->getReturnType() << BadReturnType->getSourceRange(); + return true; + } + return false; +} + +bool OwningMemoryCheck::handleOwnerMembers(const BoundNodes &Nodes) { + // Classes, that have owners as member, but do not declare destructors + // accordingly. + const auto *BadClass = Nodes.getNodeAs("non_destructor_class"); + + // Classes, that contains owners, but do not declare destructors + if (BadClass) { + const auto *DeclaredOwnerMember = + Nodes.getNodeAs("undestructed_owner_member"); + assert(DeclaredOwnerMember && + "match on class with bad destructor but without a declared owner"); + + diag(DeclaredOwnerMember->getLocStart(), + "member variable of type 'gsl::owner<>' requires the class %0 to " + "implement a destructor to release the owned resource") + << BadClass; + + // diag(DeclaredOwnerMember->getLocStart(), "declared owner as member here", + // DiagnosticIDs::Note) + //<< DeclaredOwnerMember->getSourceRange(); + return true; + } + return false; +} + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang Index: clang-tidy/hicpp/CMakeLists.txt =================================================================== --- clang-tidy/hicpp/CMakeLists.txt +++ clang-tidy/hicpp/CMakeLists.txt @@ -4,6 +4,7 @@ ExceptionBaseclassCheck.cpp NoAssemblerCheck.cpp HICPPTidyModule.cpp + SignedBitwiseCheck.cpp LINK_LIBS clangAST Index: clang-tidy/hicpp/HICPPTidyModule.cpp =================================================================== --- clang-tidy/hicpp/HICPPTidyModule.cpp +++ clang-tidy/hicpp/HICPPTidyModule.cpp @@ -26,6 +26,7 @@ #include "../readability/IdentifierNamingCheck.h" #include "ExceptionBaseclassCheck.h" #include "NoAssemblerCheck.h" +#include "SignedBitwiseCheck.h" namespace clang { namespace tidy { @@ -38,6 +39,8 @@ "hicpp-braces-around-statements"); CheckFactories.registerCheck( "hicpp-exception-baseclass"); + CheckFactories.registerCheck( + "hicpp-signed-bitwise"); CheckFactories.registerCheck( "hicpp-explicit-conversions"); CheckFactories.registerCheck( Index: clang-tidy/hicpp/SignedBitwiseCheck.h =================================================================== --- /dev/null +++ clang-tidy/hicpp/SignedBitwiseCheck.h @@ -0,0 +1,36 @@ +//===--- SignedBitwiseCheck.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_HICPP_SIGNED_BITWISE_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_SIGNED_BITWISE_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace hicpp { + +/// This check implements the rule 5.6.1 of the HICPP Standard, which disallows +/// bitwise operations on signed integer types. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/hicpp-signed-bitwise.html +class SignedBitwiseCheck : public ClangTidyCheck { +public: + SignedBitwiseCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace hicpp +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_SIGNED_BITWISE_H Index: clang-tidy/hicpp/SignedBitwiseCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/hicpp/SignedBitwiseCheck.cpp @@ -0,0 +1,56 @@ +//===--- SignedBitwiseCheck.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 "SignedBitwiseCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; +using namespace clang::ast_matchers::internal; + +namespace clang { +namespace tidy { +namespace hicpp { + +void SignedBitwiseCheck::registerMatchers(MatchFinder *Finder) { + const auto SignedIntegerOperand = + expr(ignoringImpCasts(hasType(isSignedInteger()))).bind("signed_operand"); + + // Match binary bitwise operations on signed integer arguments. + Finder->addMatcher( + binaryOperator(allOf(anyOf(hasOperatorName("|"), hasOperatorName("&"), + hasOperatorName("^"), hasOperatorName("<<"), + hasOperatorName(">>")), + hasEitherOperand(SignedIntegerOperand))) + .bind("binary_signed"), + this); + + // Match unary operations on signed integer types. + Finder->addMatcher(unaryOperator(allOf(hasOperatorName("~"), + hasUnaryOperand(SignedIntegerOperand))) + .bind("unary_signed"), + this); +} + +void SignedBitwiseCheck::check(const MatchFinder::MatchResult &Result) { + const ast_matchers::BoundNodes &N = Result.Nodes; + const auto *SignedBinary = N.getNodeAs("binary_signed"); + const auto *SignedUnary = N.getNodeAs("unary_signed"); + const auto *SignedOperand = N.getNodeAs("signed_operand"); + + const bool IsUnary = SignedUnary != nullptr; + diag(IsUnary ? SignedUnary->getLocStart() : SignedBinary->getLocStart(), + "use of a signed integer operand with a %select{binary|unary}0 bitwise " + "operator") + << IsUnary << SignedOperand->getSourceRange(); +} + +} // namespace hicpp +} // namespace tidy +} // namespace clang Index: clang-tidy/utils/Matchers.h =================================================================== --- clang-tidy/utils/Matchers.h +++ clang-tidy/utils/Matchers.h @@ -17,6 +17,10 @@ namespace tidy { namespace matchers { +AST_MATCHER(BinaryOperator, isAssignmentOperator) { + return Node.isAssignmentOp(); +} + AST_MATCHER(BinaryOperator, isRelationalOperator) { return Node.isRelationalOp(); } Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -114,12 +114,23 @@ Finds cases where integer division in a floating point context is likely to cause unintended loss of precision. +- New `cppcoreguidelines-owning-memory `_ check + + This check implements the type-based semantic of ``gsl::owner``, but without + flow analysis. + - New `hicpp-exception-baseclass `_ check Ensures that all exception will be instances of ``std::exception`` and classes that are derived from it. +- New `hicpp-signed-bitwise + `_ check + + Finds uses of bitwise operations on signed integer types, which may lead to + undefined or implementation defined behaviour. + - New `android-cloexec-inotify-init1 `_ check Index: docs/clang-tidy/checks/cppcoreguidelines-owning-memory.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/cppcoreguidelines-owning-memory.rst @@ -0,0 +1,136 @@ +.. title:: clang-tidy - cppcoreguidelines-owning-memory + +cppcoreguidelines-owning-memory +=============================== + +This check implements the type-based semantics of ``gsl::owner``, which allows +static analysis on code, that uses raw pointers to handle resources like +dynamic memory, but won't introduce RAII concepts. + +The relevant sections in the `C++ Core Guidelines `_ are I.11, C.33, R.3 and GSL.Views +The definition of an ``gsl::owner`` is straight forward + +.. code-block:: c++ + + namespace gsl { template owner = T; } + +it is therefore simple to introduce the owner even without using an implementation of +the `Guideline Support Library `_. + +All checks are purely type based and not (yet) flow sensitive. + +The following examples will demonstrate the correct and incorrect initializations +of owners, assignment is handled the same way. + +.. code-block:: c++ + + // Creating an owner with factory functions is caught. + gsl::owner function_that_returns_owner() { return gsl::owner(new int(42)); } + + // Dynamic memory must be assigned to an owner + int* Something = new int(42); // BAD, will be caught + gsl::owner Owner = new int(42); // Good + gsl::owner Owner = new int[42]; // Good as well + + // Returned owner must be assigned to an owner + int* Something = function_that_returns_owner(); // Bad, factory function + gsl::owner Owner = function_that_returns_owner(); // Good, result lands in owner + + // Something not a resource or owner should not be assigned to owners + int Stack = 42; + gsl::owner Owned = &Stack; // Bad, not an resource assigned + +In the case of dynamic memory as resource, only ``gsl::owner`` variables are allowed +to be deleted. + +.. code-block:: c++ + + // Example Bad, non-owner as resource handle, will be caught. + int* NonOwner = new int(42); // First warning here, since new must land in an owner + delete NonOwner; // Second warning here, since only owners are allowed to be deleted + + // Example Good, Ownership correclty stated + gsl::owner Owner = new int(42); // Good + delete Owner; // Good as well, statically enforced, that only owners get deleted + +The check will furthermore ensure, that functions, that expect an ``gsl::owner`` as +argument get called with either an ``gsl::owner`` or a newly created resource. + +.. code-block:: c++ + + void expects_owner(gsl::owner o) { delete o; } + + // Bad Code + int NonOwner = 42; + expects_owner(&NonOwner); // Bad, will get caught + + // Good Code + gsl::owner Owner = new int(42); + expects_owner(Owner); // Good + expects_owner(new int(42)); // Good as well, recognized created resource + +Limitations +----------- + +The ``gsl::owner`` is declared as a templated type alias. +In template functions and classes, like in the example below, the information +of the type aliases gets lost. Therefore using ``gsl::owner`` in a heavy templated +code base might lead to false positives. + +.. code-block:: c++ + + // This template function works as expected. Type information doesn't get lost. + template + void delete_owner(gsl::owner owned_object) { + delete owned_object; // Everything alright + } + + gsl::owner function_that_returns_owner() { return gsl::owner(new int(42)); } + + // Type deduction does not work for auto variables. + // This is caught by the check and will be noted accordingly. + auto OwnedObject = function_that_returns_owner(); // Type of OwnedObject will be int* + + // Problematic function template that looses the typeinformation on owner + template + void bad_template_function(T some_object) { + // This line will trigger the warning, that an non-owner is assigned to an owner + gsl::owner new_owner = some_object; + } + + // Calling the function with an owner still yields a false positive. + bad_template_function(gsl::owner(new int(42))); + + + // The same issue occurs with templated classes like the following. + template + class OwnedValue { + public: + const T getValue() const { return _val; } + private: + T _val; + }; + + // Code, that yields a false positive. + OwnedValue> Owner(new int(42)); // Type deduction yield T -> int * + // False positive, getValue returns int* and not gsl::owner + gsl::owner OwnedInt = Owner.getValue(); + +Another limitation of the current implementation is only the type based checking. +Suppose you have code like the following: + +.. code-block:: c++ + + // Two owners with assigned resources + gsl::owner Owner1 = new int(42); + gsl::owner Owner2 = new int(42); + + Owner2 = Owner1; // Conceptual Leak of initial resource of Owner2! + Owner1 = nullptr; + +The semantic of an ``gsl::owner`` is mostly like a ``std::unique_ptr``, therefore +assignment of two ``gsl::owner`` is considered a move, but therefore the resource +``Owner2`` must have been released before the assignment. +This kind of condition could be catched in later improvements of this check with +flowsensitive analysis. Currently, the `Clang Static Analyzer` catches this bug +for dynamic memory, but not for general types of resources. Index: docs/clang-tidy/checks/hicpp-signed-bitwise.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/hicpp-signed-bitwise.rst @@ -0,0 +1,9 @@ +.. title:: clang-tidy - hicpp-signed-bitwise + +hicpp-signed-bitwise +==================== + +Finds uses of bitwise operations on signed integer types, which may lead to +undefined or implementation defined behaviour. + +The according rule is defined in the `High Integrity C++ Standard, Section 5.6.1 `_. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -41,6 +41,7 @@ cppcoreguidelines-c-copy-assignment-signature cppcoreguidelines-interfaces-global-init cppcoreguidelines-no-malloc + cppcoreguidelines-owning-memory cppcoreguidelines-pro-bounds-array-to-pointer-decay cppcoreguidelines-pro-bounds-constant-array-index cppcoreguidelines-pro-bounds-pointer-arithmetic @@ -79,6 +80,7 @@ hicpp-new-delete-operators (redirects to misc-new-delete-overloads) hicpp-no-assembler hicpp-noexcept-move (redirects to misc-noexcept-moveconstructor) + hicpp-signed-bitwise hicpp-special-member-functions (redirects to cppcoreguidelines-special-member-functions) hicpp-undelegated-constructor (redirects to misc-undelegated-constructor) hicpp-use-equals-default (redirects to modernize-use-equals-default) Index: test/clang-tidy/cppcoreguidelines-owning-memory.cpp =================================================================== --- /dev/null +++ test/clang-tidy/cppcoreguidelines-owning-memory.cpp @@ -0,0 +1,347 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-owning-memory %t + +namespace gsl { +template +using owner = T; +} // namespace gsl + +template +class unique_ptr { +public: + unique_ptr(gsl::owner resource) : memory(resource) {} + unique_ptr(const unique_ptr &) = default; + + ~unique_ptr() { delete memory; } + +private: + gsl::owner memory; +}; + +void takes_owner(gsl::owner owned_int) { +} + +void takes_pointer(int *unowned_int) { +} + +void takes_owner_and_more(int some_int, gsl::owner owned_int, float f) { +} + +template +void takes_templated_owner(gsl::owner owned_T) { +} + +gsl::owner returns_owner1() { return gsl::owner(new int(42)); } // Ok +gsl::owner returns_owner2() { return new int(42); } // Ok + +int *returns_no_owner1() { return nullptr; } +int *returns_no_owner2() { + return new int(42); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: returning a 'gsl::owner<>' from a function but not declaring it; return type is 'int *' +} +int *returns_no_owner3() { + int *should_be_owner = new int(42); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' + return should_be_owner; +} +int *returns_no_owner4() { + gsl::owner owner = new int(42); + return owner; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: returning a 'gsl::owner<>' from a function but not declaring it; return type is 'int *' +} + +unique_ptr returns_no_owner5() { + return unique_ptr(new int(42)); // Ok +} + +/// FIXME: CSA finds it, but the report is misleading. +void csa_not_finding_leak() { + gsl::owner o1 = new int(42); // Ok + + gsl::owner o2 = o1; // Ok + o2 = new int(45); // conceptual leak, the memory from o1 is now leaked, since its considered moved in the guidelines + + delete o2; + // actual leak occurs here, its found, but mixed + delete o1; +} + +void test_assignment_and_initialization() { + int stack_int1 = 15; + int stack_int2; + + gsl::owner owned_int1 = &stack_int1; // BAD + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' + + gsl::owner owned_int2; + owned_int2 = &stack_int2; // BAD since no owner, bad since uninitialized + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'int *' + + gsl::owner owned_int3 = new int(42); // Good + owned_int3 = nullptr; // Good + + gsl::owner owned_int4(nullptr); // Ok + owned_int4 = new int(42); // Good + + gsl::owner owned_int5 = owned_int3; // Good + + gsl::owner owned_int6{nullptr}; // Ok + owned_int6 = owned_int4; // Good + + // FIXME:, flow analysis for the case of reassignment. Value must be released before + owned_int6 = owned_int3; // BAD, because reassignment without resource release + + auto owned_int7 = returns_owner1(); // Bad, since typededuction eliminates the owner wrapper + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' + // CHECK-MESSAGES: [[@LINE-2]]:3: note: type deduction did not result in an owner + + const auto owned_int8 = returns_owner2(); // Bad, since typededuction eliminates the owner wrapper + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *const' with a newly created 'gsl::owner<>' + // CHECK-MESSAGES: [[@LINE-2]]:3: note: type deduction did not result in an owner + + gsl::owner owned_int9 = returns_owner1(); // Ok + int *unowned_int3 = returns_owner1(); // Bad + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' + + gsl::owner owned_int10; + owned_int10 = returns_owner1(); // Ok + + int *unowned_int4; + unowned_int4 = returns_owner1(); // Bad + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *' + + gsl::owner owned_int11 = returns_no_owner1(); // Bad since no owner + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' + + gsl::owner owned_int12; + owned_int12 = returns_no_owner1(); // Bad since no owner + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'int *' + + int *unowned_int5 = returns_no_owner1(); // Ok + int *unowned_int6; + unowned_int6 = returns_no_owner1(); // Ok + + int *unowned_int7 = new int(42); // Bad, since resource not assigned to an owner + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' + + int *unowned_int8; + unowned_int8 = new int(42); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *' + + gsl::owner owned_int13 = nullptr; // Ok +} + +void test_deletion() { + gsl::owner owned_int1 = new int(42); + delete owned_int1; // Good + + gsl::owner owned_int2 = new int[42]; + delete[] owned_int2; // Good + + int *unowned_int1 = new int(42); // BAD, since new creates and owner + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' + delete unowned_int1; // BAD, since no owner + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead + + int *unowned_int2 = new int[42]; // BAD, since new creates and owner + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' + delete[] unowned_int2; // BAD since no owner + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead + + delete new int(42); // Technically ok, but stupid + delete[] new int[42]; // Technically ok, but stupid +} + +void test_owner_function_calls() { + int stack_int = 42; + int *unowned_int1 = &stack_int; + takes_owner(&stack_int); // BAD + // CHECK-MESSAGES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *' + takes_owner(unowned_int1); // BAD + // CHECK-MESSAGES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *' + + gsl::owner owned_int1 = new int(42); + takes_owner(owned_int1); // Ok + + takes_owner_and_more(42, &stack_int, 42.0f); // BAD + // CHECK-MESSAGES: [[@LINE-1]]:28: warning: expected argument of type 'gsl::owner<>'; got 'int *' + takes_owner_and_more(42, unowned_int1, 42.0f); // BAD + // CHECK-MESSAGES: [[@LINE-1]]:28: warning: expected argument of type 'gsl::owner<>'; got 'int *' + + takes_owner_and_more(42, new int(42), 42.0f); // Ok, since new is consumed by owner + takes_owner_and_more(42, owned_int1, 42.0f); // Ok, since owner as argument + + takes_templated_owner(owned_int1); // Ok + takes_templated_owner(new int(42)); // Ok + takes_templated_owner(unowned_int1); // Bad + // CHECK-MESSAGES: [[@LINE-1]]:25: warning: expected argument of type 'gsl::owner<>'; got 'int *' + + takes_owner(returns_owner1()); // Ok + takes_owner(returns_no_owner1()); // BAD + // CHECK-MESSAGES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *' +} + +void test_unowned_function_calls() { + int stack_int = 42; + int *unowned_int1 = &stack_int; + gsl::owner owned_int1 = new int(42); + + takes_pointer(&stack_int); // Ok + takes_pointer(unowned_int1); // Ok + takes_pointer(owned_int1); // Ok + takes_pointer(new int(42)); // Bad, since new creates and owner + // CHECK-MESSAGES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>' + + takes_pointer(returns_owner1()); // Bad + // CHECK-MESSAGES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>' + + takes_pointer(returns_no_owner1()); // Ok +} + +struct ArbitrayClass {}; +struct ClassWithOwner { // Does not define destructor, necessary with owner + ClassWithOwner() : owner_var(nullptr) {} // Ok + + ClassWithOwner(ArbitrayClass &other) : owner_var(&other) {} + // CHECK-MESSAGES: [[@LINE-1]]:42: warning: expected initializing of owner member variable with value of type 'gsl::owner<>'; got 'ArbitrayClass *' + + ClassWithOwner(gsl::owner other) : owner_var(other) {} // Ok + + ClassWithOwner(gsl::owner data, int /* unused */) { // Ok + owner_var = data; // Ok + } + + ClassWithOwner(ArbitrayClass *bad_data, int /* unused */, int /* unused */) { + owner_var = bad_data; + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitrayClass *' + } + + ClassWithOwner(ClassWithOwner &&other) : owner_var{other.owner_var} {} // Ok + + ClassWithOwner &operator=(ClassWithOwner &&other) { + owner_var = other.owner_var; // Ok + return *this; + } + + // Returning means, that the owner is "moved", so the class should not access this + // variable anymore after this method gets called. + gsl::owner buggy_but_returns_owner() { return owner_var; } + + gsl::owner owner_var; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: member variable of type 'gsl::owner<>' requires the class 'ClassWithOwner' to implement a destructor to release the owned resource +}; + +void test_class_with_owner() { + ArbitrayClass A; + ClassWithOwner C1; // Ok + ClassWithOwner C2{A}; // Bad, since the owner would be initialized with an nonowner, but catched in the class + ClassWithOwner C3{gsl::owner(new ArbitrayClass)}; // Ok + + const auto Owner1 = C3.buggy_but_returns_owner(); // BAD, deduces Owner1 to ArbitrayClass *const + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'ArbitrayClass *const' with a newly created 'gsl::owner<>' + // CHECK-MESSAGES: [[@LINE-2]]:3: note: type deduction did not result in an owner + + auto Owner2 = C2.buggy_but_returns_owner(); // BAD, deduces Owner2 to ArbitrayClass * + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'ArbitrayClass *' with a newly created 'gsl::owner<>' + // CHECK-MESSAGES: [[@LINE-2]]:3: note: type deduction did not result in an owner + + Owner2 = &A; // Ok, since type deduction did NOT result in owner + + gsl::owner Owner3 = C1.buggy_but_returns_owner(); // Ok, still an owner + Owner3 = &A; // Bad, since assignment of nonowner to owner + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitrayClass *' +} + +template +struct HeapArray { // Ok, since destructor with owner + HeapArray() : _data(nullptr), size(0) {} // Ok + HeapArray(int size) : _data(new int[size]), size(size) {} // Ok + HeapArray(int size, T val) { + _data = new int[size]; // Ok + size = size; + for (auto i = 0u; i < size; ++i) + _data[i] = val; // Ok + } + HeapArray(int size, T val, int *problematic) : _data{problematic}, size(size) {} // Bad + // CHECK-MESSAGES: [[@LINE-1]]:50: warning: expected initializing of owner member variable with value of type 'gsl::owner<>'; got 'void' + // FIXME: void is incorrect type, probably wrong thing matched + + HeapArray(HeapArray &&other) : _data(other._data), size(other.size) { // Ok + other._data = nullptr; // Ok + other.size = 0; + } + + HeapArray &operator=(HeapArray &&other) { + _data = other._data; // Ok, NOLINT warning here about bad types, why? + size = other.size; + return *this; + } + + ~HeapArray() { delete[] _data; } // Ok + + T *data() { return _data; } // Ok NOLINT, because it "looks" like a factory + + gsl::owner _data; + unsigned int size; +}; + +void test_inner_template() { + HeapArray Array1; + HeapArray Array2(100); + HeapArray Array3(100, 0); + + Array1 = static_cast &&>(Array2); + HeapArray Array4(static_cast &&>(Array3)); + + int *NonOwningPtr = Array1.data(); // Ok + gsl::owner OwningPtr = Array1.data(); // Bad, since it does not return the owner + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' +} + +class DefaultedDestructor { // Bad since default constructor with owner + ~DefaultedDestructor() = default; // Bad, since will not destroy the owner + gsl::owner Owner; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: member variable of type 'gsl::owner<>' requires the class 'DefaultedDestructor' to implement a destructor to release the owned resource +}; + +// FIXME: Typededuction removes the owner - wrapper, therefore gsl::owner can not be used +// with Template classes like this. Is there a walkaround? +template +struct TemplateValue { + TemplateValue() = default; + TemplateValue(T t) : val{t} {} + + void setVal(const T &t) { val = t; } + const T getVal() const { return val; } + + T val; +}; + +// FIXME: Same typededcution problems +template +void template_function(T t) { + gsl::owner owner_t = t; // Probably bad, since typededuction still wrong + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'T' + // CHECK-MESSAGES: [[@LINE-2]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' +} + +// FIXME: Same typededcution problems +void test_templates() { + int stack_int = 42; + int *stack_ptr1 = &stack_int; + + TemplateValue> Owner0; // Ok, T should be owner, but is int* + + TemplateValue> Owner1(new int(42)); // Ok, T should be owner, but is int* + Owner1.setVal(&stack_int); // Bad since non-owner assignment + Owner1.setVal(stack_ptr1); // Bad since non-owner assignment + //Owner1.setVal(new int(42)); // Ok, but since typededuction is wrong, this one is considered harmfull + + int *stack_ptr2 = Owner1.getVal(); // Bad, initializing nonowner with owner + + TemplateValue NonOwner1(new int(42)); // Bad, T is int *, hence dynamic memory to non-owner + gsl::owner IntOwner1 = NonOwner1.getVal(); // Bad, since owner initialized with nonowner + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' + + template_function(IntOwner1); // Ok, but not actually ok, since typededuction removes owner + template_function(stack_ptr1); // Bad, but typededuction gets it wrong +} Index: test/clang-tidy/hicpp-signed-bitwise.cpp =================================================================== --- /dev/null +++ test/clang-tidy/hicpp-signed-bitwise.cpp @@ -0,0 +1,219 @@ +// RUN: %check_clang_tidy %s hicpp-signed-bitwise %t + +// These could cause false positives and should not be considered. +struct StreamClass { +}; +StreamClass &operator<<(StreamClass &os, unsigned int i) { + return os; +} +StreamClass &operator<<(StreamClass &os, int i) { + return os; +} +StreamClass &operator>>(StreamClass &os, unsigned int i) { + return os; +} +StreamClass &operator>>(StreamClass &os, int i) { + return os; +} +struct AnotherStream { + AnotherStream &operator<<(unsigned char c) { return *this; } + AnotherStream &operator<<(char c) { return *this; } + + AnotherStream &operator>>(unsigned char c) { return *this; } + AnotherStream &operator>>(char c) { return *this; } +}; + +void binary_bitwise() { + int SValue = 42; + int SResult; + + unsigned int UValue = 42; + unsigned int UResult; + + SResult = SValue & 1; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator + SResult = SValue & -1; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator + SResult = SValue & SValue; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator + + UResult = SValue & 1; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator + UResult = SValue & -1; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator + + UResult = UValue & 1u; // Ok + UResult = UValue & UValue; // Ok + + unsigned char UByte1 = 0u; + unsigned char UByte2 = 16u; + char SByte1 = 0; + char SByte2 = 16; + + UByte1 = UByte1 & UByte2; // Ok + UByte1 = SByte1 & UByte2; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a binary bitwise operator + UByte1 = SByte1 & SByte2; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a binary bitwise operator + SByte1 = SByte1 & SByte2; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a binary bitwise operator + + // More complex expressions. + UResult = UValue & (SByte1 + (SByte1 | SByte2)); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator + // CHECK-MESSAGES: :[[@LINE-2]]:33: warning: use of a signed integer operand with a binary bitwise operator + + // The rest is to demonstrate functionality but all operators are matched equally. + // Therefore functionality is the same for all binary operations. + UByte1 = UByte1 | UByte2; // Ok + UByte1 = UByte1 | SByte2; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a binary bitwise operator + + UByte1 = UByte1 ^ UByte2; // Ok + UByte1 = UByte1 ^ SByte2; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a binary bitwise operator + + UByte1 = UByte1 >> UByte2; // Ok + UByte1 = UByte1 >> SByte2; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a binary bitwise operator + + UByte1 = UByte1 << UByte2; // Ok + UByte1 = UByte1 << SByte2; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a binary bitwise operator + + int SignedInt1 = 1 << 12; + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use of a signed integer operand with a binary bitwise operator + int SignedInt2 = 1u << 12; + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use of a signed integer operand with a binary bitwise operator +} + +void f1(unsigned char c) {} +void f2(char c) {} +void f3(int c) {} + +void unary_bitwise() { + unsigned char UByte1 = 0u; + char SByte1 = 0; + + UByte1 = ~UByte1; // Ok + SByte1 = ~UByte1; + SByte1 = ~SByte1; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a unary bitwise operator + UByte1 = ~SByte1; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use of a signed integer operand with a unary bitwise operator + + unsigned int UInt = 0u; + int SInt = 0; + + f1(~UByte1); // Ok + f1(~SByte1); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use of a signed integer operand with a unary bitwise operator + f1(~UInt); + f1(~SInt); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use of a signed integer operand with a unary bitwise operator + f2(~UByte1); + f2(~SByte1); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use of a signed integer operand with a unary bitwise operator + f2(~UInt); + f2(~SInt); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use of a signed integer operand with a unary bitwise operator + f3(~UByte1); // Ok + f3(~SByte1); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use of a signed integer operand with a unary bitwise operator +} + +/// HICPP uses these examples to demonstrate the rule. +void standard_examples() { + int i = 3; + unsigned int k = 0u; + + int r = i << -1; // Emits -Wshift-count-negative from clang + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use of a signed integer operand with a binary bitwise operator + r = i << 1; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a binary bitwise operator + + r = -1 >> -1; // Emits -Wshift-count-negative from clang + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a binary bitwise operator + r = -1 >> 1; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a binary bitwise operator + + r = -1 >> i; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a binary bitwise operator + r = -1 >> -i; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a binary bitwise operator + + r = ~0; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a unary bitwise operator + r = ~0u; // Ok + k = ~k; // Ok + + unsigned int u = (-1) & 2u; + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use of a signed integer operand with a binary bitwise operator + u = (-1) | 1u; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a binary bitwise operator + u = (-1) ^ 1u; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use of a signed integer operand with a binary bitwise operator +} + +void streams_should_work() { + StreamClass s; + s << 1u; // Ok + s << 1; // Ok + s >> 1; // Ok + s >> 1u; // Ok + + AnotherStream as; + unsigned char uc = 1u; + char sc = 1; + as << uc; // Ok + as << sc; // Ok + as >> uc; // Ok + as >> sc; // Ok +} + +enum OldEnum { + ValueOne, + ValueTwo, +}; + +enum OldSigned : int { + IntOne, + IntTwo, +}; + +void classicEnums() { + OldEnum e1 = ValueOne, e2 = ValueTwo; + int e3; // Using the enum type, results in an error. + e3 = ValueOne | ValueTwo; // Ok + e3 = ValueOne & ValueTwo; // Ok + e3 = ValueOne ^ ValueTwo; // Ok + e3 = e1 | e2; // Ok + e3 = e1 & e2; // Ok + e3 = e1 ^ e2; // Ok + + OldSigned s1 = IntOne, s2 = IntTwo; + int s3; + s3 = IntOne | IntTwo; // Signed + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: use of a signed integer operand with a binary bitwise operator + s3 = IntOne & IntTwo; // Signed + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: use of a signed integer operand with a binary bitwise operator + s3 = IntOne ^ IntTwo; // Signed + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: use of a signed integer operand with a binary bitwise operator + s3 = s1 | s2; // Signed + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: use of a signed integer operand with a binary bitwise operator + s3 = s1 & s2; // Signed + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: use of a signed integer operand with a binary bitwise operator + s3 = s1 ^ s2; // Signed + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: use of a signed integer operand with a binary bitwise operator +} + +enum EnumConstruction { + one = 1, + two = 2, + test1 = 1 << 12, + // CHECK-MESSAGES: [[@LINE-1]]:11: warning: use of a signed integer operand with a binary bitwise operator + test2 = one << two, + // CHECK-MESSAGES: [[@LINE-1]]:11: warning: use of a signed integer operand with a binary bitwise operator + test3 = 1u << 12, + // CHECK-MESSAGES: [[@LINE-1]]:11: warning: use of a signed integer operand with a binary bitwise operator +};