Skip to content

style: clang-tidy #2478

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 15, 2020
Merged

style: clang-tidy #2478

merged 8 commits into from
Sep 15, 2020

Conversation

henryiii
Copy link
Collaborator

@henryiii henryiii commented Sep 11, 2020

This adds the ability to run a clang-tidy list of checks in CI, and adds a few checks to get started. Due to the methodology used, only checks that made changes (one commit per check) were added. Only extremely safe checks were added. Eventually, it would be nice to expand further; there are some very useful/powerful clang tidy checks. You can see the groups here. You can do groups of checks, like modernize-* (that's C++11), readability-*, portability-*, bugprone-*, cppcoreguidelines-*, or performance-* (and remove ones that you don't support). You can also disable inline for special cases.

Checks added:

  • Namespaces always have a closing comment
  • override is always used when overriding
  • .empty() is used to check empty containers instead of size comparisons
  • using used instead of typedef
  • = default used for trivial bodies (except where MSVC 2015 requires otherwise)
  • auto used when the typename would be otherwise duplicated on the same line (not everywhere, as that would be worse, not better!)
  • emplace used instead of push back for containers

I've also added instructions for running this locally (where you can perform the fixes automatically).

@henryiii
Copy link
Collaborator Author

henryiii commented Sep 11, 2020

FYI, this is the full list of checks in Clang 10: (click to expand)
    abseil-duration-addition
    abseil-duration-comparison
    abseil-duration-conversion-cast
    abseil-duration-division
    abseil-duration-factory-float
    abseil-duration-factory-scale
    abseil-duration-subtraction
    abseil-duration-unnecessary-conversion
    abseil-faster-strsplit-delimiter
    abseil-no-internal-dependencies
    abseil-no-namespace
    abseil-redundant-strcat-calls
    abseil-str-cat-append
    abseil-string-find-startswith
    abseil-time-comparison
    abseil-time-subtraction
    abseil-upgrade-duration-conversions
    android-cloexec-accept
    android-cloexec-accept4
    android-cloexec-creat
    android-cloexec-dup
    android-cloexec-epoll-create
    android-cloexec-epoll-create1
    android-cloexec-fopen
    android-cloexec-inotify-init
    android-cloexec-inotify-init1
    android-cloexec-memfd-create
    android-cloexec-open
    android-cloexec-pipe
    android-cloexec-pipe2
    android-cloexec-socket
    android-comparison-in-temp-failure-retry
    boost-use-to-string
    bugprone-argument-comment
    bugprone-assert-side-effect
    bugprone-bad-signal-to-kill-thread
    bugprone-bool-pointer-implicit-conversion
    bugprone-branch-clone
    bugprone-copy-constructor-init
    bugprone-dangling-handle
    bugprone-dynamic-static-initializers
    bugprone-exception-escape
    bugprone-fold-init-type
    bugprone-forward-declaration-namespace
    bugprone-forwarding-reference-overload
    bugprone-inaccurate-erase
    bugprone-incorrect-roundings
    bugprone-infinite-loop
    bugprone-integer-division
    bugprone-lambda-function-name
    bugprone-macro-parentheses
    bugprone-macro-repeated-side-effects
    bugprone-misplaced-operator-in-strlen-in-alloc
    bugprone-misplaced-widening-cast
    bugprone-move-forwarding-reference
    bugprone-multiple-statement-macro
    bugprone-narrowing-conversions
    bugprone-not-null-terminated-result
    bugprone-parent-virtual-call
    bugprone-posix-return
    bugprone-signed-char-misuse
    bugprone-sizeof-container
    bugprone-sizeof-expression
    bugprone-string-constructor
    bugprone-string-integer-assignment
    bugprone-string-literal-with-embedded-nul
    bugprone-suspicious-enum-usage
    bugprone-suspicious-memset-usage
    bugprone-suspicious-missing-comma
    bugprone-suspicious-semicolon
    bugprone-suspicious-string-compare
    bugprone-swapped-arguments
    bugprone-terminating-continue
    bugprone-throw-keyword-missing
    bugprone-too-small-loop-variable
    bugprone-undefined-memory-manipulation
    bugprone-undelegated-constructor
    bugprone-unhandled-self-assignment
    bugprone-unused-raii
    bugprone-unused-return-value
    bugprone-use-after-move
    bugprone-virtual-near-miss
    cert-dcl03-c
    cert-dcl16-c
    cert-dcl21-cpp
    cert-dcl50-cpp
    cert-dcl54-cpp
    cert-dcl58-cpp
    cert-dcl59-cpp
    cert-env33-c
    cert-err09-cpp
    cert-err34-c
    cert-err52-cpp
    cert-err58-cpp
    cert-err60-cpp
    cert-err61-cpp
    cert-fio38-c
    cert-flp30-c
    cert-mem57-cpp
    cert-msc30-c
    cert-msc32-c
    cert-msc50-cpp
    cert-msc51-cpp
    cert-oop11-cpp
    cert-oop54-cpp
    cert-oop58-cpp
    cert-pos44-c
    clang-analyzer-apiModeling.StdCLibraryFunctions
    clang-analyzer-apiModeling.TrustNonnull
    clang-analyzer-apiModeling.google.GTest
    clang-analyzer-apiModeling.llvm.CastValue
    clang-analyzer-apiModeling.llvm.ReturnValue
    clang-analyzer-core.CallAndMessage
    clang-analyzer-core.DivideZero
    clang-analyzer-core.DynamicTypePropagation
    clang-analyzer-core.NonNullParamChecker
    clang-analyzer-core.NonnilStringConstants
    clang-analyzer-core.NullDereference
    clang-analyzer-core.StackAddrEscapeBase
    clang-analyzer-core.StackAddressEscape
    clang-analyzer-core.UndefinedBinaryOperatorResult
    clang-analyzer-core.VLASize
    clang-analyzer-core.builtin.BuiltinFunctions
    clang-analyzer-core.builtin.NoReturnFunctions
    clang-analyzer-core.uninitialized.ArraySubscript
    clang-analyzer-core.uninitialized.Assign
    clang-analyzer-core.uninitialized.Branch
    clang-analyzer-core.uninitialized.CapturedBlockVariable
    clang-analyzer-core.uninitialized.UndefReturn
    clang-analyzer-cplusplus.InnerPointer
    clang-analyzer-cplusplus.Move
    clang-analyzer-cplusplus.NewDelete
    clang-analyzer-cplusplus.NewDeleteLeaks
    clang-analyzer-cplusplus.PureVirtualCall
    clang-analyzer-cplusplus.SelfAssignment
    clang-analyzer-cplusplus.SmartPtr
    clang-analyzer-cplusplus.VirtualCallModeling
    clang-analyzer-deadcode.DeadStores
    clang-analyzer-fuchsia.HandleChecker
    clang-analyzer-nullability.NullPassedToNonnull
    clang-analyzer-nullability.NullReturnedFromNonnull
    clang-analyzer-nullability.NullabilityBase
    clang-analyzer-nullability.NullableDereferenced
    clang-analyzer-nullability.NullablePassedToNonnull
    clang-analyzer-nullability.NullableReturnedFromNonnull
    clang-analyzer-optin.cplusplus.UninitializedObject
    clang-analyzer-optin.cplusplus.VirtualCall
    clang-analyzer-optin.mpi.MPI-Checker
    clang-analyzer-optin.osx.OSObjectCStyleCast
    clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker
    clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker
    clang-analyzer-optin.performance.GCDAntipattern
    clang-analyzer-optin.performance.Padding
    clang-analyzer-optin.portability.UnixAPI
    clang-analyzer-osx.API
    clang-analyzer-osx.MIG
    clang-analyzer-osx.NSOrCFErrorDerefChecker
    clang-analyzer-osx.NumberObjectConversion
    clang-analyzer-osx.OSObjectRetainCount
    clang-analyzer-osx.ObjCProperty
    clang-analyzer-osx.SecKeychainAPI
    clang-analyzer-osx.cocoa.AtSync
    clang-analyzer-osx.cocoa.AutoreleaseWrite
    clang-analyzer-osx.cocoa.ClassRelease
    clang-analyzer-osx.cocoa.Dealloc
    clang-analyzer-osx.cocoa.IncompatibleMethodTypes
    clang-analyzer-osx.cocoa.Loops
    clang-analyzer-osx.cocoa.MissingSuperCall
    clang-analyzer-osx.cocoa.NSAutoreleasePool
    clang-analyzer-osx.cocoa.NSError
    clang-analyzer-osx.cocoa.NilArg
    clang-analyzer-osx.cocoa.NonNilReturnValue
    clang-analyzer-osx.cocoa.ObjCGenerics
    clang-analyzer-osx.cocoa.RetainCount
    clang-analyzer-osx.cocoa.RetainCountBase
    clang-analyzer-osx.cocoa.RunLoopAutoreleaseLeak
    clang-analyzer-osx.cocoa.SelfInit
    clang-analyzer-osx.cocoa.SuperDealloc
    clang-analyzer-osx.cocoa.UnusedIvars
    clang-analyzer-osx.cocoa.VariadicMethodTypes
    clang-analyzer-osx.coreFoundation.CFError
    clang-analyzer-osx.coreFoundation.CFNumber
    clang-analyzer-osx.coreFoundation.CFRetainRelease
    clang-analyzer-osx.coreFoundation.containers.OutOfBounds
    clang-analyzer-osx.coreFoundation.containers.PointerSizedValues
    clang-analyzer-security.FloatLoopCounter
    clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling
    clang-analyzer-security.insecureAPI.SecuritySyntaxChecker
    clang-analyzer-security.insecureAPI.UncheckedReturn
    clang-analyzer-security.insecureAPI.bcmp
    clang-analyzer-security.insecureAPI.bcopy
    clang-analyzer-security.insecureAPI.bzero
    clang-analyzer-security.insecureAPI.decodeValueOfObjCType
    clang-analyzer-security.insecureAPI.getpw
    clang-analyzer-security.insecureAPI.gets
    clang-analyzer-security.insecureAPI.mkstemp
    clang-analyzer-security.insecureAPI.mktemp
    clang-analyzer-security.insecureAPI.rand
    clang-analyzer-security.insecureAPI.strcpy
    clang-analyzer-security.insecureAPI.vfork
    clang-analyzer-unix.API
    clang-analyzer-unix.DynamicMemoryModeling
    clang-analyzer-unix.Malloc
    clang-analyzer-unix.MallocSizeof
    clang-analyzer-unix.MismatchedDeallocator
    clang-analyzer-unix.Vfork
    clang-analyzer-unix.cstring.BadSizeArg
    clang-analyzer-unix.cstring.CStringModeling
    clang-analyzer-unix.cstring.NullArg
    clang-analyzer-valist.CopyToSelf
    clang-analyzer-valist.Uninitialized
    clang-analyzer-valist.Unterminated
    clang-analyzer-valist.ValistBase
    cppcoreguidelines-avoid-c-arrays
    cppcoreguidelines-avoid-goto
    cppcoreguidelines-avoid-magic-numbers
    cppcoreguidelines-c-copy-assignment-signature
    cppcoreguidelines-explicit-virtual-functions
    cppcoreguidelines-init-variables
    cppcoreguidelines-interfaces-global-init
    cppcoreguidelines-macro-usage
    cppcoreguidelines-narrowing-conversions
    cppcoreguidelines-no-malloc
    cppcoreguidelines-non-private-member-variables-in-classes
    cppcoreguidelines-owning-memory
    cppcoreguidelines-pro-bounds-array-to-pointer-decay
    cppcoreguidelines-pro-bounds-constant-array-index
    cppcoreguidelines-pro-bounds-pointer-arithmetic
    cppcoreguidelines-pro-type-const-cast
    cppcoreguidelines-pro-type-cstyle-cast
    cppcoreguidelines-pro-type-member-init
    cppcoreguidelines-pro-type-reinterpret-cast
    cppcoreguidelines-pro-type-static-cast-downcast
    cppcoreguidelines-pro-type-union-access
    cppcoreguidelines-pro-type-vararg
    cppcoreguidelines-slicing
    cppcoreguidelines-special-member-functions
    darwin-avoid-spinlock
    darwin-dispatch-once-nonstatic
    fuchsia-default-arguments-calls
    fuchsia-default-arguments-declarations
    fuchsia-header-anon-namespaces
    fuchsia-multiple-inheritance
    fuchsia-overloaded-operator
    fuchsia-restrict-system-includes
    fuchsia-statically-constructed-objects
    fuchsia-trailing-return
    fuchsia-virtual-inheritance
    google-build-explicit-make-pair
    google-build-namespaces
    google-build-using-namespace
    google-default-arguments
    google-explicit-constructor
    google-global-names-in-headers
    google-objc-avoid-nsobject-new
    google-objc-avoid-throwing-exception
    google-objc-function-naming
    google-objc-global-variable-declaration
    google-readability-avoid-underscore-in-googletest-name
    google-readability-braces-around-statements
    google-readability-casting
    google-readability-function-size
    google-readability-namespace-comments
    google-readability-todo
    google-runtime-int
    google-runtime-operator
    google-runtime-references
    google-upgrade-googletest-case
    hicpp-avoid-c-arrays
    hicpp-avoid-goto
    hicpp-braces-around-statements
    hicpp-deprecated-headers
    hicpp-exception-baseclass
    hicpp-explicit-conversions
    hicpp-function-size
    hicpp-invalid-access-moved
    hicpp-member-init
    hicpp-move-const-arg
    hicpp-multiway-paths-covered
    hicpp-named-parameter
    hicpp-new-delete-operators
    hicpp-no-array-decay
    hicpp-no-assembler
    hicpp-no-malloc
    hicpp-noexcept-move
    hicpp-signed-bitwise
    hicpp-special-member-functions
    hicpp-static-assert
    hicpp-undelegated-constructor
    hicpp-uppercase-literal-suffix
    hicpp-use-auto
    hicpp-use-emplace
    hicpp-use-equals-default
    hicpp-use-equals-delete
    hicpp-use-noexcept
    hicpp-use-nullptr
    hicpp-use-override
    hicpp-vararg
    linuxkernel-must-check-errs
    llvm-header-guard
    llvm-include-order
    llvm-namespace-comment
    llvm-prefer-isa-or-dyn-cast-in-conditionals
    llvm-prefer-register-over-unsigned
    llvm-qualified-auto
    llvm-twine-local
    misc-definitions-in-headers
    misc-misplaced-const
    misc-new-delete-overloads
    misc-non-copyable-objects
    misc-non-private-member-variables-in-classes
    misc-redundant-expression
    misc-static-assert
    misc-throw-by-value-catch-by-reference
    misc-unconventional-assign-operator
    misc-uniqueptr-reset-release
    misc-unused-alias-decls
    misc-unused-parameters
    misc-unused-using-decls
    modernize-avoid-bind
    modernize-avoid-c-arrays
    modernize-concat-nested-namespaces
    modernize-deprecated-headers
    modernize-deprecated-ios-base-aliases
    modernize-loop-convert
    modernize-make-shared
    modernize-make-unique
    modernize-pass-by-value
    modernize-raw-string-literal
    modernize-redundant-void-arg
    modernize-replace-auto-ptr
    modernize-replace-random-shuffle
    modernize-return-braced-init-list
    modernize-shrink-to-fit
    modernize-unary-static-assert
    modernize-use-auto
    modernize-use-bool-literals
    modernize-use-default-member-init
    modernize-use-emplace
    modernize-use-equals-default
    modernize-use-equals-delete
    modernize-use-nodiscard
    modernize-use-noexcept
    modernize-use-nullptr
    modernize-use-override
    modernize-use-trailing-return-type
    modernize-use-transparent-functors
    modernize-use-uncaught-exceptions
    modernize-use-using
    mpi-buffer-deref
    mpi-type-mismatch
    objc-avoid-nserror-init
    objc-forbidden-subclassing
    objc-missing-hash
    objc-property-declaration
    objc-super-self
    openmp-exception-escape
    openmp-use-default-none
    performance-faster-string-find
    performance-for-range-copy
    performance-implicit-conversion-in-loop
    performance-inefficient-algorithm
    performance-inefficient-string-concatenation
    performance-inefficient-vector-operation
    performance-move-const-arg
    performance-move-constructor-init
    performance-no-automatic-move
    performance-noexcept-move-constructor
    performance-trivially-destructible
    performance-type-promotion-in-math-fn
    performance-unnecessary-copy-initialization
    performance-unnecessary-value-param
    portability-simd-intrinsics
    readability-avoid-const-params-in-decls
    readability-braces-around-statements
    readability-const-return-type
    readability-container-size-empty
    readability-convert-member-functions-to-static
    readability-delete-null-pointer
    readability-deleted-default
    readability-else-after-return
    readability-function-size
    readability-identifier-naming
    readability-implicit-bool-conversion
    readability-inconsistent-declaration-parameter-name
    readability-isolate-declaration
    readability-magic-numbers
    readability-make-member-function-const
    readability-misleading-indentation
    readability-misplaced-array-index
    readability-named-parameter
    readability-non-const-parameter
    readability-qualified-auto
    readability-redundant-access-specifiers
    readability-redundant-control-flow
    readability-redundant-declaration
    readability-redundant-function-ptr-dereference
    readability-redundant-member-init
    readability-redundant-preprocessor
    readability-redundant-smartptr-get
    readability-redundant-string-cstr
    readability-redundant-string-init
    readability-simplify-boolean-expr
    readability-simplify-subscript-expr
    readability-static-accessed-through-instance
    readability-static-definition-in-anonymous-namespace
    readability-string-compare
    readability-uniqueptr-delete-release
    readability-uppercase-literal-suffix
    zircon-temporary-objects

