mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-29 02:02:40 +01:00
Fold ArcanistPhutilXHPASTLinter
into ArcanistXHPASTLinter
Summary: I've been thinking about this for a while... why not just fold `ArcanistPhutilXHPASTLinter` into `ArcanistXHPASTLinter`? Test Plan: `arc unit` Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D13867
This commit is contained in:
parent
dd0deb2407
commit
9dd6eafb52
39 changed files with 437 additions and 407 deletions
4
.arclint
4
.arclint
|
@ -31,10 +31,6 @@
|
||||||
"type": "phutil-library",
|
"type": "phutil-library",
|
||||||
"include": "(\\.php$)"
|
"include": "(\\.php$)"
|
||||||
},
|
},
|
||||||
"phutil-xhpast": {
|
|
||||||
"type": "phutil-xhpast",
|
|
||||||
"include": "(\\.php$)"
|
|
||||||
},
|
|
||||||
"spelling": {
|
"spelling": {
|
||||||
"type": "spelling",
|
"type": "spelling",
|
||||||
"exclude": "(^resources/spelling/.*\\.json$)"
|
"exclude": "(^resources/spelling/.*\\.json$)"
|
||||||
|
|
|
@ -13,6 +13,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistAliasWorkflow' => 'workflow/ArcanistAliasWorkflow.php',
|
'ArcanistAliasWorkflow' => 'workflow/ArcanistAliasWorkflow.php',
|
||||||
'ArcanistAmendWorkflow' => 'workflow/ArcanistAmendWorkflow.php',
|
'ArcanistAmendWorkflow' => 'workflow/ArcanistAmendWorkflow.php',
|
||||||
'ArcanistAnoidWorkflow' => 'workflow/ArcanistAnoidWorkflow.php',
|
'ArcanistAnoidWorkflow' => 'workflow/ArcanistAnoidWorkflow.php',
|
||||||
|
'ArcanistArrayCombineXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistArrayCombineXHPASTLinterRule.php',
|
||||||
'ArcanistArrayIndexSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistArrayIndexSpacingXHPASTLinterRule.php',
|
'ArcanistArrayIndexSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistArrayIndexSpacingXHPASTLinterRule.php',
|
||||||
'ArcanistArraySeparatorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistArraySeparatorXHPASTLinterRule.php',
|
'ArcanistArraySeparatorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistArraySeparatorXHPASTLinterRule.php',
|
||||||
'ArcanistArrayValueXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistArrayValueXHPASTLinterRule.php',
|
'ArcanistArrayValueXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistArrayValueXHPASTLinterRule.php',
|
||||||
|
@ -40,6 +41,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistCheckstyleXMLLintRenderer' => 'lint/renderer/ArcanistCheckstyleXMLLintRenderer.php',
|
'ArcanistCheckstyleXMLLintRenderer' => 'lint/renderer/ArcanistCheckstyleXMLLintRenderer.php',
|
||||||
'ArcanistChmodLinter' => 'lint/linter/ArcanistChmodLinter.php',
|
'ArcanistChmodLinter' => 'lint/linter/ArcanistChmodLinter.php',
|
||||||
'ArcanistChmodLinterTestCase' => 'lint/linter/__tests__/ArcanistChmodLinterTestCase.php',
|
'ArcanistChmodLinterTestCase' => 'lint/linter/__tests__/ArcanistChmodLinterTestCase.php',
|
||||||
|
'ArcanistClassExtendsObjectXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistClassExtendsObjectXHPASTLinterRule.php',
|
||||||
'ArcanistClassFilenameMismatchXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistClassFilenameMismatchXHPASTLinterRule.php',
|
'ArcanistClassFilenameMismatchXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistClassFilenameMismatchXHPASTLinterRule.php',
|
||||||
'ArcanistClassNameLiteralXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistClassNameLiteralXHPASTLinterRule.php',
|
'ArcanistClassNameLiteralXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistClassNameLiteralXHPASTLinterRule.php',
|
||||||
'ArcanistCloseRevisionWorkflow' => 'workflow/ArcanistCloseRevisionWorkflow.php',
|
'ArcanistCloseRevisionWorkflow' => 'workflow/ArcanistCloseRevisionWorkflow.php',
|
||||||
|
@ -71,6 +73,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistCpplintLinterTestCase' => 'lint/linter/__tests__/ArcanistCpplintLinterTestCase.php',
|
'ArcanistCpplintLinterTestCase' => 'lint/linter/__tests__/ArcanistCpplintLinterTestCase.php',
|
||||||
'ArcanistDeclarationParenthesesXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDeclarationParenthesesXHPASTLinterRule.php',
|
'ArcanistDeclarationParenthesesXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDeclarationParenthesesXHPASTLinterRule.php',
|
||||||
'ArcanistDefaultParametersXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDefaultParametersXHPASTLinterRule.php',
|
'ArcanistDefaultParametersXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDefaultParametersXHPASTLinterRule.php',
|
||||||
|
'ArcanistDeprecationXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDeprecationXHPASTLinterRule.php',
|
||||||
'ArcanistDiffChange' => 'parser/diff/ArcanistDiffChange.php',
|
'ArcanistDiffChange' => 'parser/diff/ArcanistDiffChange.php',
|
||||||
'ArcanistDiffChangeType' => 'parser/diff/ArcanistDiffChangeType.php',
|
'ArcanistDiffChangeType' => 'parser/diff/ArcanistDiffChangeType.php',
|
||||||
'ArcanistDiffHunk' => 'parser/diff/ArcanistDiffHunk.php',
|
'ArcanistDiffHunk' => 'parser/diff/ArcanistDiffHunk.php',
|
||||||
|
@ -203,9 +206,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistPhpunitTestResultParser' => 'unit/parser/ArcanistPhpunitTestResultParser.php',
|
'ArcanistPhpunitTestResultParser' => 'unit/parser/ArcanistPhpunitTestResultParser.php',
|
||||||
'ArcanistPhrequentWorkflow' => 'workflow/ArcanistPhrequentWorkflow.php',
|
'ArcanistPhrequentWorkflow' => 'workflow/ArcanistPhrequentWorkflow.php',
|
||||||
'ArcanistPhutilLibraryLinter' => 'lint/linter/ArcanistPhutilLibraryLinter.php',
|
'ArcanistPhutilLibraryLinter' => 'lint/linter/ArcanistPhutilLibraryLinter.php',
|
||||||
'ArcanistPhutilXHPASTLinter' => 'lint/linter/ArcanistPhutilXHPASTLinter.php',
|
|
||||||
'ArcanistPhutilXHPASTLinterStandard' => 'lint/linter/standards/phutil/ArcanistPhutilXHPASTLinterStandard.php',
|
'ArcanistPhutilXHPASTLinterStandard' => 'lint/linter/standards/phutil/ArcanistPhutilXHPASTLinterStandard.php',
|
||||||
'ArcanistPhutilXHPASTLinterTestCase' => 'lint/linter/__tests__/ArcanistPhutilXHPASTLinterTestCase.php',
|
|
||||||
'ArcanistPlusOperatorOnStringsXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPlusOperatorOnStringsXHPASTLinterRule.php',
|
'ArcanistPlusOperatorOnStringsXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPlusOperatorOnStringsXHPASTLinterRule.php',
|
||||||
'ArcanistPregQuoteMisuseXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPregQuoteMisuseXHPASTLinterRule.php',
|
'ArcanistPregQuoteMisuseXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPregQuoteMisuseXHPASTLinterRule.php',
|
||||||
'ArcanistPuppetLintLinter' => 'lint/linter/ArcanistPuppetLintLinter.php',
|
'ArcanistPuppetLintLinter' => 'lint/linter/ArcanistPuppetLintLinter.php',
|
||||||
|
@ -214,6 +215,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistPyFlakesLinterTestCase' => 'lint/linter/__tests__/ArcanistPyFlakesLinterTestCase.php',
|
'ArcanistPyFlakesLinterTestCase' => 'lint/linter/__tests__/ArcanistPyFlakesLinterTestCase.php',
|
||||||
'ArcanistPyLintLinter' => 'lint/linter/ArcanistPyLintLinter.php',
|
'ArcanistPyLintLinter' => 'lint/linter/ArcanistPyLintLinter.php',
|
||||||
'ArcanistPyLintLinterTestCase' => 'lint/linter/__tests__/ArcanistPyLintLinterTestCase.php',
|
'ArcanistPyLintLinterTestCase' => 'lint/linter/__tests__/ArcanistPyLintLinterTestCase.php',
|
||||||
|
'ArcanistRaggedClassTreeEdgeXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistRaggedClassTreeEdgeXHPASTLinterRule.php',
|
||||||
'ArcanistRepositoryAPI' => 'repository/api/ArcanistRepositoryAPI.php',
|
'ArcanistRepositoryAPI' => 'repository/api/ArcanistRepositoryAPI.php',
|
||||||
'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php',
|
'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php',
|
||||||
'ArcanistRepositoryAPIStateTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php',
|
'ArcanistRepositoryAPIStateTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php',
|
||||||
|
@ -265,6 +267,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistUnitWorkflow' => 'workflow/ArcanistUnitWorkflow.php',
|
'ArcanistUnitWorkflow' => 'workflow/ArcanistUnitWorkflow.php',
|
||||||
'ArcanistUnnecessaryFinalModifierXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistUnnecessaryFinalModifierXHPASTLinterRule.php',
|
'ArcanistUnnecessaryFinalModifierXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistUnnecessaryFinalModifierXHPASTLinterRule.php',
|
||||||
'ArcanistUnnecessarySemicolonXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistUnnecessarySemicolonXHPASTLinterRule.php',
|
'ArcanistUnnecessarySemicolonXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistUnnecessarySemicolonXHPASTLinterRule.php',
|
||||||
|
'ArcanistUnsafeDynamicStringXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistUnsafeDynamicStringXHPASTLinterRule.php',
|
||||||
'ArcanistUpgradeWorkflow' => 'workflow/ArcanistUpgradeWorkflow.php',
|
'ArcanistUpgradeWorkflow' => 'workflow/ArcanistUpgradeWorkflow.php',
|
||||||
'ArcanistUploadWorkflow' => 'workflow/ArcanistUploadWorkflow.php',
|
'ArcanistUploadWorkflow' => 'workflow/ArcanistUploadWorkflow.php',
|
||||||
'ArcanistUsageException' => 'exception/ArcanistUsageException.php',
|
'ArcanistUsageException' => 'exception/ArcanistUsageException.php',
|
||||||
|
@ -305,6 +308,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistAliasWorkflow' => 'ArcanistWorkflow',
|
'ArcanistAliasWorkflow' => 'ArcanistWorkflow',
|
||||||
'ArcanistAmendWorkflow' => 'ArcanistWorkflow',
|
'ArcanistAmendWorkflow' => 'ArcanistWorkflow',
|
||||||
'ArcanistAnoidWorkflow' => 'ArcanistWorkflow',
|
'ArcanistAnoidWorkflow' => 'ArcanistWorkflow',
|
||||||
|
'ArcanistArrayCombineXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistArrayIndexSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistArrayIndexSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistArraySeparatorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistArraySeparatorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistArrayValueXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistArrayValueXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
|
@ -332,6 +336,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistCheckstyleXMLLintRenderer' => 'ArcanistLintRenderer',
|
'ArcanistCheckstyleXMLLintRenderer' => 'ArcanistLintRenderer',
|
||||||
'ArcanistChmodLinter' => 'ArcanistLinter',
|
'ArcanistChmodLinter' => 'ArcanistLinter',
|
||||||
'ArcanistChmodLinterTestCase' => 'ArcanistLinterTestCase',
|
'ArcanistChmodLinterTestCase' => 'ArcanistLinterTestCase',
|
||||||
|
'ArcanistClassExtendsObjectXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistClassFilenameMismatchXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistClassFilenameMismatchXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistClassNameLiteralXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistClassNameLiteralXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistCloseRevisionWorkflow' => 'ArcanistWorkflow',
|
'ArcanistCloseRevisionWorkflow' => 'ArcanistWorkflow',
|
||||||
|
@ -363,6 +368,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistCpplintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
'ArcanistCpplintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||||
'ArcanistDeclarationParenthesesXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistDeclarationParenthesesXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistDefaultParametersXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistDefaultParametersXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
|
'ArcanistDeprecationXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistDiffChange' => 'Phobject',
|
'ArcanistDiffChange' => 'Phobject',
|
||||||
'ArcanistDiffChangeType' => 'Phobject',
|
'ArcanistDiffChangeType' => 'Phobject',
|
||||||
'ArcanistDiffHunk' => 'Phobject',
|
'ArcanistDiffHunk' => 'Phobject',
|
||||||
|
@ -495,9 +501,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistPhpunitTestResultParser' => 'ArcanistTestResultParser',
|
'ArcanistPhpunitTestResultParser' => 'ArcanistTestResultParser',
|
||||||
'ArcanistPhrequentWorkflow' => 'ArcanistWorkflow',
|
'ArcanistPhrequentWorkflow' => 'ArcanistWorkflow',
|
||||||
'ArcanistPhutilLibraryLinter' => 'ArcanistLinter',
|
'ArcanistPhutilLibraryLinter' => 'ArcanistLinter',
|
||||||
'ArcanistPhutilXHPASTLinter' => 'ArcanistBaseXHPASTLinter',
|
|
||||||
'ArcanistPhutilXHPASTLinterStandard' => 'ArcanistLinterStandard',
|
'ArcanistPhutilXHPASTLinterStandard' => 'ArcanistLinterStandard',
|
||||||
'ArcanistPhutilXHPASTLinterTestCase' => 'ArcanistLinterTestCase',
|
|
||||||
'ArcanistPlusOperatorOnStringsXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistPlusOperatorOnStringsXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistPregQuoteMisuseXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistPregQuoteMisuseXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistPuppetLintLinter' => 'ArcanistExternalLinter',
|
'ArcanistPuppetLintLinter' => 'ArcanistExternalLinter',
|
||||||
|
@ -506,6 +510,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistPyFlakesLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
'ArcanistPyFlakesLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||||
'ArcanistPyLintLinter' => 'ArcanistExternalLinter',
|
'ArcanistPyLintLinter' => 'ArcanistExternalLinter',
|
||||||
'ArcanistPyLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
'ArcanistPyLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||||
|
'ArcanistRaggedClassTreeEdgeXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistRepositoryAPI' => 'Phobject',
|
'ArcanistRepositoryAPI' => 'Phobject',
|
||||||
'ArcanistRepositoryAPIMiscTestCase' => 'PhutilTestCase',
|
'ArcanistRepositoryAPIMiscTestCase' => 'PhutilTestCase',
|
||||||
'ArcanistRepositoryAPIStateTestCase' => 'PhutilTestCase',
|
'ArcanistRepositoryAPIStateTestCase' => 'PhutilTestCase',
|
||||||
|
@ -557,6 +562,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistUnitWorkflow' => 'ArcanistWorkflow',
|
'ArcanistUnitWorkflow' => 'ArcanistWorkflow',
|
||||||
'ArcanistUnnecessaryFinalModifierXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistUnnecessaryFinalModifierXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistUnnecessarySemicolonXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistUnnecessarySemicolonXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
|
'ArcanistUnsafeDynamicStringXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistUpgradeWorkflow' => 'ArcanistWorkflow',
|
'ArcanistUpgradeWorkflow' => 'ArcanistWorkflow',
|
||||||
'ArcanistUploadWorkflow' => 'ArcanistWorkflow',
|
'ArcanistUploadWorkflow' => 'ArcanistWorkflow',
|
||||||
'ArcanistUsageException' => 'Exception',
|
'ArcanistUsageException' => 'Exception',
|
||||||
|
|
|
@ -1,336 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class ArcanistPhutilXHPASTLinter extends ArcanistBaseXHPASTLinter {
|
|
||||||
|
|
||||||
const LINT_ARRAY_COMBINE = 2;
|
|
||||||
const LINT_DEPRECATED_FUNCTION = 3;
|
|
||||||
const LINT_UNSAFE_DYNAMIC_STRING = 4;
|
|
||||||
const LINT_RAGGED_CLASSTREE_EDGE = 5;
|
|
||||||
const LINT_EXTENDS_PHOBJECT = 6;
|
|
||||||
|
|
||||||
private $deprecatedFunctions = array();
|
|
||||||
private $dynamicStringFunctions = array();
|
|
||||||
private $dynamicStringClasses = array();
|
|
||||||
|
|
||||||
public function getInfoName() {
|
|
||||||
return 'XHPAST/libphutil Lint';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getInfoDescription() {
|
|
||||||
return pht(
|
|
||||||
'Use XHPAST to run libphutil-specific rules on a PHP library. This '.
|
|
||||||
'linter is intended for use in Phabricator libraries and extensions.');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLintNameMap() {
|
|
||||||
return array(
|
|
||||||
self::LINT_ARRAY_COMBINE =>
|
|
||||||
pht('%s Unreliable', 'array_combine()'),
|
|
||||||
self::LINT_DEPRECATED_FUNCTION =>
|
|
||||||
pht('Use of Deprecated Function'),
|
|
||||||
self::LINT_UNSAFE_DYNAMIC_STRING =>
|
|
||||||
pht('Unsafe Usage of Dynamic String'),
|
|
||||||
self::LINT_RAGGED_CLASSTREE_EDGE =>
|
|
||||||
pht('Class Not %s Or %s', 'abstract', 'final'),
|
|
||||||
self::LINT_EXTENDS_PHOBJECT =>
|
|
||||||
pht('Class Not Extending %s', 'Phobject'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLinterName() {
|
|
||||||
return 'PHLXHP';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLinterConfigurationName() {
|
|
||||||
return 'phutil-xhpast';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLintSeverityMap() {
|
|
||||||
$advice = ArcanistLintSeverity::SEVERITY_ADVICE;
|
|
||||||
$warning = ArcanistLintSeverity::SEVERITY_WARNING;
|
|
||||||
|
|
||||||
return array(
|
|
||||||
self::LINT_ARRAY_COMBINE => $warning,
|
|
||||||
self::LINT_DEPRECATED_FUNCTION => $warning,
|
|
||||||
self::LINT_UNSAFE_DYNAMIC_STRING => $warning,
|
|
||||||
self::LINT_RAGGED_CLASSTREE_EDGE => $warning,
|
|
||||||
self::LINT_EXTENDS_PHOBJECT => $advice,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLinterConfigurationOptions() {
|
|
||||||
$options = array(
|
|
||||||
'phutil-xhpast.deprecated.functions' => array(
|
|
||||||
'type' => 'optional map<string, string>',
|
|
||||||
'help' => pht(
|
|
||||||
'Functions which should should be considered deprecated.'),
|
|
||||||
),
|
|
||||||
'phutil-xhpast.dynamic-string.functions' => array(
|
|
||||||
'type' => 'optional map<string, string>',
|
|
||||||
'help' => pht(
|
|
||||||
'Functions which should should not be used because they represent '.
|
|
||||||
'the unsafe usage of dynamic strings.'),
|
|
||||||
),
|
|
||||||
'phutil-xhpast.dynamic-string.classes' => array(
|
|
||||||
'type' => 'optional map<string, string>',
|
|
||||||
'help' => pht(
|
|
||||||
'Classes which should should not be used because they represent the '.
|
|
||||||
'unsafe usage of dynamic strings.'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return $options + parent::getLinterConfigurationOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setLinterConfigurationValue($key, $value) {
|
|
||||||
switch ($key) {
|
|
||||||
case 'phutil-xhpast.deprecated.functions':
|
|
||||||
$this->setDeprecatedFunctions($value);
|
|
||||||
return;
|
|
||||||
case 'phutil-xhpast.dynamic-string.functions':
|
|
||||||
$this->setDynamicStringFunctions($value);
|
|
||||||
return;
|
|
||||||
case 'phutil-xhpast.dynamic-string.classes':
|
|
||||||
$this->setDynamicStringClasses($value);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
parent::setLinterConfigurationValue($key, $value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getVersion() {
|
|
||||||
// The version number should be incremented whenever a new rule is added.
|
|
||||||
return '3';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function resolveFuture($path, Future $future) {
|
|
||||||
$tree = $this->getXHPASTLinter()->getXHPASTTreeForPath($path);
|
|
||||||
if (!$tree) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$root = $tree->getRootNode();
|
|
||||||
|
|
||||||
$method_codes = array(
|
|
||||||
'lintArrayCombine' => self::LINT_ARRAY_COMBINE,
|
|
||||||
'lintUnsafeDynamicString' => self::LINT_UNSAFE_DYNAMIC_STRING,
|
|
||||||
'lintDeprecatedFunctions' => self::LINT_DEPRECATED_FUNCTION,
|
|
||||||
'lintRaggedClasstreeEdges' => self::LINT_RAGGED_CLASSTREE_EDGE,
|
|
||||||
'lintClassExtendsPhobject' => self::LINT_EXTENDS_PHOBJECT,
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($method_codes as $method => $codes) {
|
|
||||||
foreach ((array)$codes as $code) {
|
|
||||||
if ($this->isCodeEnabled($code)) {
|
|
||||||
call_user_func(array($this, $method), $root);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* -( Setters )------------------------------------------------------------ */
|
|
||||||
|
|
||||||
public function setDeprecatedFunctions(array $map) {
|
|
||||||
$this->deprecatedFunctions = $map;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setDynamicStringClasses(array $map) {
|
|
||||||
$this->dynamicStringClasses = $map;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setDynamicStringFunctions(array $map) {
|
|
||||||
$this->dynamicStringFunctions = $map;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* -( Linter Rules )------------------------------------------------------- */
|
|
||||||
|
|
||||||
private function lintUnsafeDynamicString(XHPASTNode $root) {
|
|
||||||
$safe = $this->dynamicStringFunctions + array(
|
|
||||||
'pht' => 0,
|
|
||||||
|
|
||||||
'hsprintf' => 0,
|
|
||||||
'jsprintf' => 0,
|
|
||||||
|
|
||||||
'hgsprintf' => 0,
|
|
||||||
|
|
||||||
'csprintf' => 0,
|
|
||||||
'vcsprintf' => 0,
|
|
||||||
'execx' => 0,
|
|
||||||
'exec_manual' => 0,
|
|
||||||
'phutil_passthru' => 0,
|
|
||||||
|
|
||||||
'qsprintf' => 1,
|
|
||||||
'vqsprintf' => 1,
|
|
||||||
'queryfx' => 1,
|
|
||||||
'queryfx_all' => 1,
|
|
||||||
'queryfx_one' => 1,
|
|
||||||
);
|
|
||||||
|
|
||||||
$calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
|
|
||||||
$this->lintUnsafeDynamicStringCall($calls, $safe);
|
|
||||||
|
|
||||||
$safe = $this->dynamicStringClasses + array(
|
|
||||||
'ExecFuture' => 0,
|
|
||||||
);
|
|
||||||
|
|
||||||
$news = $root->selectDescendantsOfType('n_NEW');
|
|
||||||
$this->lintUnsafeDynamicStringCall($news, $safe);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function lintUnsafeDynamicStringCall(
|
|
||||||
AASTNodeList $calls,
|
|
||||||
array $safe) {
|
|
||||||
|
|
||||||
$safe = array_combine(
|
|
||||||
array_map('strtolower', array_keys($safe)),
|
|
||||||
$safe);
|
|
||||||
|
|
||||||
foreach ($calls as $call) {
|
|
||||||
$name = $call->getChildByIndex(0)->getConcreteString();
|
|
||||||
$param = idx($safe, strtolower($name));
|
|
||||||
|
|
||||||
if ($param === null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$parameters = $call->getChildByIndex(1);
|
|
||||||
if (count($parameters->getChildren()) <= $param) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$identifier = $parameters->getChildByIndex($param);
|
|
||||||
if (!$identifier->isConstantString()) {
|
|
||||||
$this->raiseLintAtNode(
|
|
||||||
$call,
|
|
||||||
self::LINT_UNSAFE_DYNAMIC_STRING,
|
|
||||||
pht(
|
|
||||||
"Parameter %d of %s should be a scalar string, ".
|
|
||||||
"otherwise it's not safe.",
|
|
||||||
$param + 1,
|
|
||||||
$name.'()'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function lintArrayCombine(XHPASTNode $root) {
|
|
||||||
$function_calls = $this->getFunctionCalls($root, array('array_combine'));
|
|
||||||
|
|
||||||
foreach ($function_calls as $call) {
|
|
||||||
$name = $call->getChildByIndex(0)->getConcreteString();
|
|
||||||
$parameter_list = $call->getChildOfType(1, 'n_CALL_PARAMETER_LIST');
|
|
||||||
|
|
||||||
if (count($parameter_list->getChildren()) !== 2) {
|
|
||||||
// Wrong number of parameters, but raise that elsewhere if we want.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$first = $parameter_list->getChildByIndex(0);
|
|
||||||
$second = $parameter_list->getChildByIndex(1);
|
|
||||||
|
|
||||||
if ($first->getConcreteString() == $second->getConcreteString()) {
|
|
||||||
$this->raiseLintAtNode(
|
|
||||||
$call,
|
|
||||||
self::LINT_ARRAY_COMBINE,
|
|
||||||
pht(
|
|
||||||
'Prior to PHP 5.4, `%s` fails when given empty arrays. '.
|
|
||||||
'Prefer to write `%s` as `%s`.',
|
|
||||||
'array_combine()',
|
|
||||||
'array_combine(x, x)',
|
|
||||||
'array_fuse(x)'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function lintDeprecatedFunctions(XHPASTNode $root) {
|
|
||||||
$map = $this->deprecatedFunctions;
|
|
||||||
$function_calls = $this->getFunctionCalls($root, array_keys($map));
|
|
||||||
|
|
||||||
foreach ($function_calls as $call) {
|
|
||||||
$name = $call
|
|
||||||
->getChildByIndex(0)
|
|
||||||
->getConcreteString();
|
|
||||||
|
|
||||||
$name = strtolower($name);
|
|
||||||
if (empty($map[$name])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->raiseLintAtNode(
|
|
||||||
$call,
|
|
||||||
self::LINT_DEPRECATED_FUNCTION,
|
|
||||||
$map[$name]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function lintRaggedClasstreeEdges(XHPASTNode $root) {
|
|
||||||
$parser = new PhutilDocblockParser();
|
|
||||||
|
|
||||||
$classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
|
|
||||||
foreach ($classes as $class) {
|
|
||||||
$is_final = false;
|
|
||||||
$is_abstract = false;
|
|
||||||
$is_concrete_extensible = false;
|
|
||||||
|
|
||||||
$attributes = $class->getChildOfType(0, 'n_CLASS_ATTRIBUTES');
|
|
||||||
foreach ($attributes->getChildren() as $child) {
|
|
||||||
if ($child->getConcreteString() == 'final') {
|
|
||||||
$is_final = true;
|
|
||||||
}
|
|
||||||
if ($child->getConcreteString() == 'abstract') {
|
|
||||||
$is_abstract = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$docblock = $class->getDocblockToken();
|
|
||||||
if ($docblock) {
|
|
||||||
list($text, $specials) = $parser->parse($docblock->getValue());
|
|
||||||
$is_concrete_extensible = idx($specials, 'concrete-extensible');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$is_final && !$is_abstract && !$is_concrete_extensible) {
|
|
||||||
$this->raiseLintAtNode(
|
|
||||||
$class->getChildOfType(1, 'n_CLASS_NAME'),
|
|
||||||
self::LINT_RAGGED_CLASSTREE_EDGE,
|
|
||||||
pht(
|
|
||||||
"This class is neither '%s' nor '%s', and does not have ".
|
|
||||||
"a docblock marking it '%s'.",
|
|
||||||
'final',
|
|
||||||
'abstract',
|
|
||||||
'@concrete-extensible'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function lintClassExtendsPhobject(XHPASTNode $root) {
|
|
||||||
$classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
|
|
||||||
|
|
||||||
foreach ($classes as $class) {
|
|
||||||
// TODO: This doesn't quite work for namespaced classes (see T8534).
|
|
||||||
$name = $class->getChildOfType(1, 'n_CLASS_NAME');
|
|
||||||
$extends = $class->getChildByIndex(2);
|
|
||||||
|
|
||||||
if ($name->getConcreteString() == 'Phobject') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($extends->getTypeName() == 'n_EMPTY') {
|
|
||||||
$this->raiseLintAtNode(
|
|
||||||
$class,
|
|
||||||
self::LINT_EXTENDS_PHOBJECT,
|
|
||||||
pht(
|
|
||||||
'Classes should extend from %s or from some other class. '.
|
|
||||||
'All classes (except for %s itself) should have a base class.',
|
|
||||||
'Phobject',
|
|
||||||
'Phobject'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class ArcanistPhutilXHPASTLinterTestCase extends ArcanistLinterTestCase {
|
|
||||||
|
|
||||||
public function testLinter() {
|
|
||||||
$linter = new ArcanistPhutilXHPASTLinter();
|
|
||||||
$linter->setDeprecatedFunctions(array(
|
|
||||||
'deprecated_function' => pht('This function is most likely deprecated.'),
|
|
||||||
));
|
|
||||||
|
|
||||||
$this->executeTestsInDirectory(dirname(__FILE__).'/phlxhp/', $linter);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
deprecated_function();
|
|
||||||
modern_function();
|
|
||||||
~~~~~~~~~~
|
|
||||||
warning:3:1
|
|
|
@ -1,27 +0,0 @@
|
||||||
<?php
|
|
||||||
pht('a');
|
|
||||||
pht("a");
|
|
||||||
pht('a'.'b');
|
|
||||||
pht(f());
|
|
||||||
pht();
|
|
||||||
pht($a);
|
|
||||||
pht('a'.$a);
|
|
||||||
pht('$a');
|
|
||||||
pht("$a");
|
|
||||||
pht('%s', $a);
|
|
||||||
|
|
||||||
pht(<<<EOT
|
|
||||||
a
|
|
||||||
EOT
|
|
||||||
);
|
|
||||||
|
|
||||||
pht(<<<EOT
|
|
||||||
$a
|
|
||||||
EOT
|
|
||||||
);
|
|
||||||
~~~~~~~~~~
|
|
||||||
warning:5:1
|
|
||||||
warning:7:1
|
|
||||||
warning:8:1
|
|
||||||
warning:10:1
|
|
||||||
warning:18:1
|
|
|
@ -1,13 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
csprintf('x');
|
|
||||||
csprintf($x);
|
|
||||||
|
|
||||||
qsprintf();
|
|
||||||
qsprintf('x');
|
|
||||||
qsprintf('x', 'y');
|
|
||||||
qsprintf('x', $y);
|
|
||||||
|
|
||||||
~~~~~~~~~~
|
|
||||||
warning:4:1
|
|
||||||
warning:9:1
|
|
|
@ -3,4 +3,4 @@
|
||||||
array_combine($x, $x);
|
array_combine($x, $x);
|
||||||
array_combine($x, $y);
|
array_combine($x, $y);
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
warning:3:1
|
disabled:3:1
|
|
@ -25,7 +25,9 @@ $foo(&$myvar);
|
||||||
array_walk(array(), function () use (&$x) {});
|
array_walk(array(), function () use (&$x) {});
|
||||||
MyClass::myfunc(array(&$x, &$y));
|
MyClass::myfunc(array(&$x, &$y));
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
disabled:3:1
|
||||||
error:3:7 XHP19
|
error:3:7 XHP19
|
||||||
|
disabled:3:7
|
||||||
error:10:8
|
error:10:8
|
||||||
error:13:15
|
error:13:15
|
||||||
error:16:17
|
error:16:17
|
||||||
|
|
|
@ -12,6 +12,8 @@ class MyClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
disabled:3:1
|
||||||
|
disabled:3:7
|
||||||
error:3:7
|
error:3:7
|
||||||
advice:5:12
|
advice:5:12
|
||||||
advice:9:10
|
advice:9:10
|
||||||
|
|
|
@ -9,5 +9,7 @@ class Foo {
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
warning:3:8
|
warning:3:8
|
||||||
warning:4:7
|
warning:4:7
|
||||||
|
disabled:6:1
|
||||||
error:6:7
|
error:6:7
|
||||||
|
disabled:6:7
|
||||||
warning:7:9
|
warning:7:9
|
||||||
|
|
|
@ -28,6 +28,7 @@ f(function ($x)use($z) {});
|
||||||
warning:4:14
|
warning:4:14
|
||||||
warning:5:11
|
warning:5:11
|
||||||
warning:8:15
|
warning:8:15
|
||||||
|
disabled:10:1
|
||||||
error:10:13
|
error:10:13
|
||||||
warning:13:23
|
warning:13:23
|
||||||
warning:16:31
|
warning:16:31
|
||||||
|
|
|
@ -9,5 +9,7 @@ class MyClass {
|
||||||
}
|
}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
warning:4:13
|
warning:4:13
|
||||||
|
disabled:7:1
|
||||||
error:7:7
|
error:7:7
|
||||||
|
disabled:7:7
|
||||||
warning:8:27
|
warning:8:27
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
deprecated_function();
|
||||||
|
modern_function();
|
||||||
|
~~~~~~~~~~
|
||||||
|
warning:3:1
|
||||||
|
~~~~~~~~~~
|
||||||
|
~~~~~~~~~~
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"xhpast.deprecated.functions": {
|
||||||
|
"deprecated_function": "This function is deprecated."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
final class Foo {};
|
final class Foo {};
|
||||||
$x = null;;
|
$x = null;;
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
disabled:3:1
|
||||||
error:3:13 XHP19
|
error:3:13 XHP19
|
||||||
advice:3:19
|
advice:3:19
|
||||||
advice:4:11
|
advice:4:11
|
||||||
|
|
|
@ -4,4 +4,4 @@ abstract class A extends Phobject {}
|
||||||
final class B extends A {}
|
final class B extends A {}
|
||||||
final class C {}
|
final class C {}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
advice:5:1
|
disabled:5:1
|
|
@ -11,6 +11,7 @@ final class Foo {
|
||||||
private $z;
|
private $z;
|
||||||
}
|
}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
disabled:3:1
|
||||||
error:3:13 XHP19
|
error:3:13 XHP19
|
||||||
advice:5:3
|
advice:5:3
|
||||||
advice:6:3
|
advice:6:3
|
||||||
|
|
|
@ -11,6 +11,8 @@ class SomeClass {
|
||||||
abstract final public function baz() {}
|
abstract final public function baz() {}
|
||||||
}
|
}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
disabled:3:1
|
||||||
|
disabled:3:7
|
||||||
error:3:7 XHP19
|
error:3:7 XHP19
|
||||||
error:5:10
|
error:5:10
|
||||||
error:6:10
|
error:6:10
|
||||||
|
|
|
@ -9,6 +9,8 @@ class Foo {
|
||||||
static final public function foobar() {}
|
static final public function foobar() {}
|
||||||
}
|
}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
disabled:3:1
|
||||||
|
disabled:3:7
|
||||||
error:3:7 XHP19
|
error:3:7 XHP19
|
||||||
advice:5:3
|
advice:5:3
|
||||||
advice:7:3
|
advice:7:3
|
||||||
|
|
|
@ -50,6 +50,7 @@ function j() {
|
||||||
$mIxEdCaSe = 1;
|
$mIxEdCaSe = 1;
|
||||||
}
|
}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
disabled:3:1
|
||||||
warning:3:13
|
warning:3:13
|
||||||
warning:4:9
|
warning:4:9
|
||||||
warning:4:9
|
warning:4:9
|
||||||
|
@ -63,6 +64,7 @@ warning:6:25
|
||||||
warning:9:11
|
warning:9:11
|
||||||
warning:11:10
|
warning:11:10
|
||||||
warning:11:13
|
warning:11:13
|
||||||
|
disabled:13:1
|
||||||
warning:21:13
|
warning:21:13
|
||||||
warning:24:3
|
warning:24:3
|
||||||
warning:25:3
|
warning:25:3
|
||||||
|
|
|
@ -16,4 +16,5 @@ final class MyInvalidClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
disabled:9:1
|
||||||
error:11:5
|
error:11:5
|
||||||
|
|
|
@ -3,3 +3,5 @@
|
||||||
abstract class A {}
|
abstract class A {}
|
||||||
final class F {}
|
final class F {}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
disabled:3:1
|
||||||
|
disabled:4:1
|
||||||
|
|
|
@ -33,6 +33,7 @@ warning:14:10
|
||||||
warning:14:19
|
warning:14:19
|
||||||
warning:15:12
|
warning:15:12
|
||||||
warning:15:15
|
warning:15:15
|
||||||
|
disabled:16:1
|
||||||
error:16:13 XHP19 Class-Filename Mismatch
|
error:16:13 XHP19 Class-Filename Mismatch
|
||||||
warning:17:21
|
warning:17:21
|
||||||
warning:17:24
|
warning:17:24
|
||||||
|
|
39
src/lint/linter/__tests__/xhpast/pht.lint-test
Normal file
39
src/lint/linter/__tests__/xhpast/pht.lint-test
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
pht('a');
|
||||||
|
pht("a");
|
||||||
|
pht('a'.'b');
|
||||||
|
pht(f());
|
||||||
|
pht();
|
||||||
|
pht($a);
|
||||||
|
pht('a'.$a);
|
||||||
|
pht('$a');
|
||||||
|
pht("$a");
|
||||||
|
pht('%s', $a);
|
||||||
|
|
||||||
|
pht(<<<EOT
|
||||||
|
a
|
||||||
|
EOT
|
||||||
|
);
|
||||||
|
|
||||||
|
pht(<<<EOT
|
||||||
|
$a
|
||||||
|
EOT
|
||||||
|
);
|
||||||
|
~~~~~~~~~~
|
||||||
|
advice:4:5
|
||||||
|
error:6:1
|
||||||
|
error:7:1
|
||||||
|
error:8:1
|
||||||
|
error:9:1
|
||||||
|
error:11:1
|
||||||
|
error:19:1
|
||||||
|
~~~~~~~~~~
|
||||||
|
~~~~~~~~~~
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"xhpast.dynamic-string.functions": {
|
||||||
|
"pht": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,4 +9,4 @@ final class C extends Phobject {}
|
||||||
*/
|
*/
|
||||||
class D extends Phobject {}
|
class D extends Phobject {}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
warning:3:7
|
disabled:3:7
|
|
@ -25,6 +25,7 @@ MyClass :: myMethod();
|
||||||
SomeReallyLongClassName
|
SomeReallyLongClassName
|
||||||
::someMethod();
|
::someMethod();
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
disabled:3:7
|
||||||
error:3:7
|
error:3:7
|
||||||
advice:7:5
|
advice:7:5
|
||||||
advice:12:10
|
advice:12:10
|
||||||
|
|
|
@ -6,5 +6,6 @@ final class Platypus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
disabled:3:1
|
||||||
error:3:13 XHP19 Class-Filename Mismatch
|
error:3:13 XHP19 Class-Filename Mismatch
|
||||||
error:4:19
|
error:4:19
|
||||||
|
|
|
@ -89,6 +89,7 @@ warning:48:3
|
||||||
warning:53:3
|
warning:53:3
|
||||||
warning:57:3
|
warning:57:3
|
||||||
warning:66:3
|
warning:66:3
|
||||||
|
disabled:69:5
|
||||||
warning:71:3
|
warning:71:3
|
||||||
warning:75:3
|
warning:75:3
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
|
@ -24,4 +24,9 @@ abstract class SomeAbstractClass {
|
||||||
abstract public function __toString();
|
abstract public function __toString();
|
||||||
}
|
}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
disabled:3:1
|
||||||
|
disabled:3:7
|
||||||
error:6:7
|
error:6:7
|
||||||
|
disabled:13:1
|
||||||
|
disabled:13:7
|
||||||
|
disabled:23:1
|
||||||
|
|
|
@ -190,6 +190,7 @@ warning:9:3
|
||||||
error:28:3
|
error:28:3
|
||||||
error:30:3
|
error:30:3
|
||||||
error:36:3
|
error:36:3
|
||||||
|
disabled:40:1
|
||||||
error:42:5
|
error:42:5
|
||||||
error:43:7
|
error:43:7
|
||||||
error:44:5
|
error:44:5
|
||||||
|
@ -202,7 +203,9 @@ error:87:3 This stuff is basically testing the lexer/parser for function decls.
|
||||||
error:104:15 Variables in instance derefs should be checked, static should not.
|
error:104:15 Variables in instance derefs should be checked, static should not.
|
||||||
error:118:3 isset() and empty() should not trigger errors.
|
error:118:3 isset() and empty() should not trigger errors.
|
||||||
error:122:3 Should only warn once in this function.
|
error:122:3 Should only warn once in this function.
|
||||||
|
disabled:136:1
|
||||||
error:144:8
|
error:144:8
|
||||||
|
disabled:147:1
|
||||||
error:150:9
|
error:150:9
|
||||||
error:164:9
|
error:164:9
|
||||||
error:171:5
|
error:171:5
|
||||||
|
|
|
@ -5,5 +5,6 @@ final class Foo {
|
||||||
final public function baz() {}
|
final public function baz() {}
|
||||||
}
|
}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
disabled:3:1
|
||||||
error:3:13 XHP19
|
error:3:13 XHP19
|
||||||
advice:5:3
|
advice:5:3
|
||||||
|
|
|
@ -9,5 +9,6 @@ final class A {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
disabled:3:1
|
||||||
error:3:13 XHP19 Class-Filename Mismatch
|
error:3:13 XHP19 Class-Filename Mismatch
|
||||||
error:8:5 Use of $this in a static method.
|
error:8:5 Use of $this in a static method.
|
||||||
|
|
15
src/lint/linter/__tests__/xhpast/xsprintf.lint-test
Normal file
15
src/lint/linter/__tests__/xhpast/xsprintf.lint-test
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
csprintf('x');
|
||||||
|
csprintf($x);
|
||||||
|
~~~~~~~~~~
|
||||||
|
error:4:1
|
||||||
|
~~~~~~~~~~
|
||||||
|
~~~~~~~~~~
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"xhpast.dynamic-string.functions": {
|
||||||
|
"csprintf": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,8 +26,31 @@ final class ArcanistPhutilXHPASTLinterStandard
|
||||||
'The `%s` function should be avoided. It is potentially unsafe '.
|
'The `%s` function should be avoided. It is potentially unsafe '.
|
||||||
'and makes debugging more difficult.',
|
'and makes debugging more difficult.',
|
||||||
'eval'),
|
'eval'),
|
||||||
|
),
|
||||||
'xhpast.php-version' => '5.2.3',
|
'xhpast.php-version' => '5.2.3',
|
||||||
'xhpast.php-version.windows' => '5.3.0',
|
'xhpast.php-version.windows' => '5.3.0',
|
||||||
|
'xhpast.dynamic-string.classes' => array(
|
||||||
|
'ExecFuture' => 0,
|
||||||
|
),
|
||||||
|
'xhpast.dynamic-string.functions' => array(
|
||||||
|
'pht' => 0,
|
||||||
|
|
||||||
|
'hsprintf' => 0,
|
||||||
|
'jsprintf' => 0,
|
||||||
|
|
||||||
|
'hgsprintf' => 0,
|
||||||
|
|
||||||
|
'csprintf' => 0,
|
||||||
|
'vcsprintf' => 0,
|
||||||
|
'execx' => 0,
|
||||||
|
'exec_manual' => 0,
|
||||||
|
'phutil_passthru' => 0,
|
||||||
|
|
||||||
|
'qsprintf' => 1,
|
||||||
|
'vqsprintf' => 1,
|
||||||
|
'queryfx' => 1,
|
||||||
|
'queryfx_all' => 1,
|
||||||
|
'queryfx_one' => 1,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistArrayCombineXHPASTLinterRule
|
||||||
|
extends ArcanistXHPASTLinterRule {
|
||||||
|
|
||||||
|
const ID = 84;
|
||||||
|
|
||||||
|
public function getLintName() {
|
||||||
|
return pht('%s Unreliable', 'array_combine()');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLintSeverity() {
|
||||||
|
return ArcanistLintSeverity::SEVERITY_DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(XHPASTNode $root) {
|
||||||
|
$function_calls = $this->getFunctionCalls($root, array('array_combine'));
|
||||||
|
|
||||||
|
foreach ($function_calls as $call) {
|
||||||
|
$name = $call->getChildByIndex(0)->getConcreteString();
|
||||||
|
$parameter_list = $call->getChildOfType(1, 'n_CALL_PARAMETER_LIST');
|
||||||
|
|
||||||
|
if (count($parameter_list->getChildren()) !== 2) {
|
||||||
|
// Wrong number of parameters, but raise that elsewhere if we want.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first = $parameter_list->getChildByIndex(0);
|
||||||
|
$second = $parameter_list->getChildByIndex(1);
|
||||||
|
|
||||||
|
if ($first->getConcreteString() == $second->getConcreteString()) {
|
||||||
|
$this->raiseLintAtNode(
|
||||||
|
$call,
|
||||||
|
pht(
|
||||||
|
'Prior to PHP 5.4, `%s` fails when given empty arrays. '.
|
||||||
|
'Prefer to write `%s` as `%s`.',
|
||||||
|
'array_combine()',
|
||||||
|
'array_combine(x, x)',
|
||||||
|
'array_fuse(x)'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistClassExtendsObjectXHPASTLinterRule
|
||||||
|
extends ArcanistXHPASTLinterRule {
|
||||||
|
|
||||||
|
const ID = 88;
|
||||||
|
|
||||||
|
public function getLintName() {
|
||||||
|
return pht('Class Not Extending %s', 'Phobject');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLintSeverity() {
|
||||||
|
return ArcanistLintSeverity::SEVERITY_DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(XHPASTNode $root) {
|
||||||
|
$classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
|
||||||
|
|
||||||
|
foreach ($classes as $class) {
|
||||||
|
// TODO: This doesn't quite work for namespaced classes (see T8534).
|
||||||
|
$name = $class->getChildOfType(1, 'n_CLASS_NAME');
|
||||||
|
$extends = $class->getChildByIndex(2);
|
||||||
|
|
||||||
|
if ($name->getConcreteString() == 'Phobject') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($extends->getTypeName() == 'n_EMPTY') {
|
||||||
|
$this->raiseLintAtNode(
|
||||||
|
$class,
|
||||||
|
pht(
|
||||||
|
'Classes should extend from %s or from some other class. '.
|
||||||
|
'All classes (except for %s itself) should have a base class.',
|
||||||
|
'Phobject',
|
||||||
|
'Phobject'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistDeprecationXHPASTLinterRule
|
||||||
|
extends ArcanistXHPASTLinterRule {
|
||||||
|
|
||||||
|
const ID = 85;
|
||||||
|
|
||||||
|
private $deprecatedFunctions = array();
|
||||||
|
|
||||||
|
public function getLintName() {
|
||||||
|
return pht('Use of Deprecated Function');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLintSeverity() {
|
||||||
|
return ArcanistLintSeverity::SEVERITY_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLinterConfigurationOptions() {
|
||||||
|
return parent::getLinterConfigurationOptions() + array(
|
||||||
|
'xhpast.deprecated.functions' => array(
|
||||||
|
'type' => 'optional map<string, string>',
|
||||||
|
'help' => pht(
|
||||||
|
'Functions which should should be considered deprecated.'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLinterConfigurationValue($key, $value) {
|
||||||
|
switch ($key) {
|
||||||
|
case 'xhpast.deprecated.functions':
|
||||||
|
$this->deprecatedFunctions = $value;
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return parent::getLinterConfigurationOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(XHPASTNode $root) {
|
||||||
|
$map = $this->deprecatedFunctions;
|
||||||
|
$function_calls = $this->getFunctionCalls($root, array_keys($map));
|
||||||
|
|
||||||
|
foreach ($function_calls as $call) {
|
||||||
|
$name = $call
|
||||||
|
->getChildByIndex(0)
|
||||||
|
->getConcreteString();
|
||||||
|
|
||||||
|
$name = strtolower($name);
|
||||||
|
if (empty($map[$name])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->raiseLintAtNode($call, $map[$name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistRaggedClassTreeEdgeXHPASTLinterRule
|
||||||
|
extends ArcanistXHPASTLinterRule {
|
||||||
|
|
||||||
|
const ID = 87;
|
||||||
|
|
||||||
|
public function getLintName() {
|
||||||
|
return pht('Class Not %s Or %s', 'abstract', 'final');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLintSeverity() {
|
||||||
|
return ArcanistLintSeverity::SEVERITY_DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(XHPASTNode $root) {
|
||||||
|
$parser = new PhutilDocblockParser();
|
||||||
|
|
||||||
|
$classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
|
||||||
|
foreach ($classes as $class) {
|
||||||
|
$is_final = false;
|
||||||
|
$is_abstract = false;
|
||||||
|
$is_concrete_extensible = false;
|
||||||
|
|
||||||
|
$attributes = $class->getChildOfType(0, 'n_CLASS_ATTRIBUTES');
|
||||||
|
foreach ($attributes->getChildren() as $child) {
|
||||||
|
if ($child->getConcreteString() == 'final') {
|
||||||
|
$is_final = true;
|
||||||
|
}
|
||||||
|
if ($child->getConcreteString() == 'abstract') {
|
||||||
|
$is_abstract = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$docblock = $class->getDocblockToken();
|
||||||
|
if ($docblock) {
|
||||||
|
list($text, $specials) = $parser->parse($docblock->getValue());
|
||||||
|
$is_concrete_extensible = idx($specials, 'concrete-extensible');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$is_final && !$is_abstract && !$is_concrete_extensible) {
|
||||||
|
$this->raiseLintAtNode(
|
||||||
|
$class->getChildOfType(1, 'n_CLASS_NAME'),
|
||||||
|
pht(
|
||||||
|
"This class is neither '%s' nor '%s', and does not have ".
|
||||||
|
"a docblock marking it '%s'.",
|
||||||
|
'final',
|
||||||
|
'abstract',
|
||||||
|
'@concrete-extensible'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistUnsafeDynamicStringXHPASTLinterRule
|
||||||
|
extends ArcanistXHPASTLinterRule {
|
||||||
|
|
||||||
|
const ID = 86;
|
||||||
|
|
||||||
|
private $dynamicStringFunctions = array();
|
||||||
|
private $dynamicStringClasses = array();
|
||||||
|
|
||||||
|
public function getLintName() {
|
||||||
|
return pht('Unsafe Usage of Dynamic String');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLintSeverity() {
|
||||||
|
return ArcanistLintSeverity::SEVERITY_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLinterConfigurationOptions() {
|
||||||
|
$options = array(
|
||||||
|
'xhpast.dynamic-string.classes' => array(
|
||||||
|
'type' => 'optional map<string, string>',
|
||||||
|
'help' => pht(
|
||||||
|
'Classes which should should not be used because they represent the '.
|
||||||
|
'unsafe usage of dynamic strings.'),
|
||||||
|
),
|
||||||
|
'xhpast.dynamic-string.functions' => array(
|
||||||
|
'type' => 'optional map<string, string>',
|
||||||
|
'help' => pht(
|
||||||
|
'Functions which should should not be used because they represent '.
|
||||||
|
'the unsafe usage of dynamic strings.'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $options + parent::getLinterConfigurationOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLinterConfigurationValue($key, $value) {
|
||||||
|
switch ($key) {
|
||||||
|
case 'xhpast.dynamic-string.classes':
|
||||||
|
$this->dynamicStringClasses = $value;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 'xhpast.dynamic-string.functions':
|
||||||
|
$this->dynamicStringFunctions = $value;
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
parent::setLinterConfigurationValue($key, $value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(XHPASTNode $root) {
|
||||||
|
$this->lintUnsafeDynamicStringClasses($root);
|
||||||
|
$this->lintUnsafeDynamicStringFunctions($root);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function lintUnsafeDynamicStringClasses(XHPASTNode $root) {
|
||||||
|
$news = $root->selectDescendantsOfType('n_NEW');
|
||||||
|
$this->lintUnsafeDynamicStringCall($news, $this->dynamicStringClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function lintUnsafeDynamicStringFunctions(XHPASTNode $root) {
|
||||||
|
$calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
|
||||||
|
$this->lintUnsafeDynamicStringCall($calls, $this->dynamicStringFunctions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function lintUnsafeDynamicStringCall(
|
||||||
|
AASTNodeList $calls,
|
||||||
|
array $safe) {
|
||||||
|
|
||||||
|
$safe = array_combine(
|
||||||
|
array_map('strtolower', array_keys($safe)),
|
||||||
|
$safe);
|
||||||
|
|
||||||
|
foreach ($calls as $call) {
|
||||||
|
$name = $call->getChildByIndex(0)->getConcreteString();
|
||||||
|
$param = idx($safe, strtolower($name));
|
||||||
|
|
||||||
|
if ($param === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parameters = $call->getChildByIndex(1);
|
||||||
|
if (count($parameters->getChildren()) <= $param) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$identifier = $parameters->getChildByIndex($param);
|
||||||
|
if (!$identifier->isConstantString()) {
|
||||||
|
$this->raiseLintAtNode(
|
||||||
|
$call,
|
||||||
|
pht(
|
||||||
|
"Parameter %d of %s should be a scalar string, ".
|
||||||
|
"otherwise it's not safe.",
|
||||||
|
$param + 1,
|
||||||
|
$name.'()'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue