Commit a75f8d98 authored by mydeveloperday's avatar mydeveloperday Committed by paulhoad
Browse files

[clang-format] [PR36294] AlwaysBreakAfterReturnType works incorrectly for some operator functions

Summary:
https://bugs.llvm.org/show_bug.cgi?id=36294

Addressing bug related to returning after return type not being honoured for some operator types.

```
$ bin/clang-format --style="{BasedOnStyle: llvm, AlwaysBreakAfterReturnType: TopLevelDefinitions}" /tmp/foo.cpp
class Foo {
public:
  bool operator!() const;
  bool operator<(Foo const &) const;
  bool operator*() const;
  bool operator->() const;
  bool operator+() const;
  bool operator-() const;
  bool f() const;
};

bool Foo::operator!() const { return true; }
bool
Foo::operator<(Foo const &) const {
  return true;
}
bool Foo::operator*() const { return true; }
bool Foo::operator->() const { return true; }
bool
Foo::operator+() const {
  return true;
}
bool
Foo::operator-() const {
  return true;
}
bool
Foo::f() const {
  return true;
}
```

Reviewers: mitchell-stellar, klimek, owenpan, sammccall, rianquinn

Reviewed By: sammccall

Subscribers: merge_guards_bot, cfe-commits

Tags: #clang-format, #clang-tools-extra, #clang

Differential Revision: https://reviews.llvm.org/D69573
parent 874b6495
Loading
Loading
Loading
Loading
+28 −6
Original line number Diff line number Diff line
@@ -950,9 +950,9 @@ private:
        if (CurrentToken->isOneOf(tok::star, tok::amp))
          CurrentToken->Type = TT_PointerOrReference;
        consumeToken();
        if (CurrentToken &&
            CurrentToken->Previous->isOneOf(TT_BinaryOperator, TT_UnaryOperator,
                                            tok::comma))
        if (CurrentToken && CurrentToken->Previous->isOneOf(
                                TT_BinaryOperator, TT_UnaryOperator, tok::comma,
                                tok::star, tok::arrow, tok::amp, tok::ampamp))
          CurrentToken->Previous->Type = TT_OverloadedOperator;
      }
      if (CurrentToken) {
@@ -1344,8 +1344,12 @@ private:
        Contexts.back().IsExpression = false;
    } else if (Current.is(tok::kw_new)) {
      Contexts.back().CanBeExpression = false;
    } else if (Current.isOneOf(tok::semi, tok::exclaim)) {
    } else if (Current.is(tok::semi) ||
               (Current.is(tok::exclaim) && Current.Previous &&
                !Current.Previous->is(tok::kw_operator))) {
      // This should be the condition or increment in a for-loop.
      // But not operator !() (can't use TT_OverloadedOperator here as its not
      // been annotated yet).
      Contexts.back().IsExpression = true;
    }
  }
@@ -2155,11 +2159,22 @@ static bool isFunctionDeclarationName(const FormatToken &Current,
        continue;
      if (Next->isOneOf(tok::kw_new, tok::kw_delete)) {
        // For 'new[]' and 'delete[]'.
        if (Next->Next && Next->Next->is(tok::l_square) && Next->Next->Next &&
            Next->Next->Next->is(tok::r_square))
        if (Next->Next &&
            Next->Next->startsSequence(tok::l_square, tok::r_square))
          Next = Next->Next->Next;
        continue;
      }
      if (Next->startsSequence(tok::l_square, tok::r_square)) {
        // For operator[]().
        Next = Next->Next;
        continue;
      }
      if ((Next->isSimpleTypeSpecifier() || Next->is(tok::identifier)) &&
          Next->Next && Next->Next->isOneOf(tok::star, tok::amp, tok::ampamp)) {
        // For operator void*(), operator char*(), operator Foo*().
        Next = Next->Next;
        continue;
      }

      break;
    }
