Skip to content

Commit

Permalink
[clang-reorder-fields] Move trailing comments. (llvm#122918)
Browse files Browse the repository at this point in the history
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.
};
```
  • Loading branch information
legrosbuffle authored Jan 15, 2025
1 parent c8bbbaa commit 6affc18
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 8 deletions.
69 changes: 61 additions & 8 deletions clang-tools-extra/clang-reorder-fields/ReorderFieldsAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
23 changes: 23 additions & 0 deletions clang-tools-extra/test/clang-reorder-fields/Comments.cpp
Original file line number Diff line number Diff line change
@@ -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.

0 comments on commit 6affc18

Please sign in to comment.