Skip to content

[clang-format] Handle AttributeMacro before access modifiers #95634

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 5 commits into from
Jun 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,8 @@ class AnnotatingParser {
Tok->setType(TT_CtorInitializerColon);
} else {
Tok->setType(TT_InheritanceColon);
if (Prev->isOneOf(tok::kw_public, tok::kw_private, tok::kw_protected))
Line.Type = LT_AccessModifier;
}
} else if (canBeObjCSelectorComponent(*Tok->Previous) && Tok->Next &&
(Tok->Next->isOneOf(tok::r_paren, tok::comma) ||
Expand Down Expand Up @@ -1998,6 +2000,8 @@ class AnnotatingParser {
if (!consumeToken())
return LT_Invalid;
}
if (Line.Type == LT_AccessModifier)
return LT_AccessModifier;
if (KeywordVirtualFound)
return LT_VirtualFunctionDecl;
if (ImportStatement)
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Format/TokenAnnotator.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ namespace format {

enum LineType {
LT_Invalid,
// Contains public/private/protected followed by TT_InheritanceColon.
LT_AccessModifier,
LT_ImportStatement,
LT_ObjCDecl, // An @interface, @implementation, or @protocol line.
LT_ObjCMethodDecl,
Expand All @@ -45,7 +47,7 @@ enum ScopeType {
class AnnotatedLine {
public:
AnnotatedLine(const UnwrappedLine &Line)
: First(Line.Tokens.front().Tok), Level(Line.Level),
: First(Line.Tokens.front().Tok), Type(LT_Other), Level(Line.Level),
PPLevel(Line.PPLevel),
MatchingOpeningBlockLineIndex(Line.MatchingOpeningBlockLineIndex),
MatchingClosingBlockLineIndex(Line.MatchingClosingBlockLineIndex),
Expand Down
35 changes: 17 additions & 18 deletions clang/lib/Format/UnwrappedLineFormatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class LevelIndentTracker {
/// Update the indent state given that \p Line is going to be formatted
/// next.
void nextLine(const AnnotatedLine &Line) {
Offset = getIndentOffset(*Line.First);
Offset = getIndentOffset(Line);
// Update the indent level cache size so that we can rely on it
// having the right size in adjustToUnmodifiedline.
if (Line.Level >= IndentForLevel.size())
Expand Down Expand Up @@ -111,42 +111,41 @@ class LevelIndentTracker {
///
/// For example, 'public:' labels in classes are offset by 1 or 2
/// characters to the left from their level.
int getIndentOffset(const FormatToken &RootToken) {
int getIndentOffset(const AnnotatedLine &Line) {
if (Style.Language == FormatStyle::LK_Java || Style.isJavaScript() ||
Style.isCSharp()) {
return 0;
}

auto IsAccessModifier = [this, &RootToken]() {
if (RootToken.isAccessSpecifier(Style.isCpp())) {
auto IsAccessModifier = [&](const FormatToken &RootToken) {
if (Line.Type == LT_AccessModifier || RootToken.isObjCAccessSpecifier())
return true;
} else if (RootToken.isObjCAccessSpecifier()) {
return true;
}

const auto *Next = RootToken.Next;

// Handle Qt signals.
else if (RootToken.isOneOf(Keywords.kw_signals, Keywords.kw_qsignals) &&
RootToken.Next && RootToken.Next->is(tok::colon)) {
return true;
} else if (RootToken.Next &&
RootToken.Next->isOneOf(Keywords.kw_slots,
Keywords.kw_qslots) &&
RootToken.Next->Next && RootToken.Next->Next->is(tok::colon)) {
if (RootToken.isOneOf(Keywords.kw_signals, Keywords.kw_qsignals) &&
Next && Next->is(tok::colon)) {
return true;
}
// Handle malformed access specifier e.g. 'private' without trailing ':'.
else if (!RootToken.Next && RootToken.isAccessSpecifier(false)) {

if (Next && Next->isOneOf(Keywords.kw_slots, Keywords.kw_qslots) &&
Next->Next && Next->Next->is(tok::colon)) {
return true;
}
return false;

// Handle malformed access specifier e.g. 'private' without trailing ':'.
return !Next && RootToken.isAccessSpecifier(false);
};

if (IsAccessModifier()) {
if (IsAccessModifier(*Line.First)) {
// The AccessModifierOffset may be overridden by IndentAccessModifiers,
// in which case we take a negative value of the IndentWidth to simulate
// the upper indent level.
return Style.IndentAccessModifiers ? -Style.IndentWidth
: Style.AccessModifierOffset;
}

return 0;
}

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,8 @@ bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace,
do {
if (FormatTok->isAttribute()) {
nextToken();
if (FormatTok->is(tok::l_paren))
parseParens();
continue;
}
tok::TokenKind Kind = FormatTok->Tok.getKind();
Expand Down
15 changes: 15 additions & 0 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12912,6 +12912,15 @@ TEST_F(FormatTest, FormatsAccessModifiers) {
" int j;\n"
"};",
Style);
Style.AttributeMacros.push_back("FOO");
Style.AttributeMacros.push_back("BAR");
verifyFormat("struct foo {\n"
"FOO private:\n"
" int i;\n"
"BAR(x) protected:\n"
" int j;\n"
"};",
Style);

FormatStyle NoEmptyLines = getLLVMStyle();
NoEmptyLines.MaxEmptyLinesToKeep = 0;
Expand Down Expand Up @@ -26130,6 +26139,12 @@ TEST_F(FormatTest, IndentAccessModifiers) {
" int i;\n"
"};",
Style);
Style.AttributeMacros.push_back("FOO");
verifyFormat("class C {\n"
" FOO public:\n"
" int i;\n"
"};",
Style);
}

TEST_F(FormatTest, LimitlessStringsAndComments) {
Expand Down
Loading