Skip to content

Commit 1f02472

Browse files
committed
refactor: simplify AAA detection and add helpers
1 parent bd03502 commit 1f02472

File tree

1 file changed

+44
-85
lines changed

1 file changed

+44
-85
lines changed

src/EnforceAaaPatternRector.php

Lines changed: 44 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
1717
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
1818

19+
use function in_array;
20+
1921
final class EnforceAaaPatternRector extends AbstractRector
2022
{
2123
/**
@@ -65,41 +67,29 @@ public function getNodeTypes(): array
6567
private function isTestMethod(ClassMethod $classMethod): bool
6668
{
6769
$name = $this->getName(node: $classMethod);
68-
if ($name !== null && str_starts_with(haystack: $name, needle: 'test')) {
70+
if (str_starts_with(haystack: $name, needle: 'test')) {
6971
return true;
7072
}
7173

7274
$docComment = $classMethod->getDocComment();
73-
if ($docComment !== null && str_contains(haystack: strtolower(string: $docComment->getText()), needle: '@test')) {
74-
return true;
75-
}
7675

77-
return false;
76+
return $docComment !== null && str_contains(haystack: strtolower(string: $docComment->getText()), needle: '@test');
7877
}
7978

80-
private function removeAaaComments(Node $stmt): void
79+
private function isAaaComment(Comment $comment): bool
8180
{
82-
$comments = $stmt->getComments();
83-
$filteredComments = [];
84-
85-
foreach ($comments as $comment) {
86-
$text = trim(string: $comment->getText());
87-
$normalizedText = strtolower(string: $text);
88-
89-
// Only remove comments that are specifically AAA pattern comments
90-
// Check if the comment is ONLY an AAA comment (possibly with // or /* */ wrapper)
91-
$isAaaComment = false;
81+
$text = trim(string: $comment->getText());
82+
$coreText = trim(string: (string) preg_replace(pattern: '/^\/\/\s*|^\/\*\s*|\s*\*\/$/', replacement: '', subject: $text));
83+
$coreTextLower = strtolower(string: $coreText);
9284

93-
// Remove comment markers and whitespace to get core content
94-
$coreText = trim(string: (string) preg_replace(pattern: '/^\/\/\s*|^\/\*\s*|\s*\*\/$/', replacement: '', subject: $text));
95-
$coreTextLower = strtolower(string: $coreText);
96-
97-
// Check if the core text is exactly one of the AAA keywords
98-
if ($coreTextLower === 'arrange' || $coreTextLower === 'act' || $coreTextLower === 'assert') {
99-
$isAaaComment = true;
100-
}
85+
return in_array($coreTextLower, ['arrange', 'act', 'assert'], true);
86+
}
10187

