Skip to content

Commit 51aa182

Browse files
committed
more vibe coding with human intervention
on-behalf-of: @amd <[email protected]>
1 parent 33af58b commit 51aa182

File tree

1 file changed

+99
-57
lines changed

1 file changed

+99
-57
lines changed

clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp

Lines changed: 99 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -20,30 +20,47 @@ AST_MATCHER(LambdaExpr, hasDefaultCapture) {
2020
return Node.getCaptureDefault() != LCD_None;
2121
}
2222

23-
std::string getExplicitCaptureText(const LambdaCapture &Capture,
23+
// Find the source range of the default capture token (= or &)
24+
SourceRange getDefaultCaptureRange(const LambdaExpr *Lambda,
2425
const SourceManager &SM,
2526
const LangOptions &LangOpts) {
26-
if (!Capture.isExplicit() || !Capture.getLocation().isValid()) {
27-
return "";
28-
}
27+
SourceLocation DefaultLoc = Lambda->getCaptureDefaultLoc();
28+
if (DefaultLoc.isInvalid())
29+
return SourceRange();
30+
31+
// The default capture is a single token
32+
SourceLocation EndLoc =
33+
Lexer::getLocForEndOfToken(DefaultLoc, 0, SM, LangOpts);
34+
return SourceRange(DefaultLoc, EndLoc);
35+
}
2936

30-
// For explicit captures, extract the actual source text to preserve
31-
// original formatting, spacing, and comments
32-
SourceLocation CaptureBegin = Capture.getLocation();
37+
// Find where to insert implicit captures
38+
SourceLocation getImplicitCaptureInsertionLoc(const LambdaExpr *Lambda,
39+
const SourceManager &SM,
40+
const LangOptions &LangOpts) {
41+
// If there are explicit captures, insert after the last one
42+
if (Lambda->explicit_capture_begin() != Lambda->explicit_capture_end()) {
43+
// Find the location after the last explicit capture
44+
const auto *LastExplicit = Lambda->explicit_capture_end() - 1;
45+
SourceLocation LastLoc = LastExplicit->getLocation();
46+
if (LastLoc.isValid()) {
47+
return Lexer::getLocForEndOfToken(LastLoc, 0, SM, LangOpts);
48+
}
49+
}
3350

34-
// Find the end of this capture by looking for the next comma or closing
35-
// bracket This is a simplified approach - a more robust implementation would
36-
// parse tokens
37-
SourceLocation CaptureEnd =
38-
Lexer::getLocForEndOfToken(CaptureBegin, 0, SM, LangOpts);
51+
// If no explicit captures, insert after the default capture
52+
SourceLocation DefaultLoc = Lambda->getCaptureDefaultLoc();
53+
if (DefaultLoc.isValid()) {
54+
return Lexer::getLocForEndOfToken(DefaultLoc, 0, SM, LangOpts);
55+
}
3956

40-
// For now, we'll still reconstruct to ensure correctness
41-
// but this framework allows for future enhancement to preserve exact source
42-
// text
43-
return "";
57+
// Fallback: insert at the beginning of the capture list
58+
return Lambda->getIntroducerRange().getBegin().getLocWithOffset(1);
4459
}
4560

46-
std::string getCaptureString(const LambdaCapture &Capture) {
61+
// Generate the text for an implicit capture
62+
std::optional<std::string>
63+
generateImplicitCaptureText(const LambdaCapture &Capture) {
4764
if (Capture.capturesThis()) {
4865
return Capture.getCaptureKind() == LCK_StarThis ? "*this" : "this";
4966
}
@@ -57,38 +74,15 @@ std::string getCaptureString(const LambdaCapture &Capture) {
5774
return Result;
5875
}
5976

60-
// Handle VLA captures - these are rare but possible
61-
return "/* VLA capture */";
62-
}
63-
64-
std::string buildExplicitCaptureList(const LambdaExpr *Lambda,
65-
const SourceManager &SM,
66-
const LangOptions &LangOpts) {
67-
std::vector<std::string> CaptureStrings;
68-
69-
// Add explicit captures first (preserve their order and syntax)
70-
for (const auto &Capture : Lambda->explicit_captures()) {
71-
// Try to get the original source text first
72-
std::string ExplicitText = getExplicitCaptureText(Capture, SM, LangOpts);
73-
if (!ExplicitText.empty()) {
74-
CaptureStrings.push_back(ExplicitText);
75-
} else {
76-
// Fall back to reconstructed text
77-
CaptureStrings.push_back(getCaptureString(Capture));
78-
}
79-
}
80-
81-
// Add implicit captures (convert to explicit syntax)
82-
for (const auto &Capture : Lambda->implicit_captures()) {
83-
CaptureStrings.push_back(getCaptureString(Capture));
84-
}
85-
86-
return "[" + llvm::join(CaptureStrings, ", ") + "]";
77+
// TODO: handle VLAs and other weird captures
78+
return std::nullopt;
8779
}
8880

89-
SourceRange getCaptureListRange(const LambdaExpr *Lambda) {
90-
SourceRange IntroducerRange = Lambda->getIntroducerRange();
91-
return IntroducerRange;
81+
// Check if we need a comma before inserting captures
82+
bool needsCommaBefore(const LambdaExpr *Lambda, SourceLocation InsertLoc,
83+
const SourceManager &SM, const LangOptions &LangOpts) {
84+
// If there are explicit captures, we need a comma
85+
return Lambda->explicit_capture_begin() != Lambda->explicit_capture_end();
9286
}
9387

9488
} // namespace
@@ -102,24 +96,72 @@ void AvoidDefaultLambdaCaptureCheck::check(
10296
const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
10397
assert(Lambda);
10498

99+
const SourceManager &SM = *Result.SourceManager;
100+
const LangOptions &LangOpts = Result.Context->getLangOpts();
101+
105102
const SourceLocation DefaultCaptureLoc = Lambda->getCaptureDefaultLoc();
106103
if (DefaultCaptureLoc.isInvalid())
107104
return;
108105

109-
// Build the replacement capture list
110-
std::string NewCaptureList = buildExplicitCaptureList(
111-
Lambda, *Result.SourceManager, Result.Context->getLangOpts());
112-
113-
// Get the range of the entire capture list [...]
114-
SourceRange CaptureListRange = getCaptureListRange(Lambda);
115-
116106
auto Diag = diag(DefaultCaptureLoc,
117107
"lambda default captures are discouraged; "
118108
"prefer to capture specific variables explicitly");
119109

120-
// Only provide fixit if we can determine a valid replacement
121-
if (CaptureListRange.isValid() && !NewCaptureList.empty()) {
122-
Diag << FixItHint::CreateReplacement(CaptureListRange, NewCaptureList);
110+
// Get the range of the default capture token to remove
111+
SourceRange DefaultRange = getDefaultCaptureRange(Lambda, SM, LangOpts);
112+
if (!DefaultRange.isValid())
113+
return;
114+
115+
// Collect all implicit captures that need to be made explicit
116+
std::vector<std::string> ImplicitCaptureTexts;
117+
for (const auto &Capture : Lambda->implicit_captures()) {
118+
if (const auto CaptureText = generateImplicitCaptureText(Capture)) {
119+
ImplicitCaptureTexts.push_back(CaptureText.value());
120+
}
121+
}
122+
123+
// If there are no implicit captures, just remove the default capture
124+
if (ImplicitCaptureTexts.empty()) {
125+
// Also remove any trailing comma if it exists
126+
SourceLocation AfterDefault = DefaultRange.getEnd();
127+
SourceLocation CommaLoc = Lexer::findLocationAfterToken(
128+
AfterDefault, tok::comma, SM, LangOpts, false);
129+
130+
if (CommaLoc.isValid()) {
131+
// Remove default capture and the comma
132+
SourceRange RemovalRange(DefaultRange.getBegin(), CommaLoc);
133+
Diag << FixItHint::CreateRemoval(RemovalRange);
134+
} else {
135+
// Just remove the default capture
136+
Diag << FixItHint::CreateRemoval(DefaultRange);
137+
}
138+
return;
139+
}
140+
141+
// Find where to insert the implicit captures
142+
SourceLocation InsertLoc =
143+
getImplicitCaptureInsertionLoc(Lambda, SM, LangOpts);
144+
if (!InsertLoc.isValid())
145+
return;
146+
147+
// Apply the transformations:
148+
// 1. Remove the default capture
149+
Diag << FixItHint::CreateRemoval(DefaultRange);
150+
151+
// 2. Insert the explicit captures if any
152+
if (!ImplicitCaptureTexts.empty()) {
153+
// Build the insertion text for implicit captures
154+
std::string InsertionText;
155+
bool NeedsComma = needsCommaBefore(Lambda, InsertLoc, SM, LangOpts);
156+
157+
for (size_t I = 0; I < ImplicitCaptureTexts.size(); ++I) {
158+
if (NeedsComma || I > 0) {
159+
InsertionText += ", ";
160+
}
161+
InsertionText += ImplicitCaptureTexts[I];
162+
}
163+
164+
Diag << FixItHint::CreateInsertion(InsertLoc, InsertionText);
123165
}
124166
}
125167

0 commit comments

Comments
 (0)