diff --git a/resources/php_compat_info.json b/resources/php_compat_info.json --- a/resources/php_compat_info.json +++ b/resources/php_compat_info.json @@ -12267,15 +12267,15 @@ } }, "functions_windows" : { - "apache_child_terminate" : "", - "chroot" : "", - "getrusage" : "", - "imagecreatefromxpm" : "", - "lchgrp" : "", - "lchown" : "", - "nl_langinfo" : "", - "strptime" : "", - "sys_getloadavg" : "", + "apache_child_terminate" : false, + "chroot" : false, + "getrusage" : false, + "imagecreatefromxpm" : false, + "lchgrp" : false, + "lchown" : false, + "nl_langinfo" : false, + "strptime" : false, + "sys_getloadavg" : false, "checkdnsrr" : "5.3.0", "dns_get_record" : "5.3.0", "fnmatch" : "5.3.0", diff --git a/scripts/update_compat_info.php b/scripts/update_compat_info.php --- a/scripts/update_compat_info.php +++ b/scripts/update_compat_info.php @@ -104,15 +104,15 @@ // Grepped from PHP Manual. $output['functions_windows'] = array( - 'apache_child_terminate' => '', - 'chroot' => '', - 'getrusage' => '', - 'imagecreatefromxpm' => '', - 'lchgrp' => '', - 'lchown' => '', - 'nl_langinfo' => '', - 'strptime' => '', - 'sys_getloadavg' => '', + 'apache_child_terminate' => false, + 'chroot' => false, + 'getrusage' => false, + 'imagecreatefromxpm' => false, + 'lchgrp' => false, + 'lchown' => false, + 'nl_langinfo' => false, + 'strptime' => false, + 'sys_getloadavg' => false, 'checkdnsrr' => '5.3.0', 'dns_get_record' => '5.3.0', 'fnmatch' => '5.3.0', diff --git a/src/lint/linter/ArcanistXHPASTLinter.php b/src/lint/linter/ArcanistXHPASTLinter.php --- a/src/lint/linter/ArcanistXHPASTLinter.php +++ b/src/lint/linter/ArcanistXHPASTLinter.php @@ -33,10 +33,10 @@ const LINT_BINARY_EXPRESSION_SPACING = 27; const LINT_ARRAY_INDEX_SPACING = 28; const LINT_IMPLICIT_FALLTHROUGH = 30; - const LINT_PHP_53_FEATURES = 31; + const LINT_PHP_53_FEATURES = 31; // Deprecated const LINT_REUSED_AS_ITERATOR = 32; const LINT_COMMENT_SPACING = 34; - const LINT_PHP_54_FEATURES = 35; + const LINT_PHP_54_FEATURES = 35; // Deprecated const LINT_SLOWNESS = 36; const LINT_CLOSING_CALL_PAREN = 37; const LINT_CLOSING_DECL_PAREN = 38; @@ -46,9 +46,12 @@ const LINT_ELSEIF_USAGE = 42; const LINT_SEMICOLON_SPACING = 43; const LINT_CONCATENATION_OPERATOR = 44; + const LINT_PHP_COMPATIBILITY = 45; private $naminghook; private $switchhook; + private $version; + private $windowsVersion; public function getInfoName() { return 'XHPAST Lint'; @@ -105,6 +108,7 @@ self::LINT_ELSEIF_USAGE => 'ElseIf Usage', self::LINT_SEMICOLON_SPACING => 'Semicolon Spacing', self::LINT_CONCATENATION_OPERATOR => 'Concatenation Spacing', + self::LINT_PHP_COMPATIBILITY => 'PHP Compatibility', ); } @@ -164,6 +168,14 @@ 'Name of a concrete subclass of ArcanistXHPASTLintSwitchHook which '. 'tunes the analysis of switch() statements for this linter.'), ), + 'xhpast.php-version' => array( + 'type' => 'optional string', + 'help' => pht('PHP version to target.'), + ), + 'xhpast.php-version.windows' => array( + 'type' => 'optional string', + 'help' => pht('PHP version to target on Windows.') + ), ); } @@ -175,6 +187,12 @@ case 'xhpast.switchhook': $this->switchhook = $value; return; + case 'xhpast.php-version': + $this->version = $value; + return; + case 'xhpast.php-version.windows': + $this->windowsVersion = $value; + return; } return parent::setLinterConfigurationValue($key, $value); @@ -206,8 +224,6 @@ $method_codes = array( 'lintStrstrUsedForCheck' => self::LINT_SLOWNESS, 'lintStrposUsedForStart' => self::LINT_SLOWNESS, - 'lintPHP53Features' => self::LINT_PHP_53_FEATURES, - 'lintPHP54Features' => self::LINT_PHP_54_FEATURES, 'lintImplicitFallthrough' => self::LINT_IMPLICIT_FALLTHROUGH, 'lintBraceFormatting' => self::LINT_BRACE_FORMATTING, 'lintTautologicalExpressions' => self::LINT_TAUTOLOGICAL_EXPRESSION, @@ -251,6 +267,7 @@ 'lintSemicolons' => self::LINT_SEMICOLON_SPACING, 'lintSpaceAroundConcatenationOperators' => self::LINT_CONCATENATION_OPERATOR, + 'lintPHPCompatibility' => self::LINT_PHP_COMPATIBILITY, ); foreach ($method_codes as $method => $codes) { @@ -261,10 +278,9 @@ } } } - } - public function lintStrstrUsedForCheck(XHPASTNode $root) { + private function lintStrstrUsedForCheck(XHPASTNode $root) { $expressions = $root->selectDescendantsOfType('n_BINARY_EXPRESSION'); foreach ($expressions as $expression) { $operator = $expression->getChildOfType(1, 'n_OPERATOR'); @@ -306,7 +322,7 @@ } } - public function lintStrposUsedForStart(XHPASTNode $root) { + private function lintStrposUsedForStart(XHPASTNode $root) { $expressions = $root->selectDescendantsOfType('n_BINARY_EXPRESSION'); foreach ($expressions as $expression) { $operator = $expression->getChildOfType(1, 'n_OPERATOR'); @@ -349,11 +365,126 @@ } } - public function lintPHP53Features(XHPASTNode $root) { + private function lintPHPCompatibility(XHPASTNode $root) { + $php53 = self::LINT_PHP_53_FEATURES; + $php54 = self::LINT_PHP_54_FEATURES; + $disabled = ArcanistLintSeverity::SEVERITY_DISABLED; + if ($this->getLintMessageSeverity($php53) !== $disabled) { + phutil_deprecated( + '`LINT_PHP_53_FEATURES` is deprecated.', + "You should set 'xhpast.php-version' instead."); + + if (!$this->version) { + $this->version = '5.2.3'; + } + } + if ($this->getLintMessageSeverity($php54) !== $disabled) { + phutil_deprecated( + '`LINT_PHP_54_FEATURES` is deprecated.', + "You should set 'xhpast.php-version' instead."); + + if (!$this->version) { + $this->version = '5.3.0'; + } + } + + if (!$this->version) { + return; + } + + $target = phutil_get_library_root('arcanist'). + '/../resources/php_compat_info.json'; + $compat_info = json_decode(file_get_contents($target), true); + + $calls = $root->selectDescendantsOfType('n_FUNCTION_CALL'); + foreach ($calls as $call) { + $node = $call->getChildByIndex(0); + $name = $node->getConcreteString(); + $version = idx($compat_info['functions'], $name); + + if ($version && version_compare($version['min'], $this->version, '>')) { + $this->raiseLintAtNode( + $node, + self::LINT_PHP_COMPATIBILITY, + "This codebase targets PHP {$this->version}, but `{$name}()` was ". + "not introduced until PHP {$version['min']}."); + } else if (array_key_exists($name, $compat_info['params'])) { + $params = $call->getChildOfType(1, 'n_CALL_PARAMETER_LIST'); + foreach (array_values($params->getChildren()) as $i => $param) { + $version = idx($compat_info['params'][$name], $i); + if ($version && version_compare($version, $this->version, '>')) { + $this->raiseLintAtNode( + $param, + self::LINT_PHP_COMPATIBILITY, + "This codebase targets PHP {$this->version}, but parameter ". + ($i + 1)." of `{$name}()` was not introduced until PHP ". + "{$version}."); + } + } + } + + if ($this->windowsVersion) { + $windows = idx($compat_info['functions_windows'], $name); + + if ($windows === false) { + $this->raiseLintAtNode( + $node, + self::LINT_PHP_COMPATIBILITY, + "This codebase targets PHP {$this->windowsVersion} on Windows, ". + "but `{$name}()` is not available there."); + } else if (version_compare($windows, $this->windowsVersion, '>')) { + $this->raiseLintAtNode( + $node, + self::LINT_PHP_COMPATIBILITY, + "This codebase targets PHP {$this->windowsVersion} on Windows, ". + "but `{$name}()` is not available there until PHP ". + "{$this->windowsVersion}."); + } + } + } + + $classes = $root->selectDescendantsOfType('n_CLASS_NAME'); + foreach ($classes as $node) { + $name = $node->getConcreteString(); + $version = idx($compat_info['interfaces'], $name); + $version = idx($compat_info['classes'], $name, $version); + if ($version && version_compare($version['min'], $this->version, '>')) { + $this->raiseLintAtNode( + $node, + self::LINT_PHP_COMPATIBILITY, + "This codebase targets PHP {$this->version}, but `{$name}` was not ". + "introduced until PHP {$version['min']}."); + } + } + + // TODO: Technically, this will include function names. This is unlikely to + // cause any issues (unless, of course, there existed a function that had + // the same name as some constant). + $constants = $root->selectDescendantsOfType('n_SYMBOL_NAME'); + foreach ($constants as $node) { + $name = $node->getConcreteString(); + $version = idx($compat_info['constants'], $name); + if ($version && version_compare($version['min'], $this->version, '>')) { + $this->raiseLintAtNode( + $node, + self::LINT_PHP_53_FEATURES, + "This codebase targets PHP {$this->version}, but `{$name}` was not ". + "introduced until PHP {$version['min']}."); + } + } + + if (version_compare($this->version, '5.3.0') < 0) { + $this->lintPHP53Features($root); + } + if (version_compare($this->version, '5.4.0') < 0) { + $this->lintPHP54Features($root); + } + } + + private function lintPHP53Features(XHPASTNode $root) { $functions = $root->selectTokensOfType('T_FUNCTION'); foreach ($functions as $function) { - $next = $function->getNextToken(); while ($next) { if ($next->isSemantic()) { @@ -366,9 +497,9 @@ if ($next->getTypeName() == '(') { $this->raiseLintAtToken( $function, - self::LINT_PHP_53_FEATURES, - 'This codebase targets PHP 5.2, but anonymous functions were '. - 'not introduced until PHP 5.3.'); + self::LINT_PHP_COMPATIBILITY, + "This codebase targets PHP {$this->version}, but anonymous ". + "functions were not introduced until PHP 5.3."); } } } @@ -377,9 +508,9 @@ foreach ($namespaces as $namespace) { $this->raiseLintAtToken( $namespace, - self::LINT_PHP_53_FEATURES, - 'This codebase targets PHP 5.2, but namespaces were not introduced '. - 'until PHP 5.3.'); + self::LINT_PHP_COMPATIBILITY, + "This codebase targets PHP {$this->version}, but namespaces were not ". + "introduced until PHP 5.3."); } // NOTE: This is only "use x;", in anonymous functions the node type is @@ -392,9 +523,9 @@ foreach ($uses as $use) { $this->raiseLintAtNode( $use, - self::LINT_PHP_53_FEATURES, - 'This codebase targets PHP 5.2, but namespaces were not introduced '. - 'until PHP 5.3.'); + self::LINT_PHP_COMPATIBILITY, + "This codebase targets PHP {$this->version}, but namespaces were not ". + "introduced until PHP 5.3."); } $statics = $root->selectDescendantsOfType('n_CLASS_STATIC_ACCESS'); @@ -406,9 +537,9 @@ if ($name->getConcreteString() == 'static') { $this->raiseLintAtNode( $name, - self::LINT_PHP_53_FEATURES, - 'This codebase targets PHP 5.2, but `static::` was not introduced '. - 'until PHP 5.3.'); + self::LINT_PHP_COMPATIBILITY, + "This codebase targets PHP {$this->version}, but `static::` was not ". + "introduced until PHP 5.3."); } } @@ -418,9 +549,9 @@ if ($yes->getTypeName() == 'n_EMPTY') { $this->raiseLintAtNode( $ternary, - self::LINT_PHP_53_FEATURES, - 'This codebase targets PHP 5.2, but short ternary was not '. - 'introduced until PHP 5.3.'); + self::LINT_PHP_COMPATIBILITY, + "This codebase targets PHP {$this->version}, but short ternary was ". + "not introduced until PHP 5.3."); } } @@ -429,87 +560,14 @@ if (preg_match('/^<<<[\'"]/', $heredoc->getConcreteString())) { $this->raiseLintAtNode( $heredoc, - self::LINT_PHP_53_FEATURES, - 'This codebase targets PHP 5.2, but nowdoc was not introduced until '. - 'PHP 5.3.'); - } - } - - $this->lintPHP53Functions($root); - } - - private function lintPHP53Functions(XHPASTNode $root) { - $target = phutil_get_library_root('arcanist'). - '/../resources/php_compat_info.json'; - $compat_info = json_decode(file_get_contents($target), true); - $required = '5.2.3'; - - $calls = $root->selectDescendantsOfType('n_FUNCTION_CALL'); - foreach ($calls as $call) { - $node = $call->getChildByIndex(0); - $name = $node->getConcreteString(); - $version = idx($compat_info['functions'], $name); - $windows = idx($compat_info['functions_windows'], $name); - if ($version && version_compare($version['min'], $required, '>')) { - $this->raiseLintAtNode( - $node, - self::LINT_PHP_53_FEATURES, - "This codebase targets PHP 5.2.3, but `{$name}()` was not ". - "introduced until PHP {$version['min']}."); - } else if (array_key_exists($name, $compat_info['params'])) { - $params = $call->getChildOfType(1, 'n_CALL_PARAMETER_LIST'); - foreach (array_values($params->getChildren()) as $i => $param) { - $version = idx($compat_info['params'][$name], $i); - if ($version && version_compare($version, $required, '>')) { - $this->raiseLintAtNode( - $param, - self::LINT_PHP_53_FEATURES, - "This codebase targets PHP 5.2.3, but parameter ".($i + 1)." ". - "of `{$name}()` was not introduced until PHP {$version}."); - } - } - } else if ($windows === '' || version_compare($windows, '5.3.0') > 0) { - $this->raiseLintAtNode( - $node, - self::LINT_PHP_53_FEATURES, - "This codebase targets PHP 5.3.0 on Windows, but `{$name}()` is not ". - "available there". - ($windows ? " until PHP {$windows}" : '')."."); - } - } - - $classes = $root->selectDescendantsOfType('n_CLASS_NAME'); - foreach ($classes as $node) { - $name = $node->getConcreteString(); - $version = idx($compat_info['interfaces'], $name); - $version = idx($compat_info['classes'], $name, $version); - if ($version && version_compare($version['min'], $required, '>')) { - $this->raiseLintAtNode( - $node, - self::LINT_PHP_53_FEATURES, - "This codebase targets PHP 5.2.3, but `{$name}` was not ". - "introduced until PHP {$version['min']}."); - } - } - - // TODO: Technically, this will include function names. This is unlikely to - // cause any issues (unless, of course, there existed a function that had - // the same name as some constant). - $constants = $root->selectDescendantsOfType('n_SYMBOL_NAME'); - foreach ($constants as $node) { - $name = $node->getConcreteString(); - $version = idx($compat_info['constants'], $name); - if ($version && version_compare($version['min'], $required, '>')) { - $this->raiseLintAtNode( - $node, - self::LINT_PHP_53_FEATURES, - "This codebase targets PHP 5.2.3, but `{$name}` was not ". - "introduced until PHP {$version['min']}."); + self::LINT_PHP_COMPATIBILITY, + "This codebase targets PHP {$this->version}, but nowdoc was not ". + "introduced until PHP 5.3."); } } } - public function lintPHP54Features(XHPASTNode $root) { + private function lintPHP54Features(XHPASTNode $root) { $indexes = $root->selectDescendantsOfType('n_INDEX_ACCESS'); foreach ($indexes as $index) { $left = $index->getChildByIndex(0); @@ -518,7 +576,7 @@ case 'n_METHOD_CALL': $this->raiseLintAtNode( $index->getChildByIndex(1), - self::LINT_PHP_54_FEATURES, + self::LINT_PHP_COMPATIBILITY, 'The f()[...] syntax was not introduced until PHP 5.4, but this '. 'codebase targets an earlier version of PHP. You can rewrite '. 'this expression using idx().'); @@ -687,7 +745,6 @@ } private function lintBraceFormatting(XHPASTNode $root) { - foreach ($root->selectDescendantsOfType('n_STATEMENT_LIST') as $list) { $tokens = $list->getTokens(); if (!$tokens || head($tokens)->getValue() != '{') { @@ -723,7 +780,6 @@ } } } - } private function lintTautologicalExpressions(XHPASTNode $root) { @@ -784,7 +840,6 @@ } } - /** * Statically evaluate a boolean value from an XHP tree. * @@ -1109,7 +1164,7 @@ } } - protected function lintUndeclaredVariables(XHPASTNode $root) { + private function lintUndeclaredVariables(XHPASTNode $root) { // These things declare variables in a function: // Explicit parameters // Assignment @@ -1473,7 +1528,7 @@ return $concrete; } - protected function lintPHPTagUse(XHPASTNode $root) { + private function lintPHPTagUse(XHPASTNode $root) { $tokens = $root->getTokens(); foreach ($tokens as $token) { if ($token->getTypeName() == 'T_OPEN_TAG') { @@ -1511,8 +1566,7 @@ } } - protected function lintNamingConventions(XHPASTNode $root) { - + private function lintNamingConventions(XHPASTNode $root) { // We're going to build up a list of tuples // and then try to instantiate a hook class which has the opportunity to // override us. @@ -1766,7 +1820,7 @@ } } - protected function lintSurpriseConstructors(XHPASTNode $root) { + private function lintSurpriseConstructors(XHPASTNode $root) { $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION'); foreach ($classes as $class) { $class_name = $class->getChildByIndex(1)->getConcreteString(); @@ -1786,7 +1840,7 @@ } } - protected function lintParenthesesShouldHugExpressions(XHPASTNode $root) { + private function lintParenthesesShouldHugExpressions(XHPASTNode $root) { $calls = $root->selectDescendantsOfType('n_CALL_PARAMETER_LIST'); $controls = $root->selectDescendantsOfType('n_CONTROL_CONDITION'); $fors = $root->selectDescendantsOfType('n_FOR_EXPRESSION'); @@ -1843,7 +1897,7 @@ } } - protected function lintSpaceAfterControlStatementKeywords(XHPASTNode $root) { + private function lintSpaceAfterControlStatementKeywords(XHPASTNode $root) { foreach ($root->getTokens() as $id => $token) { switch ($token->getTypeName()) { case 'T_IF': @@ -1890,7 +1944,7 @@ } } - protected function lintSpaceAroundBinaryOperators(XHPASTNode $root) { + private function lintSpaceAroundBinaryOperators(XHPASTNode $root) { $expressions = $root->selectDescendantsOfType('n_BINARY_EXPRESSION'); foreach ($expressions as $expression) { $operator = $expression->getChildByIndex(1); @@ -1966,7 +2020,7 @@ // declarations (which is not n_BINARY_EXPRESSION). } - protected function lintSpaceAroundConcatenationOperators(XHPASTNode $root) { + private function lintSpaceAroundConcatenationOperators(XHPASTNode $root) { $tokens = $root->selectTokensOfType('.'); foreach ($tokens as $token) { $prev = $token->getPrevToken(); @@ -1997,7 +2051,7 @@ } } - protected function lintDynamicDefines(XHPASTNode $root) { + private function lintDynamicDefines(XHPASTNode $root) { $calls = $root->selectDescendantsOfType('n_FUNCTION_CALL'); foreach ($calls as $call) { $name = $call->getChildByIndex(0)->getConcreteString(); @@ -2014,7 +2068,7 @@ } } - protected function lintUseOfThisInStaticMethods(XHPASTNode $root) { + private function lintUseOfThisInStaticMethods(XHPASTNode $root) { $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION'); foreach ($classes as $class) { $methods = $class->selectDescendantsOfType('n_METHOD_DECLARATION'); @@ -2065,7 +2119,7 @@ * you don't pass a second argument, you're probably going to get something * wrong. */ - protected function lintPregQuote(XHPASTNode $root) { + private function lintPregQuote(XHPASTNode $root) { $function_calls = $root->selectDescendantsOfType('n_FUNCTION_CALL'); foreach ($function_calls as $call) { $name = $call->getChildByIndex(0)->getConcreteString(); @@ -2098,7 +2152,7 @@ * * The former exits with a failure code, the latter with a success code! */ - protected function lintExitExpressions(XHPASTNode $root) { + private function lintExitExpressions(XHPASTNode $root) { $unaries = $root->selectDescendantsOfType('n_UNARY_PREFIX_EXPRESSION'); foreach ($unaries as $unary) { $operator = $unary->getChildByIndex(0)->getConcreteString(); @@ -2131,7 +2185,7 @@ } } - protected function lintTODOComments(XHPASTNode $root) { + private function lintTODOComments(XHPASTNode $root) { $comments = $root->selectTokensOfType('T_COMMENT') + $root->selectTokensOfType('T_DOC_COMMENT'); @@ -2288,7 +2342,6 @@ $calls = $calls->add($root->selectDescendantsOfType('n_METHOD_CALL')); foreach ($calls as $call) { - // If the last parameter of a call is a HEREDOC, don't apply this rule. $params = $call ->getChildOfType(1, 'n_CALL_PARAMETER_LIST') @@ -2398,7 +2451,8 @@ private function lintStrings(XHPASTNode $root) { $nodes = $root->selectDescendantsOfTypes(array( 'n_CONCATENATION_LIST', - 'n_STRING_SCALAR')); + 'n_STRING_SCALAR', + )); foreach ($nodes as $node) { $strings = array(); diff --git a/src/lint/linter/__tests__/xhpast/decl-parens-hug-closing.lint-test b/src/lint/linter/__tests__/xhpast/decl-parens-hug-closing.lint-test --- a/src/lint/linter/__tests__/xhpast/decl-parens-hug-closing.lint-test +++ b/src/lint/linter/__tests__/xhpast/decl-parens-hug-closing.lint-test @@ -29,10 +29,7 @@ warning:12:16 warning:15:37 warning:18:33 -disabled:22:3 -disabled:23:3 warning:23:14 -disabled:24:3 warning:24:14 ~~~~~~~~~~ - + This shouldn't fatal the parser. ~~~~~~~~~~ error:1:10 \ No newline at end of file diff --git a/src/lint/linter/__tests__/xhpast/index-function.lint-test b/src/lint/linter/__tests__/xhpast/index-function.lint-test --- a/src/lint/linter/__tests__/xhpast/index-function.lint-test +++ b/src/lint/linter/__tests__/xhpast/index-function.lint-test @@ -1,4 +1,7 @@ m()[0]; ~~~~~~~~~~ -disabled:3:5 -disabled:4:9 +error:3:5 +error:4:9 +~~~~~~~~~~ +~~~~~~~~~~ +{"config": {"xhpast.php-version": "5.3.0"}} diff --git a/src/lint/linter/__tests__/xhpast/reused-iterator-reference.lint-test b/src/lint/linter/__tests__/xhpast/reused-iterator-reference.lint-test --- a/src/lint/linter/__tests__/xhpast/reused-iterator-reference.lint-test +++ b/src/lint/linter/__tests__/xhpast/reused-iterator-reference.lint-test @@ -129,9 +129,5 @@ warning:52:3 error:85:20 error:87:3 -disabled:93:3 -disabled:99:3 -disabled:107:3 warning:110:5 -disabled:117:3 warning:120:3 diff --git a/src/lint/linter/__tests__/xhpast/switches.lint-test b/src/lint/linter/__tests__/xhpast/switches.lint-test --- a/src/lint/linter/__tests__/xhpast/switches.lint-test +++ b/src/lint/linter/__tests__/xhpast/switches.lint-test @@ -89,7 +89,6 @@ warning:53:3 warning:57:3 warning:66:3 -disabled:68:7 # PHP 5.3 features warning:71:3 warning:75:3 ~~~~~~~~~~ diff --git a/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test b/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test --- a/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test +++ b/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test @@ -175,7 +175,6 @@ } ~~~~~~~~~~ -disabled:3:1 error:30:3 error:32:3 error:38:3 diff --git a/src/lint/linter/__tests__/xhpast/windows.lint-test b/src/lint/linter/__tests__/xhpast/windows.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/xhpast/windows.lint-test @@ -0,0 +1,8 @@ +