Skip to content

[clang-format] AlignAfterOpenBracket: BlockIndent produces inconsistent/bad formatting #55731

Closed
@H-G-Hristov

Description

@H-G-Hristov

Screen Shot 2022-05-27 at 14 30 32

My .clang-format file looks like:

---
BasedOnStyle: Google
Standard: c++20
IndentWidth: 4
ColumnLimit: 120
AccessModifierOffset: -4
AlignAfterOpenBracket: BlockIndent
AlignArrayOfStructures: Right
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortLambdasOnASingleLine: Empty
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
  AfterClass: true
  AfterEnum: true
  AfterFunction: true
  AfterStruct: true
  AfterUnion: true
  BeforeLambdaBody: true
  SplitEmptyFunction: false
BreakBeforeBraces: Custom
BreakConstructorInitializers: BeforeComma
IncludeCategories:
  # Third party headers
  - Regex:           '^<(boost|bzip|fmt|grpc|gsl|gtest|gmock|isl|json|openssl|tl|toml\+\+|proto|zlib)/'
    Priority:        3
    SortPriority:    0
    CaseSensitive:   true
  # This library headers
  - Regex:           '^<(nblib)'
    Priority:        4
    SortPriority:    0
    CaseSensitive:   true
  # This library headers
  - Regex:           '^"(nblib)'
    Priority:        5
    SortPriority:    0
    CaseSensitive:   true
  # C system headers
  - Regex:           '^<.+\.h>$'
    Priority:        1
    SortPriority:    0
    CaseSensitive:   true
  # C++ standard library headers
  - Regex:           '^<.+'
    Priority:        2
    SortPriority:    0
    CaseSensitive:   true
  # Other headers
  - Regex:           '^".*'
    Priority:        6
    SortPriority:    0
    CaseSensitive:   true
IndentPPDirectives: AfterHash
PackConstructorInitializers: Never
# Different ways to arrange specifiers and qualifiers (e.g. const/volatile).
QualifierAlignment: Left
# Specifies the use of empty lines to separate definition blocks, including classes, structs, enums, and functions.
SeparateDefinitionBlocks: Always
# Add namespace comment after short namespaces
ShortNamespaceLines: 0
...

The result looks like:

#pragma once

#include <memory>
#include <optional>

#include <nblib/types/IForwardIterator.hpp>
#include <nblib/utilities/IFileSystem.hpp>
#include <nblib/utilities/Logging.hpp>

#include "nblib/data/hardware/IStorage.hpp"

namespace nblib::data {

#if defined(__APPLE__)
class ISystemProfiler;
#endif  // defined(__APPLE__)

}  // namespace nblib::data

namespace nblib::utilities {

#if defined(__linux__)
class IProcessSpawner;
#endif  // defined(__linux__)

}  // namespace nblib::utilities

namespace nblib::data {

using LogicalVolumeIteratorBase = nblib::types::IForwardIterator<IStorage::LogicalVolumeInfo>;

class Storage : public IStorage
{
public:
    Storage();

#if defined(__APPLE__)
    explicit Storage(std::unique_ptr<ISystemProfiler> systemProfiler);
#endif

#if defined(__linux__)
    explicit Storage(std::unique_ptr<::nblib::utilities::IProcessSpawner> processSpawner);
#endif

public:
    std::vector<LogicalVolumeInfo> GetLogicalVolumes() const noexcept override;

    std::vector<LogicalVolumeInfo> GetNonSystemLogicalVolumes() const noexcept override;

    std::vector<LogicalVolumeInfo> GetInternalNonRemovableLogicalVolumes() const noexcept override;

    std::vector<std::string> GetPhysicalDiskDriveNames() const noexcept override;

    std::vector<std::string> GetInternalNonRemovablePhysicalDiskDriveNames() const noexcept override;

    std::vector<OpticalDiscDriveInfo> GetOpticalDiscDrives() const noexcept override;

    std::vector<std::string> GetFloppyDiskDriveNames() const noexcept override;

    std::optional<PhysicalDiskDriveInfo> GetPhysicalDiskDriveInfo(const std::string& deviceName
    ) const noexcept override;