(or here: https://clang.llvm.org/extra/clang-tidy/checks/list.html)

The goal is not to enable them all, as many of them conflict (google vs. llvm, etc), but to just enable the ones or the groups (minus a few) that are useful.

Also, the default checks are pretty safe and probably can be enabled. I partially ran them and don't think there were any changes to (at least the headers) in the current form.

@henryiii henryiii force-pushed the style/clangtidy branch 3 times, most recently from 4704ae8 to 00e4bf2 Compare September 12, 2020 12:27
@henryiii
Copy link
Collaborator Author

henryiii commented Sep 12, 2020

I think that this solves the occasional output failure on Windows CI (and I think I've seen it in GooFit on Unix) - the destructor was incorrect, as discovered by the default analyzer checks in clang-tidy.

/pybind11/include/pybind11/iostream.h:71:9: warning: Call to virtual method 'pythonbuf::sync' during destruction bypasses virtual dispatch [clang-analyzer-optin.cplusplus.VirtualCall]
        sync();
        ^
/pybind11/tests/test_iostream.cpp:72:5: note: Calling '~scoped_ostream_redirect'
    });
    ^
/pybind11/include/pybind11/iostream.h:118:5: note: Calling '~pythonbuf'
    }
    ^
/pybind11/include/pybind11/iostream.h:71:9: note: Call to virtual method 'pythonbuf::sync' during destruction bypasses virtual dispatch
        sync();
        ^

Here's the page on possible solutions, I chose the private member one, though the fully qualified one might work too. Useful page.

@bstaletic
Copy link
Collaborator

I chose the private member one, though the fully qualified one might work too.

That does silence the warning, but it shouldn't have any other effect. When executing the destructor, every member function is treated as if it were non-virtual. All in all, this should have exactly the same assembly output and I'd be very surprised if it has any effect on the flake you're referring to.

@henryiii
Copy link
Collaborator Author

henryiii commented Sep 12, 2020

The problem is that the correct destructor is guaranteed to be called, but when it is called (due to destruction order), the vtable for the base is loaded, not the child. So ->sync() is the base's sync, not the child's sync. By skipping the table when calling sync, then it calls the correct one. This is exactly the GooFit bug / CI flake bug we see - the destructor does not force a purge; if the CI is loaded down (or pretty much anytime in GooFit that it is working), the destructor does not cause the final output to show up.

(Sync calls virtual functions too, but those are correct in the base. It's the call from the destructor that's a problem.

If it's a clear solution, I could do the fully qualified solution instead if you prefer.

@bstaletic
Copy link
Collaborator

the table for the base is loaded, not the child.

This is the part that I wasn't aware, but it makes sense. Thanks for the explanation.

@henryiii
Copy link
Collaborator Author

henryiii commented Sep 12, 2020

I wasn't aware of it either, which is why I've been trying to solve this bug (in the context of GooFit, anyway) for over two years...

The fully qualified solution is a little cleaner/clearer, will change to that. (Edit: I think I miss-read, the fully-qualified method was just a way to silence the warning and clarify you wanted to do that).

@henryiii
Copy link
Collaborator Author

(It is possible I'm wrong here, but it's still slightly more correct this way, and we can watch for that bug in the future)

@bstaletic
Copy link
Collaborator

A simple check says that it's not as broken as you seem to suggest.

https://godbolt.org/z/4fYojh

Notice that b has a static type of Base*, but dynamic type of Derived*. Two observations:

  1. The correct destructor is called, just like you said it would. The destructor of Derived.
  2. Once we're inside that destructor, a call to f() isn't virtual. We're already "inside" Derived and so Derived::f gets called.

Can we come up with a case where "some other f()" gets called?

@henryiii
Copy link
Collaborator Author

For pybind11, this only fails on msvc (and NVCC for GooFit). I would rather expect the other compilers already handle this properly.

Copy link
Collaborator

@YannickJadoul YannickJadoul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! :-)
I also really like the incremental approach, for these kinds of changes. As far as I'm concerned, we could merge small batches of these in separate PRs, even. That would keep things more easily reviewable.

