[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.
};
```
This commit is contained in:
Clement Courbet 2025-01-15 13:07:42 +01:00 committed by GitHub
parent c8bbbaa5c7
commit 6affc18375
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 84 additions and 8 deletions

View File

@ -63,7 +63,9 @@ getNewFieldsOrder(const RecordDecl *Definition,
NameToIndex[Field->getName()] = Field->getFieldIndex();
if (DesiredFieldsOrder.size() != NameToIndex.size()) {
llvm::errs() << "Number of provided fields doesn't match definition.\n";
llvm::errs() << "Number of provided fields (" << DesiredFieldsOrder.size()
<< ") doesn't match definition (" << NameToIndex.size()
<< ").\n";
return {};
}
SmallVector<unsigned, 4> NewFieldsOrder;
@ -116,26 +118,77 @@ findMembersUsedInInitExpr(const CXXCtorInitializer *Initializer,
return Results;
}
/// Returns the full source range for the field declaration up to (not
/// including) the trailing semicolumn, including potential macro invocations,
/// e.g. `int a GUARDED_BY(mu);`.
/// Returns the next token after `Loc` (including comment tokens).
static std::optional<Token> getTokenAfter(SourceLocation Loc,
const SourceManager &SM,
const LangOptions &LangOpts) {
if (Loc.isMacroID()) {
return std::nullopt;
}
Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);
// Break down the source location.
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
// Try to load the file buffer.
bool InvalidTemp = false;
StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
if (InvalidTemp)
return std::nullopt;
const char *TokenBegin = File.data() + LocInfo.second;
Lexer lexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(),
TokenBegin, File.end());
lexer.SetCommentRetentionState(true);
// Find the token.
Token Tok;
lexer.LexFromRawLexer(Tok);
return Tok;
}
/// Returns the end of the trailing comments after `Loc`.
static SourceLocation getEndOfTrailingComment(SourceLocation Loc,
const SourceManager &SM,
const LangOptions &LangOpts) {
// We consider any following comment token that is indented more than the
// first comment to be part of the trailing comment.
const unsigned Column = SM.getPresumedColumnNumber(Loc);
std::optional<Token> Tok = getTokenAfter(Loc, SM, LangOpts);
while (Tok && Tok->is(tok::comment) &&
SM.getPresumedColumnNumber(Tok->getLocation()) > Column) {
Loc = Tok->getEndLoc();
Tok = getTokenAfter(Loc, SM, LangOpts);
}
return Loc;
}
/// Returns the full source range for the field declaration up to (including)
/// the trailing semicolumn, including potential macro invocations,
/// e.g. `int a GUARDED_BY(mu);`. If there is a trailing comment, include it.
static SourceRange getFullFieldSourceRange(const FieldDecl &Field,
const ASTContext &Context) {
SourceRange Range = Field.getSourceRange();
const SourceRange Range = Field.getSourceRange();
SourceLocation Begin = Range.getBegin();
SourceLocation End = Range.getEnd();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
while (true) {
std::optional<Token> CurrentToken = Lexer::findNextToken(End, SM, LangOpts);
if (!CurrentToken || CurrentToken->is(tok::semi))
break;
if (!CurrentToken)
return SourceRange(Begin, End);
if (CurrentToken->is(tok::eof))
return Range; // Something is wrong, return the original range.
End = CurrentToken->getLastLoc();
if (CurrentToken->is(tok::semi))
break;
}
return SourceRange(Range.getBegin(), End);
End = getEndOfTrailingComment(End, SM, LangOpts);
return SourceRange(Begin, End);
}
/// Reorders fields in the definition of a struct/class.

View File

@ -0,0 +1,23 @@
// RUN: clang-reorder-fields -record-name Foo -fields-order e1,e3,e2,a,c,b %s -- | FileCheck %s
class Foo {
int a; // Trailing comment for a.
int b; // Multiline
// trailing for b.
// Prefix comments for c.
int c;
/*c-like*/ int e1;
int /*c-like*/ e2;
int e3 /*c-like*/;
};
// CHECK: /*c-like*/ int e1;
// CHECK-NEXT: int e3 /*c-like*/;
// CHECK-NEXT: int /*c-like*/ e2;
// CHECK-NEXT: int a; // Trailing comment for a.
// CHECK-NEXT: // Prefix comments for c.
// CHECK-NEXT: int c;
// CHECK-NEXT: int b; // Multiline
// CHECK-NEXT: // trailing for b.