@@ -2673,6 +2688,13 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
                                    tok::l_square));
  if (Right.is(tok::star) && Left.is(tok::l_paren))
    return false;
  if (Right.isOneOf(tok::star, tok::amp, tok::ampamp) &&
      (Left.is(tok::identifier) || Left.isSimpleTypeSpecifier()) &&
      Left.Previous && Left.Previous->is(tok::kw_operator))
    // Space between the type and the *
    // operator void*(), operator char*(), operator Foo*() dependant
    // on PointerAlignment style.
    return (Style.PointerAlignment == FormatStyle::PAS_Right);
  const auto SpaceRequiredForArrayInitializerLSquare =
      [](const FormatToken &LSquareTok, const FormatStyle &Style) {
        return Style.SpacesInContainerLiterals ||
+89 −1
Original line number Diff line number Diff line
@@ -6134,7 +6134,48 @@ TEST_F(FormatTest, ReturnTypeBreakingStyle) {
               "void\n"
               "A::operator>>() {}\n"
               "void\n"
               "A::operator+() {}\n",
               "A::operator+() {}\n"
               "void\n"
               "A::operator*() {}\n"
               "void\n"
               "A::operator->() {}\n"
               "void\n"
               "A::operator void *() {}\n"
               "void\n"
               "A::operator void &() {}\n"
               "void\n"
               "A::operator void &&() {}\n"
               "void\n"
               "A::operator char *() {}\n"
               "void\n"
               "A::operator[]() {}\n"
               "void\n"
               "A::operator!() {}\n",
               Style);
  verifyFormat("constexpr auto\n"
               "operator()() const -> reference {}\n"
               "constexpr auto\n"
               "operator>>() const -> reference {}\n"
               "constexpr auto\n"
               "operator+() const -> reference {}\n"
               "constexpr auto\n"
               "operator*() const -> reference {}\n"
               "constexpr auto\n"
               "operator->() const -> reference {}\n"
               "constexpr auto\n"
               "operator++() const -> reference {}\n"
               "constexpr auto\n"
               "operator void *() const -> reference {}\n"
               "constexpr auto\n"
               "operator void &() const -> reference {}\n"
               "constexpr auto\n"
               "operator void &&() const -> reference {}\n"
               "constexpr auto\n"
               "operator char *() const -> reference {}\n"
               "constexpr auto\n"
               "operator!() const -> reference {}\n"
               "constexpr auto\n"
               "operator[]() const -> reference {}\n",
               Style);
  verifyFormat("void *operator new(std::size_t s);", // No break here.
               Style);
@@ -14755,6 +14796,53 @@ TEST_F(FormatTest, STLWhileNotDefineChed) {
               "#endif // while");
}
TEST_F(FormatTest, OperatorSpacing) {
  FormatStyle Style = getLLVMStyle();
  Style.PointerAlignment = FormatStyle::PAS_Right;
  verifyFormat("Foo::operator*();", Style);
  verifyFormat("Foo::operator void *();", Style);
  verifyFormat("Foo::operator()(void *);", Style);
  verifyFormat("Foo::operator*(void *);", Style);
  verifyFormat("Foo::operator*();", Style);
  verifyFormat("operator*(int (*)(), class Foo);", Style);
  verifyFormat("Foo::operator&();", Style);
  verifyFormat("Foo::operator void &();", Style);
  verifyFormat("Foo::operator()(void &);", Style);
  verifyFormat("Foo::operator&(void &);", Style);
  verifyFormat("Foo::operator&();", Style);
  verifyFormat("operator&(int (&)(), class Foo);", Style);
  verifyFormat("Foo::operator&&();", Style);
  verifyFormat("Foo::operator void &&();", Style);
  verifyFormat("Foo::operator()(void &&);", Style);
  verifyFormat("Foo::operator&&(void &&);", Style);
  verifyFormat("Foo::operator&&();", Style);
  verifyFormat("operator&&(int(&&)(), class Foo);", Style);
  Style.PointerAlignment = FormatStyle::PAS_Left;
  verifyFormat("Foo::operator*();", Style);
  verifyFormat("Foo::operator void*();", Style);
  verifyFormat("Foo::operator()(void*);", Style);
  verifyFormat("Foo::operator*(void*);", Style);
  verifyFormat("Foo::operator*();", Style);
  verifyFormat("operator*(int (*)(), class Foo);", Style);
  verifyFormat("Foo::operator&();", Style);
  verifyFormat("Foo::operator void&();", Style);
  verifyFormat("Foo::operator()(void&);", Style);
  verifyFormat("Foo::operator&(void&);", Style);
  verifyFormat("Foo::operator&();", Style);
  verifyFormat("operator&(int (&)(), class Foo);", Style);
  verifyFormat("Foo::operator&&();", Style);
  verifyFormat("Foo::operator void&&();", Style);
  verifyFormat("Foo::operator()(void&&);", Style);
  verifyFormat("Foo::operator&&(void&&);", Style);
  verifyFormat("Foo::operator&&();", Style);
  verifyFormat("operator&&(int(&&)(), class Foo);", Style);
}
} // namespace
} // namespace format
} // namespace clang