The only one I'm a bit skeptical about is modernize-use-using. Not sure it that's so problematic. But yes, it's modernizing our code base, so I'm also not opposed.

Personal request: maybe something that flags C-style casts? (cppcoreguidelines-pro-type-cstyle-cast?) Then again, in the context of the Python C API, C-style casts might make sense, ofc, since it is C.

@henryiii
Copy link
Collaborator Author

C-style casts are required in a few places, I believe, but it would be good to identify those and replace the others. I tried this briefly (as part of several others - doing many at once leads to conflicts, though). I know the cast I worked on recently was not allowed to be written as a static_cast, since it was converting something like *T to int (or something like that, I don't remember).

I didn't add the NULL/0 -> nullptr because it looked odd to have a nullptr in a C api. :)

@YannickJadoul
Copy link
Collaborator

YannickJadoul commented Sep 13, 2020

C-style casts are required in a few places, I believe, but it would be good to identify those and replace the others. I tried this briefly (as part of several others - doing many at once leads to conflicts, though). I know the cast I worked on recently was not allowed to be written as a static_cast, since it was converting something like *T to int (or something like that, I don't remember).

Can be in a different PR, though. I can put some effort into that, if you want; not per se expecting you to do it, ofc ;-)

Also: ugh, never realized there's C casts you can't even do with a reinterpret_cast...
EDIT: Nevermind that reinterpret_cast does work, here! :-)

