Skip to content

Commit 6affc18

Browse files
authored
[clang-reorder-fields] Move trailing comments. (#122918)
Currently, trailing comments get mixed up: ``` struct Foo { int a; // This one is the cool field // within the struct. int b; }; ``` becomes: ``` struct Foo { int b; // This one is the cool field // within the struct. int a; }; ``` This should be: ``` struct Foo { int b; int a; // This one is the cool field // within the struct. }; ```
1 parent c8bbbaa commit 6affc18

File tree

2 files changed

+84
-8
lines changed

2 files changed

+84
-8
lines changed

clang-tools-extra/clang-reorder-fields/ReorderFieldsAction.cpp

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ getNewFieldsOrder(const RecordDecl *Definition,
6363
NameToIndex[Field->getName()] = Field->getFieldIndex();
6464

6565
if (DesiredFieldsOrder.size() != NameToIndex.size()) {
66-
llvm::errs() << "Number of provided fields doesn't match definition.\n";
66+
llvm::errs() << "Number of provided fields (" << DesiredFieldsOrder.size()
67+
<< ") doesn't match definition (" << NameToIndex.size()
68+
<< ").\n";
6769
return {};
6870
}
6971
SmallVector<unsigned, 4> NewFieldsOrder;
@@ -116,26 +118,77 @@ findMembersUsedInInitExpr(const CXXCtorInitializer *Initializer,
116118
return Results;
117119
}
118120

119-
/// Returns the full source range for the field declaration up to (not
120-
/// including) the trailing semicolumn, including potential macro invocations,
121-
/// e.g. `int a GUARDED_BY(mu);`.
121+
/// Returns the next token after `Loc` (including comment tokens).
122+
static std::optional<Token> getTokenAfter(SourceLocation Loc,
123+
const SourceManager &SM,
124+
const LangOptions &LangOpts) {
125+
if (Loc.isMacroID()) {
126+
return std::nullopt;
127+
}
128+
Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);
129+
130+
// Break down the source location.
131+
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
132+
133+
// Try to load the file buffer.
134+
bool InvalidTemp = false;
135+
StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
136+
if (InvalidTemp)
137+
return std::nullopt;
138+
139+
const char *TokenBegin = File.data() + LocInfo.second;
140+
141+
Lexer lexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(),
142+
TokenBegin, File.end());
143+
lexer.SetCommentRetentionState(true);
144+
// Find the token.
145+
Token Tok;
146+
lexer.LexFromRawLexer(Tok);
147+
return Tok;
148+
}
149+
150+
/// Returns the end of the trailing comments after `Loc`.
151+
static SourceLocation getEndOfTrailingComment(SourceLocation Loc,
152+
const SourceManager &SM,
153+
const LangOptions &LangOpts) {
154+
// We consider any following comment token that is indented more than the
155+
// first comment to be part of the trailing comment.
156+
const unsigned Column = SM.getPresumedColumnNumber(Loc);
157+
std::optional<Token> Tok = getTokenAfter(Loc, SM, LangOpts);
158+
while (Tok && Tok->is(tok::comment) &&
159+
SM.getPresumedColumnNumber(Tok->getLocation()) > Column) {
160+
Loc = Tok->getEndLoc();
161+
Tok = getTokenAfter(Loc, SM, LangOpts);
162+
}
163+
return Loc;
164+
}
165+
166+
/// Returns the full source range for the field declaration up to (including)
167+
/// the trailing semicolumn, including potential macro invocations,
168+
/// e.g. `int a GUARDED_BY(mu);`. If there is a trailing comment, include it.
122169
static SourceRange getFullFieldSourceRange(const FieldDecl &Field,
123170
const ASTContext &Context) {
124-
SourceRange Range = Field.getSourceRange();
171+
const SourceRange Range = Field.getSourceRange();
172+
SourceLocation Begin = Range.getBegin();
125173
SourceLocation End = Range.getEnd();
126174
const SourceManager &SM = Context.getSourceManager();
127175
const LangOptions &LangOpts = Context.getLangOpts();
128176
while (true) {
129177
std::optional<Token> CurrentToken = Lexer::findNextToken(End, SM, LangOpts);
130178

131-
if (!CurrentToken || CurrentToken->is(tok::semi))
132-
break;
179+
if (!CurrentToken)
180+
return SourceRange(Begin, End);
133181

134182
if (CurrentToken->is(tok::eof))
135183
return Range; // Something is wrong, return the original range.
184+
136185
End = CurrentToken->getLastLoc();
186+
187+
if (CurrentToken->is(tok::semi))
188+
break;
137189
}
138-
return SourceRange(Range.getBegin(), End);
190+
End = getEndOfTrailingComment(End, SM, LangOpts);
191+
return SourceRange(Begin, End);
139192
}
140193

141194
/// Reorders fields in the definition of a struct/class.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: clang-reorder-fields -record-name Foo -fields-order e1,e3,e2,a,c,b %s -- | FileCheck %s
2+
3+
class Foo {
4+
int a; // Trailing comment for a.
5+
int b; // Multiline
6+
// trailing for b.
7+
// Prefix comments for c.
8+
int c;
9+
10+
/*c-like*/ int e1;
11+
int /*c-like*/ e2;
12+
int e3 /*c-like*/;
13+
};
14+
15+
// CHECK: /*c-like*/ int e1;
16+
// CHECK-NEXT: int e3 /*c-like*/;
17+
// CHECK-NEXT: int /*c-like*/ e2;
18+
// CHECK-NEXT: int a; // Trailing comment for a.
19+
// CHECK-NEXT: // Prefix comments for c.
20+
// CHECK-NEXT: int c;
21+
// CHECK-NEXT: int b; // Multiline
22+
// CHECK-NEXT: // trailing for b.
23+

0 commit comments

Comments
 (0)