    std::optional<LogicalVolumeInfo> GetLogicalVolumeInfo(const std::string& deviceName) const noexcept override;

private:
    template <typename Iterator>
    std::optional<LogicalVolumeInfo> GetLogicalVolumeInfo(Iterator&& iterator, const std::string& deviceName)
        const noexcept;

private:
#if defined(__APPLE__)
    std::unique_ptr<ISystemProfiler> m_systemProfiler;
#endif  // __APPLE__

#if defined(__linux__)
    std::unique_ptr<::nblib::utilities::IProcessSpawner> m_processSpawner;
#endif  // __linux__
};

template <typename IteratorT>
std::optional<IStorage::LogicalVolumeInfo> Storage::GetLogicalVolumeInfo(
    IteratorT&& iterator, const std::string& deviceName
) const noexcept
{
    static_assert(std::is_base_of_v<LogicalVolumeIteratorBase, IteratorT>);

    try {
        LogicalVolumeInfo volumeInfo{};
        while (iterator.GetNext(volumeInfo)) {
            if (volumeInfo.deviceName == deviceName) {
                return volumeInfo;
            }
        }

        return {};
    } catch (const std::exception& e) {
        UCLOG_ERROR << "Unable to get logical volume info: " << e.what();

        return {};
    }
}

}  // namespace nblib::data

I would expect something like:

#pragma once

#include <memory>
#include <optional>

#include <nblib/types/IForwardIterator.hpp>
#include <nblib/utilities/IFileSystem.hpp>
#include <nblib/utilities/Logging.hpp>

#include "nblib/data/hardware/IStorage.hpp"

namespace nblib::data {

#if defined(__APPLE__)
class ISystemProfiler;
#endif  // defined(__APPLE__)

}  // namespace nblib::data

namespace nblib::utilities {

#if defined(__linux__)
class IProcessSpawner;
#endif  // defined(__linux__)

}  // namespace nblib::utilities

namespace nblib::data {

using LogicalVolumeIteratorBase = nblib::types::IForwardIterator<IStorage::LogicalVolumeInfo>;

class Storage : public IStorage
{
public:
    Storage();

#if defined(__APPLE__)
    explicit Storage(std::unique_ptr<ISystemProfiler> systemProfiler);
#endif

#if defined(__linux__)
    explicit Storage(std::unique_ptr<::nblib::utilities::IProcessSpawner> processSpawner);
#endif

public:
    std::vector<LogicalVolumeInfo> GetLogicalVolumes() const noexcept override;

    std::vector<LogicalVolumeInfo> GetNonSystemLogicalVolumes() const noexcept override;

    std::vector<LogicalVolumeInfo> GetInternalNonRemovableLogicalVolumes() const noexcept override;

    std::vector<std::string> GetPhysicalDiskDriveNames() const noexcept override;

    std::vector<std::string> GetInternalNonRemovablePhysicalDiskDriveNames() const noexcept override;

    std::vector<OpticalDiscDriveInfo> GetOpticalDiscDrives() const noexcept override;

    std::vector<std::string> GetFloppyDiskDriveNames() const noexcept override;

    std::optional<PhysicalDiskDriveInfo> GetPhysicalDiskDriveInfo(
       const std::string& deviceName
    ) const noexcept override;

    std::optional<LogicalVolumeInfo> GetLogicalVolumeInfo(const std::string& deviceName) const noexcept override;

private:
    template <typename Iterator>
    std::optional<LogicalVolumeInfo> GetLogicalVolumeInfo(
        Iterator&& iterator, const std::string& deviceName
    ) const noexcept;

private:
#if defined(__APPLE__)
    std::unique_ptr<ISystemProfiler> m_systemProfiler;
#endif  // __APPLE__

#if defined(__linux__)
    std::unique_ptr<::nblib::utilities::IProcessSpawner> m_processSpawner;
#endif  // __linux__
};

template <typename IteratorT>
std::optional<IStorage::LogicalVolumeInfo> Storage::GetLogicalVolumeInfo(
    IteratorT&& iterator, const std::string& deviceName
) const noexcept
{
    static_assert(std::is_base_of_v<LogicalVolumeIteratorBase, IteratorT>);

    try {
        LogicalVolumeInfo volumeInfo{};
        while (iterator.GetNext(volumeInfo)) {
            if (volumeInfo.deviceName == deviceName) {
                return volumeInfo;
            }
        }

        return {};
    } catch (const std::exception& e) {
        UCLOG_ERROR << "Unable to get logical volume info: " << e.what();

        return {};
    }
}

}  // namespace nblib::data

If a function declaration cannot fit on a single line it should preferably be broken after the opening and closing bracket always.

   ...

    // Fits on a single line:
    void myFunc2(int param) const noexcept;
    
    // Doesn't fit on a single line:
    void myFunc2(
        long long veryLongParametrName
    ) const noexcept;

   void myFunc3(
       int param1,
       int param2,
   ) const noexcept;
   
   // What about trailing return types?
   auto myFunc3(
       int param1,
       int param2,
   ) const noexcept -> std::vector<of_very_long_type>;

   auto myFunc3(
       int param1,
       int param2,
   ) const noexcept 
     -> std::vector<of_very_long_type>;

   ...

I love this new option but it needs work for braces {} and brackets (), etc.

References:

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions