Skip to content

Commit c4069e0

Browse files
MC-19366: Fixes that enum values with directives would be parsed as expected and would thus lead to false positives
1 parent af9c041 commit c4069e0

6 files changed

+67
-27
lines changed

Magento2/Sniffs/GraphQL/AbstractGraphQLSniff.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,30 @@ protected function isSnakeCase($name, $upperCase = false)
4343
return preg_match($pattern, $name);
4444
}
4545

46+
/**
47+
* Returns the pointer to the last token of a directive if the token at <var>$startPointer</var> starts a directive.
48+
*
49+
* @param array $tokens
50+
* @param int $startPointer
51+
* @return int The end of the directive if one is found, the start pointer otherwise
52+
*/
53+
protected function seekEndOfDirective(array $tokens, $startPointer)
54+
{
55+
$endPointer = $startPointer;
56+
57+
if ($tokens[$startPointer]['code'] === T_DOC_COMMENT_TAG) {
58+
//advance to next token
59+
$endPointer += 1;
60+
61+
//if next token is an opening parenthesis, we consume everything up to the closing parenthesis
62+
if ($tokens[$endPointer + 1]['code'] === T_OPEN_PARENTHESIS) {
63+
$endPointer = $tokens[$endPointer + 1]['parenthesis_closer'];
64+
}
65+
}
66+
67+
return $endPointer;
68+
}
69+
4670
/**
4771
* Searches for the first token that has <var>$tokenCode</var> in <var>$tokens</var> from position
4872
* <var>$startPointer</var> (excluded).

Magento2/Sniffs/GraphQL/ValidArgumentNameSniff.php

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,7 @@ private function getArgumentDefinitionEndPointer($argumentDefinitionStartPointer
108108

109109
//while next token starts a directive, we advance to the end of the directive
110110
while ($tokens[$endPointer + 1]['code'] === T_DOC_COMMENT_TAG) {
111-
//consume next two tokens
112-
$endPointer += 2;
113-
114-
//if next token is an opening parenthesis, we consume everything up to the closing parenthesis
115-
if ($tokens[$endPointer + 1]['code'] === T_OPEN_PARENTHESIS) {
116-
$endPointer = $tokens[$endPointer + 1]['parenthesis_closer'];
117-
}
111+
$endPointer = $this->seekEndOfDirective($tokens, $endPointer + 1);
118112
}
119113

120114
return $endPointer;
@@ -181,7 +175,7 @@ private function getArguments($startPointer, $endPointer, array $tokens)
181175

182176
switch (true) {
183177
case in_array($tokenCode, $skipTypes):
184-
//NOP This is a toke that we have to skip
178+
//NOP This is a token that we have to skip
185179
break;
186180
case $tokenCode === T_COLON:
187181
//we have reached the end of the argument name, thus we store its pointer and value

Magento2/Sniffs/GraphQL/ValidEnumValueSniff.php

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Copyright © Magento. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
67
namespace Magento2\Sniffs\GraphQL;
78

89
use PHP_CodeSniffer\Files\File;
@@ -46,16 +47,14 @@ public function process(File $phpcsFile, $stackPtr)
4647

4748
$values = $this->getValues($openingCurlyPointer, $closingCurlyPointer, $tokens, $phpcsFile->eolChar);
4849

49-
foreach ($values as $value) {
50-
$pointer = $value[0];
51-
$name = $value[1];
50+
foreach ($values as $pointer => $value) {
5251

53-
if (!$this->isSnakeCase($name, true)) {
52+
if (!$this->isSnakeCase($value, true)) {
5453
$type = 'Enum value';
5554
$error = '%s "%s" is not in SCREAMING_SNAKE_CASE format';
5655
$data = [
5756
$type,
58-
$name,
57+
$value,
5958
];
6059

6160
$phpcsFile->addError($error, $pointer, 'NotScreamingSnakeCase', $data);
@@ -98,11 +97,13 @@ private function getOpeningCurlyBracketPointer($startPointer, array $tokens)
9897
* Finds all enum values contained in <var>$tokens</var> in range <var>$startPointer</var> to
9998
* <var>$endPointer</var>.
10099
*
100+
* The returned array uses token pointers as keys and value names as values.
101+
*
101102
* @param int $startPointer
102103
* @param int $endPointer
103104
* @param array $tokens
104105
* @param string $eolChar
105-
* @return array[]
106+
* @return array<int,string>
106107
*/
107108
private function getValues($startPointer, $endPointer, array $tokens, $eolChar)
108109
{
@@ -112,20 +113,31 @@ private function getValues($startPointer, $endPointer, array $tokens, $eolChar)
112113
$skipTypes = [T_COMMENT, T_WHITESPACE];
113114

114115
for ($i = $startPointer + 1; $i < $endPointer; ++$i) {
115-
//skip some tokens
116116
if (in_array($tokens[$i]['code'], $skipTypes)) {
117+
//NOP This is a token that we have to skip
117118
continue;
118119
}
119-
$enumValue .= $tokens[$i]['content'];
120120

121-
if ($valueTokenPointer === null) {
122-
$valueTokenPointer = $i;
121+
//add current tokens content to enum value if we have a string
122+
if ($tokens[$i]['code'] === T_STRING) {
123+
$enumValue .= $tokens[$i]['content'];
124+
125+
//and store the pointer if we have not done it already
126+
if ($valueTokenPointer === null) {
127+
$valueTokenPointer = $i;
128+
}
129+
}
130+
131+
//consume directive if we have found one
132+
if ($tokens[$i]['code'] === T_DOC_COMMENT_TAG) {
133+
$i = $this->seekEndOfDirective($tokens, $i);
123134
}
124135

125-
if (strpos($enumValue, $eolChar) !== false) {
126-
$values[] = [$valueTokenPointer, rtrim($enumValue, $eolChar)];
127-
$enumValue = '';
128-
$valueTokenPointer = null;
136+
//if current token has a line break, we have found the end of the value definition
137+
if (strpos($tokens[$i]['content'], $eolChar) !== false) {
138+
$values[$valueTokenPointer] = trim($enumValue);
139+
$enumValue = '';
140+
$valueTokenPointer = null;
129141
}
130142
}
131143

Magento2/Tests/GraphQL/ValidArgumentNameUnitTest.graphqls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type Foo @doc(descripton: "Foo Bar Baz") {
4040
validArgument: String = "My fair lady" @doc(
4141
# A comment
4242
description: "This is a valid argument, spanned over multiple lines."
43-
) @foo
43+
) @unparametrizedDirective
4444
validArgumentWithDefaultValue: Int = 20 @doc(description: "This is another valid argument with a default value.")
4545
validArgumentListType: [String] @doc(description: "This is a valid argument that uses a list type.")
4646
validArgumentNonNullType: String! @doc(description: "This is a valid argument that uses a non null type.")

Magento2/Tests/GraphQL/ValidEnumValueUnitTest.graphqls

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,20 @@ enum Foo {
55
VALID_SCREAMING_SNAKE_CASE_VALUE_WITH_12345_NUMBERS
66
VALID_SCREAMING_SNAKE_CASE_VALUE_ENDING_WITH_NUMBER_5
77
VALIDUPPERCASEVALUE
8+
VALID_SCREMING_CASE_VALUE_WITH_DIRECTIVE @doc(description: "This is a valid enum value with a directive")
9+
VALID_SCREMING_CASE_VALUE_WITH_TWO_DIRECTIVES @doc(
10+
description: "This is a valid enum value with a directive"
11+
) @unparametrizedDirective
812

913
# Invalid values
1014
1_INVALID_SCREAMING_SNAKE_CASE_VALUE_STARTING_WITH_NUMBER
1115
invalidCamelCaseValue
1216
InvalidCamelCaseCapsValue
1317
invalidlowercasevalue
18+
invalidCamelCaseValueWithDirective @doc(description: "This is an invalid enum value with a directive")
19+
invalidCamelCaseValueWithTwoDirectives @doc(
20+
description: "This is an invalid enum value with a directive"
21+
) @unparametrizedDirective
1422
}
1523

1624
# Ignored although it triggers a T_CLASS token

Magento2/Tests/GraphQL/ValidEnumValueUnitTest.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ class ValidEnumValueUnitTest extends AbstractGraphQLSniffUnitTestCase
1616
protected function getErrorList()
1717
{
1818
return [
19-
10 => 1,
20-
11 => 1,
21-
12 => 1,
22-
13 => 1,
19+
14 => 1,
20+
15 => 1,
21+
16 => 1,
22+
17 => 1,
23+
18 => 1,
24+
19 => 1,
2325
];
2426
}
2527

0 commit comments

Comments
 (0)