I didn't add the NULL/0 -> nullptr because it looked odd to have a nullptr in a C api. :)

Agreed. That's why I'm a bit hesitant indeed.

@henryiii
Copy link
Collaborator Author

Ahh, I think I didn't try reinterpret_cast.

@henryiii
Copy link
Collaborator Author

On that page I linked, "has fix" doesn't seem to be accurate. cppcoreguidelines-pro-type-cstyle-cast says it has a fix but doesn't, and google-readability-casting says it doesn't but does. However, (correctly) it only fixes when it's sure (so I think pretty much .cpp files), so this will take some work to convert.

@henryiii
Copy link
Collaborator Author

Assuming you run it (the formula is in the PR), try running it a couple of times, it finds a few more fixes the second time. Remember to clean after you run to get it to recompile. And no multithreaded building if you are changing the source with -fix!

@henryiii
Copy link
Collaborator Author

henryiii commented Sep 13, 2020

Yes, it's here: https://clang.llvm.org/extra/clang-tidy/checks/readability-qualified-auto.html - that recommends (based on the LLVM style guide) that auto be fully qualified.

For example:

/pybind11/tests/test_callbacks.cpp:108:9: warning: 'auto result' can be declared as 'const auto *result' [readability-qualified-auto]
        auto result = f.target<fn_type>();
        ^~~~~
        const auto *