102-
if (!$isAaaComment) {
88+
private function removeAaaComments(Node $stmt): void
89+
{
90+
$filteredComments = [];
91+
foreach ($stmt->getComments() as $comment) {
92+
if (! $this->isAaaComment(comment: $comment)) {
10393
$filteredComments[] = $comment;
10494
}
10595
}
@@ -114,38 +104,37 @@ private function addAaaComment(Node $stmt, string $aaaComment): void
114104
$stmt->setAttribute('comments', array_merge([$aaa], $existing));
115105
}
116106

107+
private function isAssertCall(Node\Expr $expr): bool
108+
{
109+
if ($expr instanceof MethodCall
110+
&& $expr->var instanceof Node\Expr\Variable
111+
&& $this->isName(node: $expr->var, name: 'this')
112+
) {
113+
$methodName = $this->getName(node: $expr->name);
114+
115+
return $methodName !== null && str_starts_with(haystack: $methodName, needle: 'assert');
116+
}
117+
118+
if ($expr instanceof StaticCall
119+
&& $expr->class instanceof Node\Name
120+
&& $this->isName(node: $expr->class, name: 'self')
121+
) {
122+
$methodName = $this->getName(node: $expr->name);
123+
124+
return $methodName !== null && str_starts_with(haystack: $methodName, needle: 'assert');
125+
}
126+
127+
return false;
128+
}
129+
117130
/**
118131
* @param Stmt[] $stmts
119132
*/
120133
private function findFirstAssert(array $stmts): ?int
121134
{
122135
foreach ($stmts as $i => $stmt) {
123-
if (! $stmt instanceof Expression) {
124-
continue;
125-
}
126-
127-
$expr = $stmt->expr;
128-
129-
// $this->assert*
130-
if ($expr instanceof MethodCall
131-
&& $expr->var instanceof Node\Expr\Variable
132-
&& $this->isName(node: $expr->var, name: 'this')
133-
) {
134-
$methodName = $this->getName(node: $expr->name);
135-
if ($methodName !== null && str_starts_with(haystack: $methodName, needle: 'assert')) {
136-
return $i;
137-
}
138-
}
139-
140-
// self::assert*
141-
if ($expr instanceof StaticCall
142-
&& $expr->class instanceof Node\Name
143-
&& $this->isName(node: $expr->class, name: 'self')
144-
) {
145-
$methodName = $this->getName(node: $expr->name);
146-
if ($methodName !== null && str_starts_with(haystack: $methodName, needle: 'assert')) {
147-
return $i;
148-
}
136+
if ($stmt instanceof Expression && $this->isAssertCall(expr: $stmt->expr)) {
137+
return $i;
149138
}
150139
}
151140

@@ -159,7 +148,7 @@ private function findLastNonAssert(array $stmts, int $firstAssertIndex): int
159148
{
160149
// Find the last statement before the first assert that is not an assert
161150
for ($i = $firstAssertIndex - 1; $i >= 0; --$i) {
162-
if (!$this->isAssertStatement(stmt: $stmts[$i])) {
151+
if (! $this->isAssertStatement(stmt: $stmts[$i])) {
163152
return $i;
164153
}
165154
}
@@ -169,35 +158,7 @@ private function findLastNonAssert(array $stmts, int $firstAssertIndex): int
169158

170159
private function isAssertStatement(Stmt $stmt): bool
171160
{
172-
if (!$stmt instanceof Expression) {
173-
return false;
174-
}
175-
176-
$expr = $stmt->expr;
177-
178-
// $this->assert*
179-
if ($expr instanceof MethodCall
180-
&& $expr->var instanceof Node\Expr\Variable
181-
&& $this->isName(node: $expr->var, name: 'this')
182-
) {
183-
$methodName = $this->getName(node: $expr->name);
184-
if ($methodName !== null && str_starts_with(haystack: $methodName, needle: 'assert')) {
185-
return true;
186-
}
187-
}
188-
189-
// self::assert*
190-
if ($expr instanceof StaticCall
191-
&& $expr->class instanceof Node\Name
192-
&& $this->isName(node: $expr->class, name: 'self')
193-
) {
194-
$methodName = $this->getName(node: $expr->name);
195-
if ($methodName !== null && str_starts_with(haystack: $methodName, needle: 'assert')) {
196-
return true;
197-
}
198-
}
199-
200-
return false;
161+
return $stmt instanceof Expression && $this->isAssertCall(expr: $stmt->expr);
201162
}
202163

203164
public function refactor(Node $node): ?Node
@@ -227,8 +188,6 @@ public function refactor(Node $node): ?Node
227188
$this->removeAaaComments(stmt: $stmt);
228189
}
229190

230-
$changed = true;
231-
232191
// Handle simple case: only one statement before assert (treat as Act only)
233192
if ($firstAssertIndex === 1) {
234193
$this->addAaaComment(stmt: $stmts[0], aaaComment: 'Act');
@@ -241,7 +200,7 @@ public function refactor(Node $node): ?Node
241200

242201
// Last non-assert statement before first assert gets "Act"
243202
$lastActIndex = $this->findLastNonAssert(stmts: $stmts, firstAssertIndex: $firstAssertIndex);
244-
if ($lastActIndex >= 0 && $lastActIndex !== 0 && isset($stmts[$lastActIndex])) {
203+
if ($lastActIndex > 0 && isset($stmts[$lastActIndex])) {
245204
$this->addAaaComment(stmt: $stmts[$lastActIndex], aaaComment: 'Act');
246205
}
247206
}
@@ -251,6 +210,6 @@ public function refactor(Node $node): ?Node
251210
$this->addAaaComment(stmt: $stmts[$firstAssertIndex], aaaComment: 'Assert');
252211
}
253212

254-
return $changed ? $node : null;
213+
return $node;
255214
}
256215
}

0 commit comments

Comments
 (0)