/pybind11/tests/test_callbacks.cpp:108:9: note: FIX-IT applied suggested code changes

I'll add that one too, along with a partial attempt for C++ style casts.

Edit: Nevermind, seems we hide differences in constness between Python 2.7, 3.5, and 3.8 using unqualified auto. Could be be improved a bit, with explicit ignores and notes on the ones that may or not be const, but for now, leaving that check off.

@henryiii henryiii force-pushed the style/clangtidy branch 2 times, most recently from 98da5c4 to 7d86d3b Compare September 13, 2020 14:24
@bstaletic
Copy link
Collaborator

Also: ugh, never realized there's C casts you can't even do with a reinterpret_cast...

For the record, C casts are pretty wild. They try, in order:

  1. const_cast<NewType>
  2. static_cast<NewType>, but with relaxed rules
  3. const_cast(static_cast()), now in combination and with relaxed rules
  4. reinterpret_cast<NewType>
  5. const_cast(reinterpret_cast())

For more info see https://en.cppreference.com/w/cpp/language/explicit_cast#Explanation

@wjakob
Copy link
Member

wjakob commented Sep 15, 2020

Oh wow, that is a nasty issue you've found in iostream.h. Although this code should probably work "as-is" (dispatching sync() to the pybind11-provided class), I can see how this is probably on the fringe of well-defined behavior in a compiler like MSVC. Your workaround with the separate non-virtual function looks good to me.

I have one objection to some of those changes here, and that's the excessive use of "fancy" C++ cast operators. They definitely do have a place in the standard, and that's when working with polymorphic classes, where some casts can lead to undefined behavior, and others will resolve into complex operations that walk through RTTI tables.

But for very basic stuff, like casting something to an int, or casting a void* pointer to a CPython API object, I find this really unreadable to have lots of static_casts over the place. Especially considering the later point, it's worth noting that almost all pointers in pybind11 are CPython API C-style pointers. We have virtually (ha!) no polymorphism within pybind11 itself.

```
/pybind11/include/pybind11/iostream.h:71:9: warning: Call to virtual method 'pythonbuf::sync' during destruction bypasses virtual dispatch [clang-analyzer-optin.cplusplus.VirtualCall]
        sync();
        ^
/pybind11/tests/test_iostream.cpp:72:5: note: Calling '~scoped_ostream_redirect'
    });
```
@henryiii
Copy link
Collaborator Author

I've dropped that cast operator commit (was always supposed to be completely non controversial), and rebased. Going in now, then!

@henryiii henryiii merged commit e7bafc8 into pybind:master Sep 15, 2020
@henryiii henryiii deleted the style/clangtidy branch September 15, 2020 13:57
@henryiii
Copy link
Collaborator Author

I can see how this is probably on the fringe of well-defined behavior in a compiler like MSVC. Your workaround with the separate non-virtual function looks good to me.

That's really what it looks like to me. Though I will unconvince myself of this instantly if this fails CI again at some point after the merge. ;)

@rwgk
Copy link
Collaborator

rwgk commented Sep 15, 2020

I only got a chance to look here now: massive thanks @henryiii !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants