mirror of
https://we.phorge.it/source/arcanist.git
synced 2025-01-15 17:21:09 +01:00
[Wilds] Remove all linters and test cases not used by Arcanist
Summary: Ref T13098. I may restore some of these later, but this was a big obvious step in getting `arc unit` passing. I'd generally like these to live in third party libraries some day, if not immediately. Test Plan: No failing tests after D19716. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13098 Differential Revision: https://secure.phabricator.com/D19717
This commit is contained in:
parent
22cf774ae1
commit
529b21844b
194 changed files with 2 additions and 8101 deletions
|
@ -97,9 +97,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistBuildRef' => 'ref/ArcanistBuildRef.php',
|
||||
'ArcanistBundle' => 'parser/ArcanistBundle.php',
|
||||
'ArcanistBundleTestCase' => 'parser/__tests__/ArcanistBundleTestCase.php',
|
||||
'ArcanistCSSLintLinter' => 'lint/linter/ArcanistCSSLintLinter.php',
|
||||
'ArcanistCSSLintLinterTestCase' => 'lint/linter/__tests__/ArcanistCSSLintLinterTestCase.php',
|
||||
'ArcanistCSharpLinter' => 'lint/linter/ArcanistCSharpLinter.php',
|
||||
'ArcanistCallConduitWorkflow' => 'workflow/ArcanistCallConduitWorkflow.php',
|
||||
'ArcanistCallParenthesesXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistCallParenthesesXHPASTLinterRule.php',
|
||||
'ArcanistCallParenthesesXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistCallParenthesesXHPASTLinterRuleTestCase.php',
|
||||
|
@ -120,10 +117,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistClassNameLiteralXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistClassNameLiteralXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistCloseRevisionWorkflow' => 'workflow/ArcanistCloseRevisionWorkflow.php',
|
||||
'ArcanistCloseWorkflow' => 'workflow/ArcanistCloseWorkflow.php',
|
||||
'ArcanistClosureLinter' => 'lint/linter/ArcanistClosureLinter.php',
|
||||
'ArcanistClosureLinterTestCase' => 'lint/linter/__tests__/ArcanistClosureLinterTestCase.php',
|
||||
'ArcanistCoffeeLintLinter' => 'lint/linter/ArcanistCoffeeLintLinter.php',
|
||||
'ArcanistCoffeeLintLinterTestCase' => 'lint/linter/__tests__/ArcanistCoffeeLintLinterTestCase.php',
|
||||
'ArcanistCommentRemover' => 'parser/ArcanistCommentRemover.php',
|
||||
'ArcanistCommentRemoverTestCase' => 'parser/__tests__/ArcanistCommentRemoverTestCase.php',
|
||||
'ArcanistCommentSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistCommentSpacingXHPASTLinterRule.php',
|
||||
|
@ -133,7 +126,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistCommitUpstreamHardpointLoader' => 'loader/ArcanistCommitUpstreamHardpointLoader.php',
|
||||
'ArcanistCommitWorkflow' => 'workflow/ArcanistCommitWorkflow.php',
|
||||
'ArcanistCompilerLintRenderer' => 'lint/renderer/ArcanistCompilerLintRenderer.php',
|
||||
'ArcanistComposerLinter' => 'lint/linter/ArcanistComposerLinter.php',
|
||||
'ArcanistComprehensiveLintEngine' => 'lint/engine/ArcanistComprehensiveLintEngine.php',
|
||||
'ArcanistConcatenationOperatorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistConcatenationOperatorXHPASTLinterRule.php',
|
||||
'ArcanistConcatenationOperatorXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistConcatenationOperatorXHPASTLinterRuleTestCase.php',
|
||||
|
@ -155,10 +147,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistControlStatementSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistControlStatementSpacingXHPASTLinterRule.php',
|
||||
'ArcanistControlStatementSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistControlStatementSpacingXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistCoverWorkflow' => 'workflow/ArcanistCoverWorkflow.php',
|
||||
'ArcanistCppcheckLinter' => 'lint/linter/ArcanistCppcheckLinter.php',
|
||||
'ArcanistCppcheckLinterTestCase' => 'lint/linter/__tests__/ArcanistCppcheckLinterTestCase.php',
|
||||
'ArcanistCpplintLinter' => 'lint/linter/ArcanistCpplintLinter.php',
|
||||
'ArcanistCpplintLinterTestCase' => 'lint/linter/__tests__/ArcanistCpplintLinterTestCase.php',
|
||||
'ArcanistCurlyBraceArrayIndexXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistCurlyBraceArrayIndexXHPASTLinterRule.php',
|
||||
'ArcanistCurlyBraceArrayIndexXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistCurlyBraceArrayIndexXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistDeclarationParenthesesXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDeclarationParenthesesXHPASTLinterRule.php',
|
||||
|
@ -202,8 +190,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistExitExpressionXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistExitExpressionXHPASTLinterRule.php',
|
||||
'ArcanistExitExpressionXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistExitExpressionXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistExportWorkflow' => 'workflow/ArcanistExportWorkflow.php',
|
||||
'ArcanistExternalLinter' => 'lint/linter/ArcanistExternalLinter.php',
|
||||
'ArcanistExternalLinterTestCase' => 'lint/linter/__tests__/ArcanistExternalLinterTestCase.php',
|
||||
'ArcanistExtractUseXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistExtractUseXHPASTLinterRule.php',
|
||||
'ArcanistExtractUseXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistExtractUseXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistFeatureWorkflow' => 'workflow/ArcanistFeatureWorkflow.php',
|
||||
|
@ -214,13 +200,10 @@ phutil_register_library_map(array(
|
|||
'ArcanistFilenameLinterTestCase' => 'lint/linter/__tests__/ArcanistFilenameLinterTestCase.php',
|
||||
'ArcanistFilesystemConfigurationSource' => 'config/source/ArcanistFilesystemConfigurationSource.php',
|
||||
'ArcanistFlagWorkflow' => 'workflow/ArcanistFlagWorkflow.php',
|
||||
'ArcanistFlake8Linter' => 'lint/linter/ArcanistFlake8Linter.php',
|
||||
'ArcanistFlake8LinterTestCase' => 'lint/linter/__tests__/ArcanistFlake8LinterTestCase.php',
|
||||
'ArcanistFormattedStringXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistFormattedStringXHPASTLinterRule.php',
|
||||
'ArcanistFormattedStringXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistFormattedStringXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRule.php',
|
||||
'ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistFutureLinter' => 'lint/linter/ArcanistFutureLinter.php',
|
||||
'ArcanistGeneratedLinter' => 'lint/linter/ArcanistGeneratedLinter.php',
|
||||
'ArcanistGeneratedLinterTestCase' => 'lint/linter/__tests__/ArcanistGeneratedLinterTestCase.php',
|
||||
'ArcanistGetConfigWorkflow' => 'toolset/workflow/ArcanistGetConfigWorkflow.php',
|
||||
|
@ -233,12 +216,8 @@ phutil_register_library_map(array(
|
|||
'ArcanistGitWorkingCopy' => 'workingcopy/ArcanistGitWorkingCopy.php',
|
||||
'ArcanistGlobalVariableXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistGlobalVariableXHPASTLinterRule.php',
|
||||
'ArcanistGlobalVariableXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistGlobalVariableXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistGoLintLinter' => 'lint/linter/ArcanistGoLintLinter.php',
|
||||
'ArcanistGoLintLinterTestCase' => 'lint/linter/__tests__/ArcanistGoLintLinterTestCase.php',
|
||||
'ArcanistGoTestResultParser' => 'unit/parser/ArcanistGoTestResultParser.php',
|
||||
'ArcanistGoTestResultParserTestCase' => 'unit/parser/__tests__/ArcanistGoTestResultParserTestCase.php',
|
||||
'ArcanistHLintLinter' => 'lint/linter/ArcanistHLintLinter.php',
|
||||
'ArcanistHLintLinterTestCase' => 'lint/linter/__tests__/ArcanistHLintLinterTestCase.php',
|
||||
'ArcanistHardpointLoader' => 'loader/ArcanistHardpointLoader.php',
|
||||
'ArcanistHelpWorkflow' => 'toolset/workflow/ArcanistHelpWorkflow.php',
|
||||
'ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule.php',
|
||||
|
@ -253,7 +232,7 @@ phutil_register_library_map(array(
|
|||
'ArcanistImplicitFallthroughXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistImplicitFallthroughXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistImplicitVisibilityXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistImplicitVisibilityXHPASTLinterRule.php',
|
||||
'ArcanistImplicitVisibilityXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistImplicitVisibilityXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistInlineHTMLXHPASTLinterRule' => 'lint/linter/ArcanistInlineHTMLXHPASTLinterRule.php',
|
||||
'ArcanistInlineHTMLXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistInlineHTMLXHPASTLinterRule.php',
|
||||
'ArcanistInlineHTMLXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistInlineHTMLXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistInnerFunctionXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistInnerFunctionXHPASTLinterRule.php',
|
||||
'ArcanistInnerFunctionXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistInnerFunctionXHPASTLinterRuleTestCase.php',
|
||||
|
@ -272,16 +251,10 @@ phutil_register_library_map(array(
|
|||
'ArcanistInvalidOctalNumericScalarXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistInvalidOctalNumericScalarXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistIsAShouldBeInstanceOfXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistIsAShouldBeInstanceOfXHPASTLinterRule.php',
|
||||
'ArcanistIsAShouldBeInstanceOfXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistIsAShouldBeInstanceOfXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistJSHintLinter' => 'lint/linter/ArcanistJSHintLinter.php',
|
||||
'ArcanistJSHintLinterTestCase' => 'lint/linter/__tests__/ArcanistJSHintLinterTestCase.php',
|
||||
'ArcanistJSONLintLinter' => 'lint/linter/ArcanistJSONLintLinter.php',
|
||||
'ArcanistJSONLintLinterTestCase' => 'lint/linter/__tests__/ArcanistJSONLintLinterTestCase.php',
|
||||
'ArcanistJSONLintRenderer' => 'lint/renderer/ArcanistJSONLintRenderer.php',
|
||||
'ArcanistJSONLinter' => 'lint/linter/ArcanistJSONLinter.php',
|
||||
'ArcanistJSONLinterTestCase' => 'lint/linter/__tests__/ArcanistJSONLinterTestCase.php',
|
||||
'ArcanistJSONUnitSink' => 'unit/sink/ArcanistJSONUnitSink.php',
|
||||
'ArcanistJscsLinter' => 'lint/linter/ArcanistJscsLinter.php',
|
||||
'ArcanistJscsLinterTestCase' => 'lint/linter/__tests__/ArcanistJscsLinterTestCase.php',
|
||||
'ArcanistKeywordCasingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistKeywordCasingXHPASTLinterRule.php',
|
||||
'ArcanistKeywordCasingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistKeywordCasingXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistLambdaFuncFunctionXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistLambdaFuncFunctionXHPASTLinterRule.php',
|
||||
|
@ -290,8 +263,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistLandWorkflow' => 'workflow/ArcanistLandWorkflow.php',
|
||||
'ArcanistLanguageConstructParenthesesXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistLanguageConstructParenthesesXHPASTLinterRule.php',
|
||||
'ArcanistLanguageConstructParenthesesXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistLanguageConstructParenthesesXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistLesscLinter' => 'lint/linter/ArcanistLesscLinter.php',
|
||||
'ArcanistLesscLinterTestCase' => 'lint/linter/__tests__/ArcanistLesscLinterTestCase.php',
|
||||
'ArcanistLiberateWorkflow' => 'workflow/ArcanistLiberateWorkflow.php',
|
||||
'ArcanistLintEngine' => 'lint/engine/ArcanistLintEngine.php',
|
||||
'ArcanistLintMessage' => 'lint/ArcanistLintMessage.php',
|
||||
|
@ -348,8 +319,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistNoneLintRenderer' => 'lint/renderer/ArcanistNoneLintRenderer.php',
|
||||
'ArcanistObjectOperatorSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistObjectOperatorSpacingXHPASTLinterRule.php',
|
||||
'ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistPEP8Linter' => 'lint/linter/ArcanistPEP8Linter.php',
|
||||
'ArcanistPEP8LinterTestCase' => 'lint/linter/__tests__/ArcanistPEP8LinterTestCase.php',
|
||||
'ArcanistPHPCloseTagXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPHPCloseTagXHPASTLinterRule.php',
|
||||
'ArcanistPHPCloseTagXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistPHPCloseTagXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistPHPCompatibilityXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPHPCompatibilityXHPASTLinterRule.php',
|
||||
|
@ -371,10 +340,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistPasteWorkflow' => 'workflow/ArcanistPasteWorkflow.php',
|
||||
'ArcanistPatchWorkflow' => 'workflow/ArcanistPatchWorkflow.php',
|
||||
'ArcanistPhageToolset' => 'toolset/ArcanistPhageToolset.php',
|
||||
'ArcanistPhpLinter' => 'lint/linter/ArcanistPhpLinter.php',
|
||||
'ArcanistPhpLinterTestCase' => 'lint/linter/__tests__/ArcanistPhpLinterTestCase.php',
|
||||
'ArcanistPhpcsLinter' => 'lint/linter/ArcanistPhpcsLinter.php',
|
||||
'ArcanistPhpcsLinterTestCase' => 'lint/linter/__tests__/ArcanistPhpcsLinterTestCase.php',
|
||||
'ArcanistPhpunitTestResultParser' => 'unit/parser/ArcanistPhpunitTestResultParser.php',
|
||||
'ArcanistPhrequentWorkflow' => 'workflow/ArcanistPhrequentWorkflow.php',
|
||||
'ArcanistPhutilLibraryLinter' => 'lint/linter/ArcanistPhutilLibraryLinter.php',
|
||||
|
@ -389,12 +354,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistPromptsWorkflow' => 'toolset/workflow/ArcanistPromptsWorkflow.php',
|
||||
'ArcanistPublicPropertyXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPublicPropertyXHPASTLinterRule.php',
|
||||
'ArcanistPublicPropertyXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistPublicPropertyXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistPuppetLintLinter' => 'lint/linter/ArcanistPuppetLintLinter.php',
|
||||
'ArcanistPuppetLintLinterTestCase' => 'lint/linter/__tests__/ArcanistPuppetLintLinterTestCase.php',
|
||||
'ArcanistPyFlakesLinter' => 'lint/linter/ArcanistPyFlakesLinter.php',
|
||||
'ArcanistPyFlakesLinterTestCase' => 'lint/linter/__tests__/ArcanistPyFlakesLinterTestCase.php',
|
||||
'ArcanistPyLintLinter' => 'lint/linter/ArcanistPyLintLinter.php',
|
||||
'ArcanistPyLintLinterTestCase' => 'lint/linter/__tests__/ArcanistPyLintLinterTestCase.php',
|
||||
'ArcanistRaggedClassTreeEdgeXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistRaggedClassTreeEdgeXHPASTLinterRule.php',
|
||||
'ArcanistRaggedClassTreeEdgeXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistRaggedClassTreeEdgeXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistRef' => 'ref/ArcanistRef.php',
|
||||
|
@ -412,13 +371,8 @@ phutil_register_library_map(array(
|
|||
'ArcanistRevertWorkflow' => 'workflow/ArcanistRevertWorkflow.php',
|
||||
'ArcanistRevisionRef' => 'ref/ArcanistRevisionRef.php',
|
||||
'ArcanistRevisionRefSource' => 'ref/ArcanistRevisionRefSource.php',
|
||||
'ArcanistRuboCopLinter' => 'lint/linter/ArcanistRuboCopLinter.php',
|
||||
'ArcanistRuboCopLinterTestCase' => 'lint/linter/__tests__/ArcanistRuboCopLinterTestCase.php',
|
||||
'ArcanistRubyLinter' => 'lint/linter/ArcanistRubyLinter.php',
|
||||
'ArcanistRubyLinterTestCase' => 'lint/linter/__tests__/ArcanistRubyLinterTestCase.php',
|
||||
'ArcanistRuntimeConfigurationSource' => 'config/source/ArcanistRuntimeConfigurationSource.php',
|
||||
'ArcanistScalarConfigOption' => 'config/option/ArcanistScalarConfigOption.php',
|
||||
'ArcanistScriptAndRegexLinter' => 'lint/linter/ArcanistScriptAndRegexLinter.php',
|
||||
'ArcanistSelfClassReferenceXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistSelfClassReferenceXHPASTLinterRule.php',
|
||||
'ArcanistSelfClassReferenceXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistSelfClassReferenceXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistSelfMemberReferenceXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistSelfMemberReferenceXHPASTLinterRule.php',
|
||||
|
@ -516,8 +470,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistXHPASTLinterRule' => 'lint/linter/xhpast/ArcanistXHPASTLinterRule.php',
|
||||
'ArcanistXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistXHPASTLinterTestCase' => 'lint/linter/__tests__/ArcanistXHPASTLinterTestCase.php',
|
||||
'ArcanistXMLLinter' => 'lint/linter/ArcanistXMLLinter.php',
|
||||
'ArcanistXMLLinterTestCase' => 'lint/linter/__tests__/ArcanistXMLLinterTestCase.php',
|
||||
'ArcanistXUnitTestResultParser' => 'unit/parser/ArcanistXUnitTestResultParser.php',
|
||||
'BaseHTTPFuture' => 'future/http/BaseHTTPFuture.php',
|
||||
'CaseInsensitiveArray' => 'utils/CaseInsensitiveArray.php',
|
||||
|
@ -1201,9 +1153,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistBuildRef' => 'Phobject',
|
||||
'ArcanistBundle' => 'Phobject',
|
||||
'ArcanistBundleTestCase' => 'PhutilTestCase',
|
||||
'ArcanistCSSLintLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistCSSLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistCSharpLinter' => 'ArcanistLinter',
|
||||
'ArcanistCallConduitWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistCallParenthesesXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistCallParenthesesXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
|
@ -1224,10 +1173,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistClassNameLiteralXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistCloseRevisionWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistCloseWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistClosureLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistClosureLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistCoffeeLintLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistCoffeeLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistCommentRemover' => 'Phobject',
|
||||
'ArcanistCommentRemoverTestCase' => 'PhutilTestCase',
|
||||
'ArcanistCommentSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
|
@ -1237,7 +1182,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistCommitUpstreamHardpointLoader' => 'ArcanistHardpointLoader',
|
||||
'ArcanistCommitWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistCompilerLintRenderer' => 'ArcanistLintRenderer',
|
||||
'ArcanistComposerLinter' => 'ArcanistLinter',
|
||||
'ArcanistComprehensiveLintEngine' => 'ArcanistLintEngine',
|
||||
'ArcanistConcatenationOperatorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistConcatenationOperatorXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
|
@ -1259,10 +1203,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistControlStatementSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistControlStatementSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistCoverWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistCppcheckLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistCppcheckLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistCpplintLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistCpplintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistCurlyBraceArrayIndexXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistCurlyBraceArrayIndexXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistDeclarationParenthesesXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
|
@ -1306,8 +1246,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistExitExpressionXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistExitExpressionXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistExportWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistExternalLinter' => 'ArcanistFutureLinter',
|
||||
'ArcanistExternalLinterTestCase' => 'ArcanistLinterTestCase',
|
||||
'ArcanistExtractUseXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistExtractUseXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistFeatureWorkflow' => 'ArcanistWorkflow',
|
||||
|
@ -1318,13 +1256,10 @@ phutil_register_library_map(array(
|
|||
'ArcanistFilenameLinterTestCase' => 'ArcanistLinterTestCase',
|
||||
'ArcanistFilesystemConfigurationSource' => 'ArcanistDictionaryConfigurationSource',
|
||||
'ArcanistFlagWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistFlake8Linter' => 'ArcanistExternalLinter',
|
||||
'ArcanistFlake8LinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistFormattedStringXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistFormattedStringXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistFunctionCallShouldBeTypeCastXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistFutureLinter' => 'ArcanistLinter',
|
||||
'ArcanistGeneratedLinter' => 'ArcanistLinter',
|
||||
'ArcanistGeneratedLinterTestCase' => 'ArcanistLinterTestCase',
|
||||
'ArcanistGetConfigWorkflow' => 'ArcanistWorkflow',
|
||||
|
@ -1337,12 +1272,8 @@ phutil_register_library_map(array(
|
|||
'ArcanistGitWorkingCopy' => 'ArcanistWorkingCopy',
|
||||
'ArcanistGlobalVariableXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistGlobalVariableXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistGoLintLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistGoLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistGoTestResultParser' => 'ArcanistTestResultParser',
|
||||
'ArcanistGoTestResultParserTestCase' => 'PhutilTestCase',
|
||||
'ArcanistHLintLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistHLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistHardpointLoader' => 'Phobject',
|
||||
'ArcanistHelpWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
|
@ -1376,16 +1307,10 @@ phutil_register_library_map(array(
|
|||
'ArcanistInvalidOctalNumericScalarXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistIsAShouldBeInstanceOfXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistIsAShouldBeInstanceOfXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistJSHintLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistJSHintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistJSONLintLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistJSONLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistJSONLintRenderer' => 'ArcanistLintRenderer',
|
||||
'ArcanistJSONLinter' => 'ArcanistLinter',
|
||||
'ArcanistJSONLinterTestCase' => 'ArcanistLinterTestCase',
|
||||
'ArcanistJSONUnitSink' => 'ArcanistUnitSink',
|
||||
'ArcanistJscsLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistJscsLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistKeywordCasingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistKeywordCasingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistLambdaFuncFunctionXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
|
@ -1394,8 +1319,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistLandWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistLanguageConstructParenthesesXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistLanguageConstructParenthesesXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistLesscLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistLesscLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistLiberateWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistLintEngine' => 'Phobject',
|
||||
'ArcanistLintMessage' => 'Phobject',
|
||||
|
@ -1452,8 +1375,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistNoneLintRenderer' => 'ArcanistLintRenderer',
|
||||
'ArcanistObjectOperatorSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistPEP8Linter' => 'ArcanistExternalLinter',
|
||||
'ArcanistPEP8LinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistPHPCloseTagXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistPHPCloseTagXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistPHPCompatibilityXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
|
@ -1475,10 +1396,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistPasteWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistPatchWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistPhageToolset' => 'ArcanistToolset',
|
||||
'ArcanistPhpLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistPhpLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistPhpcsLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistPhpcsLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistPhpunitTestResultParser' => 'ArcanistTestResultParser',
|
||||
'ArcanistPhrequentWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistPhutilLibraryLinter' => 'ArcanistLinter',
|
||||
|
@ -1493,12 +1410,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistPromptsWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistPublicPropertyXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistPublicPropertyXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistPuppetLintLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistPuppetLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistPyFlakesLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistPyFlakesLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistPyLintLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistPyLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistRaggedClassTreeEdgeXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistRaggedClassTreeEdgeXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistRef' => 'Phobject',
|
||||
|
@ -1516,13 +1427,8 @@ phutil_register_library_map(array(
|
|||
'ArcanistRevertWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistRevisionRef' => 'ArcanistRef',
|
||||
'ArcanistRevisionRefSource' => 'Phobject',
|
||||
'ArcanistRuboCopLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistRuboCopLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistRubyLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistRubyLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistRuntimeConfigurationSource' => 'ArcanistDictionaryConfigurationSource',
|
||||
'ArcanistScalarConfigOption' => 'ArcanistConfigOption',
|
||||
'ArcanistScriptAndRegexLinter' => 'ArcanistLinter',
|
||||
'ArcanistSelfClassReferenceXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistSelfClassReferenceXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistSelfMemberReferenceXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
|
@ -1620,8 +1526,6 @@ phutil_register_library_map(array(
|
|||
'ArcanistXHPASTLinterRule' => 'Phobject',
|
||||
'ArcanistXHPASTLinterRuleTestCase' => 'ArcanistLinterTestCase',
|
||||
'ArcanistXHPASTLinterTestCase' => 'ArcanistLinterTestCase',
|
||||
'ArcanistXMLLinter' => 'ArcanistLinter',
|
||||
'ArcanistXMLLinterTestCase' => 'ArcanistLinterTestCase',
|
||||
'ArcanistXUnitTestResultParser' => 'Phobject',
|
||||
'BaseHTTPFuture' => 'Future',
|
||||
'CaseInsensitiveArray' => 'PhutilArray',
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Uses "CSS Lint" to detect checkstyle errors in CSS code.
|
||||
*/
|
||||
final class ArcanistCSSLintLinter extends ArcanistExternalLinter {
|
||||
|
||||
public function getInfoName() {
|
||||
return 'CSSLint';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'http://csslint.net';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'Use `%s` to detect issues with CSS source files.',
|
||||
'csslint');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'CSSLint';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'csslint';
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
return array(
|
||||
'--format=lint-xml',
|
||||
);
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'csslint';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
if (preg_match('/^v(?P<version>\d+\.\d+\.\d+)$/', $stdout, $matches)) {
|
||||
return $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht(
|
||||
'Install %s using `%s`.', 'CSSLint',
|
||||
'npm install -g csslint');
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$report_dom = new DOMDocument();
|
||||
$ok = @$report_dom->loadXML($stdout);
|
||||
|
||||
if (!$ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$files = $report_dom->getElementsByTagName('file');
|
||||
$messages = array();
|
||||
|
||||
foreach ($files as $file) {
|
||||
foreach ($file->childNodes as $child) {
|
||||
$line = $child->getAttribute('line');
|
||||
$char = $child->getAttribute('char');
|
||||
$original_text = $child->getAttribute('evidence');
|
||||
|
||||
if ($line === '') {
|
||||
$line = null;
|
||||
}
|
||||
|
||||
if ($char === '') {
|
||||
$char = null;
|
||||
} else {
|
||||
$original_text = substr($original_text, $char - 1);
|
||||
}
|
||||
|
||||
$message = id(new ArcanistLintMessage())
|
||||
->setPath($path)
|
||||
->setLine($line)
|
||||
->setChar($char)
|
||||
->setCode($this->getLinterName())
|
||||
->setName($this->getLinterName())
|
||||
->setDescription($child->getAttribute('reason'))
|
||||
->setOriginalText($original_text);
|
||||
|
||||
switch ($child->getAttribute('severity')) {
|
||||
case 'error':
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
|
||||
break;
|
||||
|
||||
case 'warning':
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
|
||||
break;
|
||||
|
||||
default:
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
protected function getLintCodeFromLinterConfigurationKey($code) {
|
||||
// NOTE: We can't figure out which rule generated each message, so we
|
||||
// can not customize severities. I opened a pull request to add this
|
||||
// ability; see:
|
||||
//
|
||||
// https://github.com/stubbornella/csslint/pull/409
|
||||
throw new Exception(
|
||||
pht(
|
||||
"%s does not currently support custom severity levels, because ".
|
||||
"rules can't be identified from messages in output.",
|
||||
'CSSLint'));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,261 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* C# linter for Arcanist.
|
||||
*/
|
||||
final class ArcanistCSharpLinter extends ArcanistLinter {
|
||||
|
||||
private $runtimeEngine;
|
||||
private $cslintEngine;
|
||||
private $cslintHintPath;
|
||||
private $loaded;
|
||||
private $discoveryMap;
|
||||
private $futures;
|
||||
|
||||
const SUPPORTED_VERSION = 1;
|
||||
|
||||
public function getLinterName() {
|
||||
return 'C#';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'csharp';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationOptions() {
|
||||
$options = parent::getLinterConfigurationOptions();
|
||||
|
||||
$options['discovery'] = array(
|
||||
'type' => 'map<string, list<string>>',
|
||||
'help' => pht('Provide a discovery map.'),
|
||||
);
|
||||
|
||||
|
||||
// TODO: This should probably be replaced with "bin" when this moves
|
||||
// to extend ExternalLinter.
|
||||
|
||||
$options['binary'] = array(
|
||||
'type' => 'string',
|
||||
'help' => pht('Override default binary.'),
|
||||
);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function setLinterConfigurationValue($key, $value) {
|
||||
switch ($key) {
|
||||
case 'discovery':
|
||||
$this->discoveryMap = $value;
|
||||
return;
|
||||
case 'binary':
|
||||
$this->cslintHintPath = $value;
|
||||
return;
|
||||
}
|
||||
parent::setLinterConfigurationValue($key, $value);
|
||||
}
|
||||
|
||||
protected function getLintCodeFromLinterConfigurationKey($code) {
|
||||
return $code;
|
||||
}
|
||||
|
||||
public function setCustomSeverityMap(array $map) {
|
||||
foreach ($map as $code => $severity) {
|
||||
if (substr($code, 0, 2) === 'SA' && $severity == 'disabled') {
|
||||
throw new Exception(
|
||||
pht(
|
||||
"In order to keep StyleCop integration with IDEs and other tools ".
|
||||
"consistent with Arcanist results, you aren't permitted to ".
|
||||
"disable StyleCop rules within '%s'. Instead configure the ".
|
||||
"severity using the StyleCop settings dialog (usually accessible ".
|
||||
"from within your IDE). StyleCop settings for your project will ".
|
||||
"be used when linting for Arcanist.",
|
||||
'.arclint'));
|
||||
}
|
||||
}
|
||||
return parent::setCustomSeverityMap($map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines what executables and lint paths to use. Between platforms
|
||||
* this also changes whether the lint engine is run under .NET or Mono. It
|
||||
* also ensures that all of the required binaries are available for the lint
|
||||
* to run successfully.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function loadEnvironment() {
|
||||
if ($this->loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine runtime engine (.NET or Mono).
|
||||
if (phutil_is_windows()) {
|
||||
$this->runtimeEngine = '';
|
||||
} else if (Filesystem::binaryExists('mono')) {
|
||||
$this->runtimeEngine = 'mono ';
|
||||
} else {
|
||||
throw new Exception(
|
||||
pht('Unable to find Mono and you are not on Windows!'));
|
||||
}
|
||||
|
||||
// Determine cslint path.
|
||||
$cslint = $this->cslintHintPath;
|
||||
if ($cslint !== null && file_exists($cslint)) {
|
||||
$this->cslintEngine = Filesystem::resolvePath($cslint);
|
||||
} else if (Filesystem::binaryExists('cslint.exe')) {
|
||||
$this->cslintEngine = 'cslint.exe';
|
||||
} else {
|
||||
throw new Exception(pht('Unable to locate %s.', 'cslint'));
|
||||
}
|
||||
|
||||
// Determine cslint version.
|
||||
$ver_future = new ExecFuture(
|
||||
'%C -v',
|
||||
$this->runtimeEngine.$this->cslintEngine);
|
||||
list($err, $stdout, $stderr) = $ver_future->resolve();
|
||||
if ($err !== 0) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You are running an old version of %s. Please '.
|
||||
'upgrade to version %s.',
|
||||
'cslint',
|
||||
self::SUPPORTED_VERSION));
|
||||
}
|
||||
$ver = (int)$stdout;
|
||||
if ($ver < self::SUPPORTED_VERSION) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You are running an old version of %s. Please '.
|
||||
'upgrade to version %s.',
|
||||
'cslint',
|
||||
self::SUPPORTED_VERSION));
|
||||
} else if ($ver > self::SUPPORTED_VERSION) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Arcanist does not support this version of %s (it is newer). '.
|
||||
'You can try upgrading Arcanist with `%s`.',
|
||||
'cslint',
|
||||
'arc upgrade'));
|
||||
}
|
||||
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
public function lintPath($path) {}
|
||||
|
||||
public function willLintPaths(array $paths) {
|
||||
$this->loadEnvironment();
|
||||
|
||||
$futures = array();
|
||||
|
||||
// Bulk linting up into futures, where the number of files
|
||||
// is based on how long the command is.
|
||||
$current_paths = array();
|
||||
foreach ($paths as $path) {
|
||||
// If the current paths for the command, plus the next path
|
||||
// is greater than 6000 characters (less than the Windows
|
||||
// command line limit), then finalize this future and add it.
|
||||
$total = 0;
|
||||
foreach ($current_paths as $current_path) {
|
||||
$total += strlen($current_path) + 3; // Quotes and space.
|
||||
}
|
||||
if ($total + strlen($path) > 6000) {
|
||||
// %s won't pass through the JSON correctly
|
||||
// under Windows. This is probably because not only
|
||||
// does the JSON have quotation marks in the content,
|
||||
// but because there'll be a lot of escaping and
|
||||
// double escaping because the JSON also contains
|
||||
// regular expressions. cslint supports passing the
|
||||
// settings JSON through base64-encoded to mitigate
|
||||
// this issue.
|
||||
$futures[] = new ExecFuture(
|
||||
'%C --settings-base64=%s -r=. %Ls',
|
||||
$this->runtimeEngine.$this->cslintEngine,
|
||||
base64_encode(json_encode($this->discoveryMap)),
|
||||
$current_paths);
|
||||
$current_paths = array();
|
||||
}
|
||||
|
||||
// Append the path to the current paths array.
|
||||
$current_paths[] = $this->getEngine()->getFilePathOnDisk($path);
|
||||
}
|
||||
|
||||
// If we still have paths left in current paths, then we need to create
|
||||
// a future for those too.
|
||||
if (count($current_paths) > 0) {
|
||||
$futures[] = new ExecFuture(
|
||||
'%C --settings-base64=%s -r=. %Ls',
|
||||
$this->runtimeEngine.$this->cslintEngine,
|
||||
base64_encode(json_encode($this->discoveryMap)),
|
||||
$current_paths);
|
||||
$current_paths = array();
|
||||
}
|
||||
|
||||
$this->futures = $futures;
|
||||
}
|
||||
|
||||
public function didLintPaths(array $paths) {
|
||||
if ($this->futures) {
|
||||
$futures = id(new FutureIterator($this->futures))
|
||||
->limit(8);
|
||||
foreach ($futures as $future) {
|
||||
$this->resolveFuture($future);
|
||||
}
|
||||
$this->futures = array();
|
||||
}
|
||||
}
|
||||
|
||||
protected function resolveFuture(Future $future) {
|
||||
list($stdout) = $future->resolvex();
|
||||
$all_results = json_decode($stdout);
|
||||
foreach ($all_results as $results) {
|
||||
if ($results === null || $results->Issues === null) {
|
||||
return;
|
||||
}
|
||||
foreach ($results->Issues as $issue) {
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($results->FileName);
|
||||
$message->setLine($issue->LineNumber);
|
||||
$message->setCode($issue->Index->Code);
|
||||
$message->setName($issue->Index->Name);
|
||||
$message->setChar($issue->Column);
|
||||
$message->setOriginalText($issue->OriginalText);
|
||||
$message->setReplacementText($issue->ReplacementText);
|
||||
$desc = @vsprintf($issue->Index->Message, $issue->Parameters);
|
||||
if ($desc === false) {
|
||||
$desc = $issue->Index->Message;
|
||||
}
|
||||
$message->setDescription($desc);
|
||||
$severity = ArcanistLintSeverity::SEVERITY_ADVICE;
|
||||
switch ($issue->Index->Severity) {
|
||||
case 0:
|
||||
$severity = ArcanistLintSeverity::SEVERITY_ADVICE;
|
||||
break;
|
||||
case 1:
|
||||
$severity = ArcanistLintSeverity::SEVERITY_AUTOFIX;
|
||||
break;
|
||||
case 2:
|
||||
$severity = ArcanistLintSeverity::SEVERITY_WARNING;
|
||||
break;
|
||||
case 3:
|
||||
$severity = ArcanistLintSeverity::SEVERITY_ERROR;
|
||||
break;
|
||||
case 4:
|
||||
$severity = ArcanistLintSeverity::SEVERITY_DISABLED;
|
||||
break;
|
||||
}
|
||||
$severity_override = $this->getLintMessageSeverity($issue->Index->Code);
|
||||
if ($severity_override !== null) {
|
||||
$severity = $severity_override;
|
||||
}
|
||||
$message->setSeverity($severity);
|
||||
$this->addLintMessage($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getDefaultMessageSeverity($code) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Uses `gjslint` to detect errors and potential problems in JavaScript code.
|
||||
*/
|
||||
final class ArcanistClosureLinter extends ArcanistExternalLinter {
|
||||
|
||||
public function getInfoName() {
|
||||
return pht('Closure Linter');
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'https://developers.google.com/closure/utilities/';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht("Uses Google's Closure Linter to check JavaScript code.");
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'GJSLINT';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'gjslint';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'gjslint';
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht(
|
||||
'Install %s using `%s`.',
|
||||
'gjslint',
|
||||
'pip install closure-linter');
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$lines = phutil_split_lines($stdout, false);
|
||||
$messages = array();
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$matches = null;
|
||||
if (!preg_match('/^Line (\d+), E:(\d+): (.*)/', $line, $matches)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$message = id(new ArcanistLintMessage())
|
||||
->setPath($path)
|
||||
->setLine($matches[1])
|
||||
->setName('GJSLINT'.$matches[2])
|
||||
->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR)
|
||||
->setCode($this->getLinterName().$matches[2])
|
||||
->setDescription($matches[3]);
|
||||
$messages[] = $message;
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistCoffeeLintLinter extends ArcanistExternalLinter {
|
||||
|
||||
private $config;
|
||||
|
||||
public function getInfoName() {
|
||||
return 'CoffeeLint';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'http://www.coffeelint.org';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'CoffeeLint is a style checker that helps keep CoffeeScript '.
|
||||
'code clean and consistent.');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'COFFEE';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'coffeelint';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'coffeelint';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
if (preg_match('/^(?P<version>\d+\.\d+\.\d+)$/', $stdout, $matches)) {
|
||||
return $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht(
|
||||
'Install CoffeeLint using `%s`.',
|
||||
'npm install -g coffeelint');
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
$options = array(
|
||||
'--reporter=raw',
|
||||
);
|
||||
|
||||
if ($this->config) {
|
||||
$options[] = '--file='.$this->config;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function getLinterConfigurationOptions() {
|
||||
$options = array(
|
||||
'coffeelint.config' => array(
|
||||
'type' => 'optional string',
|
||||
'help' => pht('A custom configuration file.'),
|
||||
),
|
||||
);
|
||||
|
||||
return $options + parent::getLinterConfigurationOptions();
|
||||
}
|
||||
|
||||
public function setLinterConfigurationValue($key, $value) {
|
||||
switch ($key) {
|
||||
case 'coffeelint.config':
|
||||
$this->config = $value;
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::setLinterConfigurationValue($key, $value);
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$messages = array();
|
||||
$output = phutil_json_decode($stdout);
|
||||
|
||||
// We are only linting a single file.
|
||||
if (count($output) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($output as $reports) {
|
||||
foreach ($reports as $report) {
|
||||
// Column number is not provided in the output.
|
||||
// See https://github.com/clutchski/coffeelint/issues/87
|
||||
|
||||
$message = id(new ArcanistLintMessage())
|
||||
->setPath($path)
|
||||
->setLine($report['lineNumber'])
|
||||
->setCode($this->getLinterName())
|
||||
->setName(ucwords(str_replace('_', ' ', $report['name'])))
|
||||
->setDescription($report['message'])
|
||||
->setOriginalText(idx($report, 'line'));
|
||||
|
||||
switch ($report['level']) {
|
||||
case 'warn':
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
|
||||
break;
|
||||
|
||||
default:
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ADVICE);
|
||||
break;
|
||||
}
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
protected function getLintCodeFromLinterConfigurationKey($code) {
|
||||
// NOTE: We can't figure out which rule generated each message, so we
|
||||
// can not customize severities.
|
||||
throw new Exception(
|
||||
pht(
|
||||
"CoffeeLint does not currently support custom severity levels, ".
|
||||
"because rules can't be identified from messages in output."));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistComposerLinter extends ArcanistLinter {
|
||||
|
||||
const LINT_OUT_OF_DATE = 1;
|
||||
|
||||
public function getInfoName() {
|
||||
return pht('Composer Dependency Manager');
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht('A linter for Composer related files.');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'COMPOSER';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'composer';
|
||||
}
|
||||
|
||||
public function getLintNameMap() {
|
||||
return array(
|
||||
self::LINT_OUT_OF_DATE => pht('Lock file out-of-date'),
|
||||
);
|
||||
}
|
||||
|
||||
public function lintPath($path) {
|
||||
switch (basename($path)) {
|
||||
case 'composer.json':
|
||||
$this->lintComposerJson($path);
|
||||
break;
|
||||
case 'composer.lock':
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function lintComposerJson($path) {
|
||||
$composer_hash = md5(Filesystem::readFile(dirname($path).'/composer.json'));
|
||||
$composer_lock = phutil_json_decode(
|
||||
Filesystem::readFile(dirname($path).'/composer.lock'));
|
||||
|
||||
if ($composer_hash !== $composer_lock['hash']) {
|
||||
$this->raiseLintAtPath(
|
||||
self::LINT_OUT_OF_DATE,
|
||||
pht(
|
||||
"The '%s' file seems to be out-of-date. ".
|
||||
"You probably need to run `%s`.",
|
||||
'composer.lock',
|
||||
'composer update'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Uses Cppcheck to do basic checks in a C++ file.
|
||||
*/
|
||||
final class ArcanistCppcheckLinter extends ArcanistExternalLinter {
|
||||
|
||||
public function getInfoName() {
|
||||
return 'C++ linter';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'http://cppcheck.sourceforge.net';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'Use `%s` to perform static analysis on C/C++ code.',
|
||||
'cppcheck');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'cppcheck';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'cppcheck';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'cppcheck';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
$regex = '/^Cppcheck (?P<version>\d+\.\d+)$/';
|
||||
if (preg_match($regex, $stdout, $matches)) {
|
||||
return $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht(
|
||||
'Install Cppcheck using `%s` or similar.',
|
||||
'apt-get install cppcheck');
|
||||
}
|
||||
|
||||
protected function getDefaultFlags() {
|
||||
return array(
|
||||
'-j2',
|
||||
'--enable=performance,style,portability,information',
|
||||
);
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
return array(
|
||||
'--quiet',
|
||||
'--inline-suppr',
|
||||
'--xml',
|
||||
'--xml-version=2',
|
||||
);
|
||||
}
|
||||
|
||||
public function shouldExpectCommandErrors() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$dom = new DOMDocument();
|
||||
$ok = @$dom->loadXML($stderr);
|
||||
|
||||
if (!$ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$errors = $dom->getElementsByTagName('error');
|
||||
$messages = array();
|
||||
foreach ($errors as $error) {
|
||||
foreach ($error->getElementsByTagName('location') as $location) {
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($location->getAttribute('file'));
|
||||
$message->setLine($location->getAttribute('line'));
|
||||
$message->setCode('Cppcheck');
|
||||
$message->setName($error->getAttribute('id'));
|
||||
$message->setDescription($error->getAttribute('msg'));
|
||||
|
||||
switch ($error->getAttribute('severity')) {
|
||||
case 'error':
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($error->getAttribute('inconclusive')) {
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ADVICE);
|
||||
} else {
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Uses Google's `cpplint.py` to check code.
|
||||
*/
|
||||
final class ArcanistCpplintLinter extends ArcanistExternalLinter {
|
||||
|
||||
public function getLinterName() {
|
||||
return 'C++ Google\'s Styleguide';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'cpplint';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'cpplint.py';
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht(
|
||||
'Install cpplint.py using `%s`, and place it in your path with the '.
|
||||
'appropriate permissions set.',
|
||||
'wget https://raw.github.com'.
|
||||
'/google/styleguide/gh-pages/cpplint/cpplint.py');
|
||||
}
|
||||
|
||||
protected function getDefaultMessageSeverity($code) {
|
||||
return ArcanistLintSeverity::SEVERITY_WARNING;
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$lines = explode("\n", $stderr);
|
||||
|
||||
$messages = array();
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
$matches = null;
|
||||
$regex = '/(\d+):\s*(.*)\s*\[(.*)\] \[(\d+)\]$/';
|
||||
if (!preg_match($regex, $line, $matches)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($matches as $key => $match) {
|
||||
$matches[$key] = trim($match);
|
||||
}
|
||||
|
||||
$severity = $this->getLintMessageSeverity($matches[3]);
|
||||
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($path);
|
||||
$message->setLine($matches[1]);
|
||||
$message->setCode($matches[3]);
|
||||
$message->setName($matches[3]);
|
||||
$message->setDescription($matches[2]);
|
||||
$message->setSeverity($severity);
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
protected function getLintCodeFromLinterConfigurationKey($code) {
|
||||
if (!preg_match('@^[a-z_]+/[a-z0-9_+]+$@', $code)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unrecognized lint message code "%s". Expected a valid cpplint '.
|
||||
'lint code like "%s" or "%s".',
|
||||
$code,
|
||||
'build/include_order',
|
||||
'whitespace/braces'));
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,570 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Base class for linters which operate by invoking an external program and
|
||||
* parsing results.
|
||||
*
|
||||
* @task bin Interpreters, Binaries and Flags
|
||||
* @task parse Parsing Linter Output
|
||||
* @task exec Executing the Linter
|
||||
*/
|
||||
abstract class ArcanistExternalLinter extends ArcanistFutureLinter {
|
||||
|
||||
private $bin;
|
||||
private $interpreter;
|
||||
private $flags;
|
||||
private $versionRequirement;
|
||||
|
||||
|
||||
/* -( Interpreters, Binaries and Flags )----------------------------------- */
|
||||
|
||||
/**
|
||||
* Return the default binary name or binary path where the external linter
|
||||
* lives. This can either be a binary name which is expected to be installed
|
||||
* in PATH (like "jshint"), or a relative path from the project root
|
||||
* (like "resources/support/bin/linter") or an absolute path.
|
||||
*
|
||||
* If the binary needs an interpreter (like "python" or "node"), you should
|
||||
* also override @{method:shouldUseInterpreter} and provide the interpreter
|
||||
* in @{method:getDefaultInterpreter}.
|
||||
*
|
||||
* @return string Default binary to execute.
|
||||
* @task bin
|
||||
*/
|
||||
abstract public function getDefaultBinary();
|
||||
|
||||
/**
|
||||
* Return a human-readable string describing how to install the linter. This
|
||||
* is normally something like "Install such-and-such by running `npm install
|
||||
* -g such-and-such`.", but will differ from linter to linter.
|
||||
*
|
||||
* @return string Human readable install instructions
|
||||
* @task bin
|
||||
*/
|
||||
abstract public function getInstallInstructions();
|
||||
|
||||
/**
|
||||
* Return a human-readable string describing how to upgrade the linter.
|
||||
*
|
||||
* @return string Human readable upgrade instructions
|
||||
* @task bin
|
||||
*/
|
||||
public function getUpgradeInstructions() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to continue when the external linter exits with an error code.
|
||||
* By default, linters which exit with an error code are assumed to have
|
||||
* failed. However, some linters exit with a specific code to indicate that
|
||||
* lint messages were detected.
|
||||
*
|
||||
* If the linter sometimes raises errors during normal operation, override
|
||||
* this method and return true so execution continues when it exits with
|
||||
* a nonzero status.
|
||||
*
|
||||
* @param bool Return true to continue on nonzero error code.
|
||||
* @task bin
|
||||
*/
|
||||
public function shouldExpectCommandErrors() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide mandatory, non-overridable flags to the linter. Generally these
|
||||
* are format flags, like `--format=xml`, which must always be given for
|
||||
* the output to be usable.
|
||||
*
|
||||
* Flags which are not mandatory should be provided in
|
||||
* @{method:getDefaultFlags} instead.
|
||||
*
|
||||
* @return list<string> Mandatory flags, like `"--format=xml"`.
|
||||
* @task bin
|
||||
*/
|
||||
protected function getMandatoryFlags() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide default, overridable flags to the linter. Generally these are
|
||||
* configuration flags which affect behavior but aren't critical. Flags
|
||||
* which are required should be provided in @{method:getMandatoryFlags}
|
||||
* instead.
|
||||
*
|
||||
* Default flags can be overridden with @{method:setFlags}.
|
||||
*
|
||||
* @return list<string> Overridable default flags.
|
||||
* @task bin
|
||||
*/
|
||||
protected function getDefaultFlags() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override default flags with custom flags. If not overridden, flags provided
|
||||
* by @{method:getDefaultFlags} are used.
|
||||
*
|
||||
* @param list<string> New flags.
|
||||
* @return this
|
||||
* @task bin
|
||||
*/
|
||||
final public function setFlags(array $flags) {
|
||||
$this->flags = $flags;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the binary's version requirement.
|
||||
*
|
||||
* @param string Version requirement.
|
||||
* @return this
|
||||
* @task bin
|
||||
*/
|
||||
final public function setVersionRequirement($version) {
|
||||
$this->versionRequirement = trim($version);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the binary or script to execute. This method synthesizes defaults
|
||||
* and configuration. You can override the binary with @{method:setBinary}.
|
||||
*
|
||||
* @return string Binary to execute.
|
||||
* @task bin
|
||||
*/
|
||||
final public function getBinary() {
|
||||
return coalesce($this->bin, $this->getDefaultBinary());
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default binary with a new one.
|
||||
*
|
||||
* @param string New binary.
|
||||
* @return this
|
||||
* @task bin
|
||||
*/
|
||||
final public function setBinary($bin) {
|
||||
$this->bin = $bin;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this linter should use an interpreter (like "python" or
|
||||
* "node") in addition to the script.
|
||||
*
|
||||
* After overriding this method to return `true`, override
|
||||
* @{method:getDefaultInterpreter} to set a default.
|
||||
*
|
||||
* @return bool True to use an interpreter.
|
||||
* @task bin
|
||||
*/
|
||||
public function shouldUseInterpreter() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default interpreter, like "python" or "node". This method is
|
||||
* only invoked if @{method:shouldUseInterpreter} has been overridden to
|
||||
* return `true`.
|
||||
*
|
||||
* @return string Default interpreter.
|
||||
* @task bin
|
||||
*/
|
||||
public function getDefaultInterpreter() {
|
||||
throw new PhutilMethodNotImplementedException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the effective interpreter. This method synthesizes configuration and
|
||||
* defaults.
|
||||
*
|
||||
* @return string Effective interpreter.
|
||||
* @task bin
|
||||
*/
|
||||
final public function getInterpreter() {
|
||||
return coalesce($this->interpreter, $this->getDefaultInterpreter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the interpreter, overriding any default.
|
||||
*
|
||||
* @param string New interpreter.
|
||||
* @return this
|
||||
* @task bin
|
||||
*/
|
||||
final public function setInterpreter($interpreter) {
|
||||
$this->interpreter = $interpreter;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/* -( Parsing Linter Output )---------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Parse the output of the external lint program into objects of class
|
||||
* @{class:ArcanistLintMessage} which `arc` can consume. Generally, this
|
||||
* means examining the output and converting each warning or error into a
|
||||
* message.
|
||||
*
|
||||
* If parsing fails, returning `false` will cause the caller to throw an
|
||||
* appropriate exception. (You can also throw a more specific exception if
|
||||
* you're able to detect a more specific condition.) Otherwise, return a list
|
||||
* of messages.
|
||||
*
|
||||
* @param string Path to the file being linted.
|
||||
* @param int Exit code of the linter.
|
||||
* @param string Stdout of the linter.
|
||||
* @param string Stderr of the linter.
|
||||
* @return list<ArcanistLintMessage>|false List of lint messages, or false
|
||||
* to indicate parser failure.
|
||||
* @task parse
|
||||
*/
|
||||
abstract protected function parseLinterOutput($path, $err, $stdout, $stderr);
|
||||
|
||||
|
||||
/* -( Executing the Linter )----------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Check that the binary and interpreter (if applicable) exist, and throw
|
||||
* an exception with a message about how to install them if they do not.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final public function checkBinaryConfiguration() {
|
||||
$interpreter = null;
|
||||
if ($this->shouldUseInterpreter()) {
|
||||
$interpreter = $this->getInterpreter();
|
||||
}
|
||||
|
||||
$binary = $this->getBinary();
|
||||
|
||||
// NOTE: If we have an interpreter, we don't require the script to be
|
||||
// executable (so we just check that the path exists). Otherwise, the
|
||||
// binary must be executable.
|
||||
|
||||
if ($interpreter) {
|
||||
if (!Filesystem::binaryExists($interpreter)) {
|
||||
throw new ArcanistMissingLinterException(
|
||||
pht(
|
||||
'Unable to locate interpreter "%s" to run linter %s. You may need '.
|
||||
'to install the interpreter, or adjust your linter configuration.',
|
||||
$interpreter,
|
||||
get_class($this)));
|
||||
}
|
||||
if (!Filesystem::pathExists($binary)) {
|
||||
throw new ArcanistMissingLinterException(
|
||||
sprintf(
|
||||
"%s\n%s",
|
||||
pht(
|
||||
'Unable to locate script "%s" to run linter %s. You may need '.
|
||||
'to install the script, or adjust your linter configuration.',
|
||||
$binary,
|
||||
get_class($this)),
|
||||
pht(
|
||||
'TO INSTALL: %s',
|
||||
$this->getInstallInstructions())));
|
||||
}
|
||||
} else {
|
||||
if (!Filesystem::binaryExists($binary)) {
|
||||
throw new ArcanistMissingLinterException(
|
||||
sprintf(
|
||||
"%s\n%s",
|
||||
pht(
|
||||
'Unable to locate binary "%s" to run linter %s. You may need '.
|
||||
'to install the binary, or adjust your linter configuration.',
|
||||
$binary,
|
||||
get_class($this)),
|
||||
pht(
|
||||
'TO INSTALL: %s',
|
||||
$this->getInstallInstructions())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a binary version requirement has been specified, compare the version
|
||||
* of the configured binary to the required version, and if the binary's
|
||||
* version is not supported, throw an exception.
|
||||
*
|
||||
* @param string Version string to check.
|
||||
* @return void
|
||||
*/
|
||||
final protected function checkBinaryVersion($version) {
|
||||
if (!$this->versionRequirement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$version) {
|
||||
$message = pht(
|
||||
'Linter %s requires %s version %s. Unable to determine the version '.
|
||||
'that you have installed.',
|
||||
get_class($this),
|
||||
$this->getBinary(),
|
||||
$this->versionRequirement);
|
||||
|
||||
$instructions = $this->getUpgradeInstructions();
|
||||
if ($instructions) {
|
||||
$message .= "\n".pht('TO UPGRADE: %s', $instructions);
|
||||
}
|
||||
|
||||
throw new ArcanistMissingLinterException($message);
|
||||
}
|
||||
|
||||
$operator = '==';
|
||||
$compare_to = $this->versionRequirement;
|
||||
|
||||
$matches = null;
|
||||
if (preg_match('/^([<>]=?|=)\s*(.*)$/', $compare_to, $matches)) {
|
||||
$operator = $matches[1];
|
||||
$compare_to = $matches[2];
|
||||
if ($operator === '=') {
|
||||
$operator = '==';
|
||||
}
|
||||
}
|
||||
|
||||
if (!version_compare($version, $compare_to, $operator)) {
|
||||
$message = pht(
|
||||
'Linter %s requires %s version %s. You have version %s.',
|
||||
get_class($this),
|
||||
$this->getBinary(),
|
||||
$this->versionRequirement,
|
||||
$version);
|
||||
|
||||
$instructions = $this->getUpgradeInstructions();
|
||||
if ($instructions) {
|
||||
$message .= "\n".pht('TO UPGRADE: %s', $instructions);
|
||||
}
|
||||
|
||||
throw new ArcanistMissingLinterException($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the composed executable command, including the interpreter and binary
|
||||
* but without flags or paths. This can be used to execute `--version`
|
||||
* commands.
|
||||
*
|
||||
* @return string Command to execute the raw linter.
|
||||
* @task exec
|
||||
*/
|
||||
final protected function getExecutableCommand() {
|
||||
$this->checkBinaryConfiguration();
|
||||
|
||||
$interpreter = null;
|
||||
if ($this->shouldUseInterpreter()) {
|
||||
$interpreter = $this->getInterpreter();
|
||||
}
|
||||
|
||||
$binary = $this->getBinary();
|
||||
|
||||
if ($interpreter) {
|
||||
$bin = csprintf('%s %s', $interpreter, $binary);
|
||||
} else {
|
||||
$bin = csprintf('%s', $binary);
|
||||
}
|
||||
|
||||
return $bin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the composed flags for the executable, including both mandatory and
|
||||
* configured flags.
|
||||
*
|
||||
* @return list<string> Composed flags.
|
||||
* @task exec
|
||||
*/
|
||||
final protected function getCommandFlags() {
|
||||
return array_merge(
|
||||
$this->getMandatoryFlags(),
|
||||
nonempty($this->flags, $this->getDefaultFlags()));
|
||||
}
|
||||
|
||||
public function getCacheVersion() {
|
||||
try {
|
||||
$this->checkBinaryConfiguration();
|
||||
} catch (ArcanistMissingLinterException $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$version = $this->getVersion();
|
||||
|
||||
if ($version) {
|
||||
$this->checkBinaryVersion($version);
|
||||
return $version.'-'.json_encode($this->getCommandFlags());
|
||||
} else {
|
||||
// Either we failed to parse the version number or the `getVersion`
|
||||
// function hasn't been implemented.
|
||||
return json_encode($this->getCommandFlags());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the path to be added to the command string.
|
||||
*
|
||||
* This method is expected to return an already escaped string.
|
||||
*
|
||||
* @param string Path to the file being linted
|
||||
* @return string The command-ready file argument
|
||||
*/
|
||||
protected function getPathArgumentForLinterFuture($path) {
|
||||
return csprintf('%s', $path);
|
||||
}
|
||||
|
||||
final protected function buildFutures(array $paths) {
|
||||
$executable = $this->getExecutableCommand();
|
||||
|
||||
$bin = csprintf('%C %Ls', $executable, $this->getCommandFlags());
|
||||
|
||||
$futures = array();
|
||||
foreach ($paths as $path) {
|
||||
$disk_path = $this->getEngine()->getFilePathOnDisk($path);
|
||||
$path_argument = $this->getPathArgumentForLinterFuture($disk_path);
|
||||
$future = new ExecFuture('%C %C', $bin, $path_argument);
|
||||
|
||||
$future->setCWD($this->getProjectRoot());
|
||||
$futures[$path] = $future;
|
||||
}
|
||||
|
||||
return $futures;
|
||||
}
|
||||
|
||||
final protected function resolveFuture($path, Future $future) {
|
||||
list($err, $stdout, $stderr) = $future->resolve();
|
||||
if ($err && !$this->shouldExpectCommandErrors()) {
|
||||
$future->resolvex();
|
||||
}
|
||||
|
||||
$messages = $this->parseLinterOutput($path, $err, $stdout, $stderr);
|
||||
|
||||
if ($err && $this->shouldExpectCommandErrors() && !$messages) {
|
||||
// We assume that if the future exits with a non-zero status and we
|
||||
// failed to parse any linter messages, then something must've gone wrong
|
||||
// during parsing.
|
||||
$messages = false;
|
||||
}
|
||||
|
||||
if ($messages === false) {
|
||||
if ($err) {
|
||||
$future->resolvex();
|
||||
} else {
|
||||
throw new Exception(
|
||||
sprintf(
|
||||
"%s\n\nSTDOUT\n%s\n\nSTDERR\n%s",
|
||||
pht('Linter failed to parse output!'),
|
||||
$stdout,
|
||||
$stderr));
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($messages as $message) {
|
||||
$this->addLintMessage($message);
|
||||
}
|
||||
}
|
||||
|
||||
public function getLinterConfigurationOptions() {
|
||||
$options = array(
|
||||
'bin' => array(
|
||||
'type' => 'optional string | list<string>',
|
||||
'help' => pht(
|
||||
'Specify a string (or list of strings) identifying the binary '.
|
||||
'which should be invoked to execute this linter. This overrides '.
|
||||
'the default binary. If you provide a list of possible binaries, '.
|
||||
'the first one which exists will be used.'),
|
||||
),
|
||||
'flags' => array(
|
||||
'type' => 'optional list<string>',
|
||||
'help' => pht(
|
||||
'Provide a list of additional flags to pass to the linter on the '.
|
||||
'command line.'),
|
||||
),
|
||||
'version' => array(
|
||||
'type' => 'optional string',
|
||||
'help' => pht(
|
||||
'Specify a version requirement for the binary. The version number '.
|
||||
'may be prefixed with <, <=, >, >=, or = to specify the version '.
|
||||
'comparison operator (default: =).'),
|
||||
),
|
||||
);
|
||||
|
||||
if ($this->shouldUseInterpreter()) {
|
||||
$options['interpreter'] = array(
|
||||
'type' => 'optional string | list<string>',
|
||||
'help' => pht(
|
||||
'Specify a string (or list of strings) identifying the interpreter '.
|
||||
'which should be used to invoke the linter binary. If you provide '.
|
||||
'a list of possible interpreters, the first one that exists '.
|
||||
'will be used.'),
|
||||
);
|
||||
}
|
||||
|
||||
return $options + parent::getLinterConfigurationOptions();
|
||||
}
|
||||
|
||||
public function setLinterConfigurationValue($key, $value) {
|
||||
switch ($key) {
|
||||
case 'interpreter':
|
||||
$root = $this->getProjectRoot();
|
||||
|
||||
foreach ((array)$value as $path) {
|
||||
if (Filesystem::binaryExists($path)) {
|
||||
$this->setInterpreter($path);
|
||||
return;
|
||||
}
|
||||
|
||||
$path = Filesystem::resolvePath($path, $root);
|
||||
|
||||
if (Filesystem::binaryExists($path)) {
|
||||
$this->setInterpreter($path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception(
|
||||
pht('None of the configured interpreters can be located.'));
|
||||
case 'bin':
|
||||
$is_script = $this->shouldUseInterpreter();
|
||||
|
||||
$root = $this->getProjectRoot();
|
||||
|
||||
foreach ((array)$value as $path) {
|
||||
if (!$is_script && Filesystem::binaryExists($path)) {
|
||||
$this->setBinary($path);
|
||||
return;
|
||||
}
|
||||
|
||||
$path = Filesystem::resolvePath($path, $root);
|
||||
if ((!$is_script && Filesystem::binaryExists($path)) ||
|
||||
($is_script && Filesystem::pathExists($path))) {
|
||||
$this->setBinary($path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception(
|
||||
pht('None of the configured binaries can be located.'));
|
||||
case 'flags':
|
||||
$this->setFlags($value);
|
||||
return;
|
||||
case 'version':
|
||||
$this->setVersionRequirement($value);
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::setLinterConfigurationValue($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a configuration lint code to an `arc` lint code. Primarily, this is
|
||||
* intended for validation, but can also be used to normalize case or
|
||||
* otherwise be more permissive in accepted inputs.
|
||||
*
|
||||
* If the code is not recognized, you should throw an exception.
|
||||
*
|
||||
* @param string Code specified in configuration.
|
||||
* @return string Normalized code to use in severity map.
|
||||
*/
|
||||
protected function getLintCodeFromLinterConfigurationKey($code) {
|
||||
return $code;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Uses "flake8" to detect various errors in Python code.
|
||||
* Requires version 1.7.0 or newer of flake8.
|
||||
*/
|
||||
final class ArcanistFlake8Linter extends ArcanistExternalLinter {
|
||||
|
||||
public function getInfoName() {
|
||||
return 'Python Flake8 multi-linter';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'https://pypi.python.org/pypi/flake8';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'Uses `%s` to run several linters (PyFlakes, pep8, and a McCabe '.
|
||||
'complexity checker) on Python source files.',
|
||||
'flake8');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'flake8';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'flake8';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'flake8';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
if (preg_match('/^(?P<version>\d+\.\d+(?:\.\d+)?)\b/', $stdout, $matches)) {
|
||||
return $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht('Install flake8 using `%s`.', 'pip install flake8');
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$lines = phutil_split_lines($stdout, false);
|
||||
|
||||
// stdin:2: W802 undefined name 'foo' # pyflakes
|
||||
// stdin:3:1: E302 expected 2 blank lines, found 1 # pep8
|
||||
$regexp =
|
||||
'/^(?:.*?):(?P<line>\d+):(?:(?P<char>\d+):)? (?P<code>\S+) (?P<msg>.*)$/';
|
||||
|
||||
$messages = array();
|
||||
foreach ($lines as $line) {
|
||||
$matches = null;
|
||||
if (!preg_match($regexp, $line, $matches)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($matches as $key => $match) {
|
||||
$matches[$key] = trim($match);
|
||||
}
|
||||
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($path);
|
||||
$message->setLine($matches['line']);
|
||||
if (!empty($matches['char'])) {
|
||||
$message->setChar($matches['char']);
|
||||
}
|
||||
$message->setCode($matches['code']);
|
||||
$message->setName($this->getLinterName().' '.$matches['code']);
|
||||
$message->setDescription($matches['msg']);
|
||||
$message->setSeverity($this->getLintMessageSeverity($matches['code']));
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
protected function getDefaultMessageSeverity($code) {
|
||||
if (preg_match('/^C/', $code)) {
|
||||
// "C": Cyclomatic complexity
|
||||
return ArcanistLintSeverity::SEVERITY_ADVICE;
|
||||
} else if (preg_match('/^W/', $code)) {
|
||||
// "W": PEP8 Warning
|
||||
return ArcanistLintSeverity::SEVERITY_WARNING;
|
||||
} else {
|
||||
// "E": PEP8 Error
|
||||
// "F": PyFlakes Error
|
||||
// or: Flake8 Extension Message
|
||||
return ArcanistLintSeverity::SEVERITY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getLintCodeFromLinterConfigurationKey($code) {
|
||||
if (!preg_match('/^[A-Z]\d+$/', $code)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unrecognized lint message code "%s". Expected a valid flake8 '.
|
||||
'lint code like "%s", or "%s", or "%s", or "%s".',
|
||||
$code,
|
||||
'E225',
|
||||
'W291',
|
||||
'F811',
|
||||
'C901'));
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
<?php
|
||||
|
||||
abstract class ArcanistFutureLinter extends ArcanistLinter {
|
||||
|
||||
private $futures;
|
||||
|
||||
abstract protected function buildFutures(array $paths);
|
||||
abstract protected function resolveFuture($path, Future $future);
|
||||
|
||||
final protected function getFuturesLimit() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
final public function willLintPaths(array $paths) {
|
||||
$limit = $this->getFuturesLimit();
|
||||
$this->futures = id(new FutureIterator(array()))->limit($limit);
|
||||
foreach ($this->buildFutures($paths) as $path => $future) {
|
||||
$this->futures->addFuture($future, $path);
|
||||
}
|
||||
}
|
||||
|
||||
final public function lintPath($path) {
|
||||
return;
|
||||
}
|
||||
|
||||
final public function didLintPaths(array $paths) {
|
||||
if (!$this->futures) {
|
||||
return;
|
||||
}
|
||||
|
||||
$map = array();
|
||||
foreach ($this->futures as $path => $future) {
|
||||
$this->setActivePath($path);
|
||||
$this->resolveFuture($path, $future);
|
||||
$map[$path] = $future;
|
||||
}
|
||||
$this->futures = array();
|
||||
|
||||
$this->didResolveLinterFutures($map);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hook for cleaning up resources.
|
||||
*
|
||||
* This is invoked after a block of futures resolve, and allows linters to
|
||||
* discard or clean up any shared resources they no longer need.
|
||||
*
|
||||
* @param map<string, Future> Map of paths to resolved futures.
|
||||
* @return void
|
||||
*/
|
||||
protected function didResolveLinterFutures(array $futures) {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistGoLintLinter extends ArcanistExternalLinter {
|
||||
|
||||
public function getInfoName() {
|
||||
return 'Golint';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'https://github.com/golang/lint';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht('Golint is a linter for Go source code.');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'GOLINT';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'golint';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'golint';
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht(
|
||||
'Install Golint using `%s`.',
|
||||
'go get github.com/golang/lint/golint');
|
||||
}
|
||||
|
||||
public function shouldExpectCommandErrors() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function canCustomizeLintSeverities() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$lines = phutil_split_lines($stdout, false);
|
||||
|
||||
$messages = array();
|
||||
foreach ($lines as $line) {
|
||||
$matches = explode(':', $line, 4);
|
||||
|
||||
if (count($matches) === 4) {
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($path);
|
||||
$message->setLine($matches[1]);
|
||||
$message->setChar($matches[2]);
|
||||
$message->setCode($this->getLinterName());
|
||||
$message->setName($this->getLinterName());
|
||||
$message->setDescription(ucfirst(trim($matches[3])));
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ADVICE);
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Calls `hlint` and parses its results.
|
||||
*/
|
||||
final class ArcanistHLintLinter extends ArcanistExternalLinter {
|
||||
|
||||
public function getInfoName() {
|
||||
return 'Haskell Linter';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'https://github.com/ndmitchell/hlint';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht('HLint is a linter for Haskell code.');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'HLINT';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'hlint';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'hlint';
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht('Install hlint with `%s`.', 'cabal install hlint');
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
return array('--json');
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout, $stderr) = execx(
|
||||
'%C --version', $this->getExecutableCommand());
|
||||
|
||||
$matches = null;
|
||||
if (preg_match('@HLint v(.*),@', $stdout, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$json = phutil_json_decode($stdout);
|
||||
$messages = array();
|
||||
foreach ($json as $fix) {
|
||||
if ($fix === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setCode($this->getLinterName());
|
||||
$message->setPath($path);
|
||||
$message->setLine($fix['startLine']);
|
||||
$message->setChar($fix['startColumn']);
|
||||
$message->setName($fix['hint']);
|
||||
$message->setOriginalText($fix['from']);
|
||||
$message->setReplacementText($fix['to']);
|
||||
|
||||
/* Some improvements may slightly change semantics, so attach
|
||||
all necessary notes too. */
|
||||
$notes = '';
|
||||
foreach ($fix['note'] as $note) {
|
||||
$notes .= phutil_console_format(
|
||||
' **%s**: %s.',
|
||||
pht('NOTE'),
|
||||
trim($note, '"'));
|
||||
}
|
||||
|
||||
$message->setDescription(
|
||||
pht(
|
||||
'In module `%s`, declaration `%s`.',
|
||||
$fix['module'],
|
||||
$fix['decl']).$notes);
|
||||
|
||||
switch ($fix['severity']) {
|
||||
case 'Error':
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
|
||||
break;
|
||||
case 'Warning':
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
|
||||
break;
|
||||
default:
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ADVICE);
|
||||
break;
|
||||
}
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Uses JSHint to detect errors and potential problems in JavaScript code.
|
||||
*/
|
||||
final class ArcanistJSHintLinter extends ArcanistExternalLinter {
|
||||
|
||||
private $jshintignore;
|
||||
private $jshintrc;
|
||||
|
||||
public function getInfoName() {
|
||||
return 'JavaScript error checking';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'http://www.jshint.com';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'Use `%s` to detect issues with JavaScript source files.',
|
||||
'jshint');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'JSHint';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'jshint';
|
||||
}
|
||||
|
||||
protected function getDefaultMessageSeverity($code) {
|
||||
if (preg_match('/^W/', $code)) {
|
||||
return ArcanistLintSeverity::SEVERITY_WARNING;
|
||||
} else if (preg_match('/^E043$/', $code)) {
|
||||
// TODO: If JSHint encounters a large number of errors, it will quit
|
||||
// prematurely and add an additional "Too Many Errors" error. Ideally, we
|
||||
// should be able to pass some sort of `--force` option to `jshint`.
|
||||
//
|
||||
// See https://github.com/jshint/jshint/issues/180
|
||||
return ArcanistLintSeverity::SEVERITY_DISABLED;
|
||||
} else {
|
||||
return ArcanistLintSeverity::SEVERITY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'jshint';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
// NOTE: `jshint --version` emits version information on stderr, not stdout.
|
||||
list($stdout, $stderr) = execx(
|
||||
'%C --version',
|
||||
$this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
$regex = '/^jshint v(?P<version>\d+\.\d+\.\d+)$/';
|
||||
if (preg_match($regex, $stderr, $matches)) {
|
||||
return $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht('Install JSHint using `%s`.', 'npm install -g jshint');
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
$options = array();
|
||||
|
||||
$options[] = '--reporter='.dirname(realpath(__FILE__)).'/reporter.js';
|
||||
|
||||
if ($this->jshintrc) {
|
||||
$options[] = '--config='.$this->jshintrc;
|
||||
}
|
||||
|
||||
if ($this->jshintignore) {
|
||||
$options[] = '--exclude-path='.$this->jshintignore;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function getLinterConfigurationOptions() {
|
||||
$options = array(
|
||||
'jshint.jshintignore' => array(
|
||||
'type' => 'optional string',
|
||||
'help' => pht('Pass in a custom jshintignore file path.'),
|
||||
),
|
||||
'jshint.jshintrc' => array(
|
||||
'type' => 'optional string',
|
||||
'help' => pht('Custom configuration file.'),
|
||||
),
|
||||
);
|
||||
|
||||
return $options + parent::getLinterConfigurationOptions();
|
||||
}
|
||||
|
||||
public function setLinterConfigurationValue($key, $value) {
|
||||
switch ($key) {
|
||||
case 'jshint.jshintignore':
|
||||
$this->jshintignore = $value;
|
||||
return;
|
||||
|
||||
case 'jshint.jshintrc':
|
||||
$this->jshintrc = $value;
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::setLinterConfigurationValue($key, $value);
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$errors = null;
|
||||
try {
|
||||
$errors = phutil_json_decode($stdout);
|
||||
} catch (PhutilJSONParserException $ex) {
|
||||
// Something went wrong and we can't decode the output. Exit abnormally.
|
||||
throw new PhutilProxyException(
|
||||
pht('JSHint returned unparseable output.'),
|
||||
$ex);
|
||||
}
|
||||
|
||||
$messages = array();
|
||||
foreach ($errors as $err) {
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($path);
|
||||
$message->setLine(idx($err, 'line'));
|
||||
$message->setChar(idx($err, 'col'));
|
||||
$message->setCode(idx($err, 'code'));
|
||||
$message->setName('JSHint'.idx($err, 'code'));
|
||||
$message->setDescription(idx($err, 'reason'));
|
||||
$message->setSeverity($this->getLintMessageSeverity(idx($err, 'code')));
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
protected function getLintCodeFromLinterConfigurationKey($code) {
|
||||
if (!preg_match('/^(E|W)\d+$/', $code)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unrecognized lint message code "%s". Expected a valid JSHint '.
|
||||
'lint code like "%s" or "%s".',
|
||||
$code,
|
||||
'E033',
|
||||
'W093'));
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* A linter for JSON files.
|
||||
*/
|
||||
final class ArcanistJSONLintLinter extends ArcanistExternalLinter {
|
||||
|
||||
public function getInfoName() {
|
||||
return pht('JSON Lint');
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'https://github.com/zaach/jsonlint';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht('Use `%s` to detect syntax errors in JSON files.', 'jsonlint');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'JSON';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'jsonlint';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'jsonlint';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
// NOTE: `jsonlint --version` returns a non-zero exit status.
|
||||
list($err, $stdout) = exec_manual(
|
||||
'%C --version',
|
||||
$this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
if (preg_match('/^(?P<version>\d+\.\d+\.\d+)$/', $stdout, $matches)) {
|
||||
return $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht('Install jsonlint using `%s`.', 'npm install -g jsonlint');
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
return array(
|
||||
'--compact',
|
||||
);
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$lines = phutil_split_lines($stderr, false);
|
||||
|
||||
$messages = array();
|
||||
foreach ($lines as $line) {
|
||||
$matches = null;
|
||||
$match = preg_match(
|
||||
'/^(?:(?<path>.+): )?'.
|
||||
'line (?<line>\d+), col (?<column>\d+), '.
|
||||
'(?<description>.*)$/',
|
||||
$line,
|
||||
$matches);
|
||||
|
||||
if ($match) {
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($path);
|
||||
$message->setLine($matches['line']);
|
||||
$message->setChar($matches['column']);
|
||||
$message->setCode($this->getLinterName());
|
||||
$message->setName($this->getLinterName());
|
||||
$message->setDescription(ucfirst($matches['description']));
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistJscsLinter extends ArcanistExternalLinter {
|
||||
|
||||
private $config;
|
||||
private $preset;
|
||||
|
||||
public function getInfoName() {
|
||||
return 'JavaScript Code Style';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'https://github.com/mdevils/node-jscs';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'Use `%s` to detect issues with Javascript source files.',
|
||||
'jscs');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'JSCS';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'jscs';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'jscs';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
$regex = '/^(?P<version>\d+\.\d+\.\d+)$/';
|
||||
if (preg_match($regex, $stdout, $matches)) {
|
||||
return $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht('Install JSCS using `%s`.', 'npm install -g jscs');
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
$options = array();
|
||||
|
||||
$options[] = '--reporter=checkstyle';
|
||||
$options[] = '--no-colors';
|
||||
|
||||
if ($this->config) {
|
||||
$options[] = '--config='.$this->config;
|
||||
}
|
||||
|
||||
if ($this->preset) {
|
||||
$options[] = '--preset='.$this->preset;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function getLinterConfigurationOptions() {
|
||||
$options = array(
|
||||
'jscs.config' => array(
|
||||
'type' => 'optional string',
|
||||
'help' => pht('Pass in a custom %s file path.', 'jscsrc'),
|
||||
),
|
||||
'jscs.preset' => array(
|
||||
'type' => 'optional string',
|
||||
'help' => pht('Custom preset.'),
|
||||
),
|
||||
);
|
||||
|
||||
return $options + parent::getLinterConfigurationOptions();
|
||||
}
|
||||
|
||||
public function setLinterConfigurationValue($key, $value) {
|
||||
switch ($key) {
|
||||
case 'jscs.config':
|
||||
$this->config = $value;
|
||||
return;
|
||||
|
||||
case 'jscs.preset':
|
||||
$this->preset = $value;
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::setLinterConfigurationValue($key, $value);
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$report_dom = new DOMDocument();
|
||||
$ok = @$report_dom->loadXML($stdout);
|
||||
|
||||
if (!$ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$messages = array();
|
||||
foreach ($report_dom->getElementsByTagName('file') as $file) {
|
||||
foreach ($file->getElementsByTagName('error') as $error) {
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($path);
|
||||
$message->setLine($error->getAttribute('line'));
|
||||
$message->setChar($error->getAttribute('column'));
|
||||
$message->setCode('JSCS');
|
||||
$message->setName('JSCS');
|
||||
$message->setDescription($error->getAttribute('message'));
|
||||
|
||||
switch ($error->getAttribute('severity')) {
|
||||
case 'error':
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
|
||||
break;
|
||||
|
||||
case 'warning':
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
|
||||
break;
|
||||
|
||||
default:
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ADVICE);
|
||||
break;
|
||||
}
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
protected function getLintCodeFromLinterConfigurationKey($code) {
|
||||
|
||||
// NOTE: We can't figure out which rule generated each message, so we
|
||||
// can not customize severities.
|
||||
//
|
||||
// See https://github.com/mdevils/node-jscs/issues/224
|
||||
|
||||
throw new Exception(
|
||||
pht(
|
||||
"JSCS does not currently support custom severity levels, because ".
|
||||
"rules can't be identified from messages in output."));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,183 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* A linter for LESSCSS files.
|
||||
*
|
||||
* This linter uses [[https://github.com/less/less.js | lessc]] to detect
|
||||
* errors and potential problems in [[http://lesscss.org/ | LESS]] code.
|
||||
*/
|
||||
final class ArcanistLesscLinter extends ArcanistExternalLinter {
|
||||
|
||||
const LINT_RUNTIME_ERROR = 1;
|
||||
const LINT_ARGUMENT_ERROR = 2;
|
||||
const LINT_FILE_ERROR = 3;
|
||||
const LINT_NAME_ERROR = 4;
|
||||
const LINT_OPERATION_ERROR = 5;
|
||||
const LINT_PARSE_ERROR = 6;
|
||||
const LINT_SYNTAX_ERROR = 7;
|
||||
|
||||
private $strictMath = false;
|
||||
private $strictUnits = false;
|
||||
|
||||
public function getInfoName() {
|
||||
return 'CSS pre-processor';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'http://lesscss.org/';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'Use the `%s` mode provided by `%s` to detect errors in '.
|
||||
'Less source files.',
|
||||
'--lint',
|
||||
'lessc');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'LESSC';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'lessc';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationOptions() {
|
||||
return parent::getLinterConfigurationOptions() + array(
|
||||
'lessc.strict-math' => array(
|
||||
'type' => 'optional bool',
|
||||
'help' => pht(
|
||||
'Enable strict math, which only processes mathematical expressions '.
|
||||
'inside extraneous parentheses.'),
|
||||
),
|
||||
'lessc.strict-units' => array(
|
||||
'type' => 'optional bool',
|
||||
'help' => pht('Enable strict handling of units in expressions.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function setLinterConfigurationValue($key, $value) {
|
||||
switch ($key) {
|
||||
case 'lessc.strict-math':
|
||||
$this->strictMath = $value;
|
||||
return;
|
||||
case 'lessc.strict-units':
|
||||
$this->strictUnits = $value;
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::setLinterConfigurationValue($key, $value);
|
||||
}
|
||||
|
||||
public function getLintNameMap() {
|
||||
return array(
|
||||
self::LINT_RUNTIME_ERROR => pht('Runtime Error'),
|
||||
self::LINT_ARGUMENT_ERROR => pht('Argument Error'),
|
||||
self::LINT_FILE_ERROR => pht('File Error'),
|
||||
self::LINT_NAME_ERROR => pht('Name Error'),
|
||||
self::LINT_OPERATION_ERROR => pht('Operation Error'),
|
||||
self::LINT_PARSE_ERROR => pht('Parse Error'),
|
||||
self::LINT_SYNTAX_ERROR => pht('Syntax Error'),
|
||||
);
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'lessc';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
$regex = '/^lessc (?P<version>\d+\.\d+\.\d+)\b/';
|
||||
if (preg_match($regex, $stdout, $matches)) {
|
||||
$version = $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht('Install lessc using `%s`.', 'npm install -g less');
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
return array(
|
||||
'--lint',
|
||||
'--no-color',
|
||||
'--strict-math='.($this->strictMath ? 'on' : 'off'),
|
||||
'--strict-units='.($this->strictUnits ? 'on' : 'off'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$lines = phutil_split_lines($stderr, false);
|
||||
|
||||
$messages = array();
|
||||
foreach ($lines as $line) {
|
||||
$matches = null;
|
||||
$match = preg_match(
|
||||
'/^(?P<name>\w+): (?P<description>.+) '.
|
||||
'in (?P<path>.+|-) '.
|
||||
'on line (?P<line>\d+), column (?P<column>\d+):$/',
|
||||
$line,
|
||||
$matches);
|
||||
|
||||
if ($match) {
|
||||
switch ($matches['name']) {
|
||||
case 'RuntimeError':
|
||||
$code = self::LINT_RUNTIME_ERROR;
|
||||
break;
|
||||
|
||||
case 'ArgumentError':
|
||||
$code = self::LINT_ARGUMENT_ERROR;
|
||||
break;
|
||||
|
||||
case 'FileError':
|
||||
$code = self::LINT_FILE_ERROR;
|
||||
break;
|
||||
|
||||
case 'NameError':
|
||||
$code = self::LINT_NAME_ERROR;
|
||||
break;
|
||||
|
||||
case 'OperationError':
|
||||
$code = self::LINT_OPERATION_ERROR;
|
||||
break;
|
||||
|
||||
case 'ParseError':
|
||||
$code = self::LINT_PARSE_ERROR;
|
||||
break;
|
||||
|
||||
case 'SyntaxError':
|
||||
$code = self::LINT_SYNTAX_ERROR;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException(
|
||||
pht(
|
||||
'Unrecognized lint message code "%s".',
|
||||
$code));
|
||||
}
|
||||
|
||||
$code = $this->getLintCodeFromLinterConfigurationKey($matches['name']);
|
||||
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($path);
|
||||
$message->setLine($matches['line']);
|
||||
$message->setChar($matches['column']);
|
||||
$message->setCode($this->getLintMessageFullCode($code));
|
||||
$message->setSeverity($this->getLintMessageSeverity($code));
|
||||
$message->setName($this->getLintMessageName($code));
|
||||
$message->setDescription(ucfirst($matches['description']));
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Uses "pep8.py" to enforce PEP8 rules for Python.
|
||||
*/
|
||||
final class ArcanistPEP8Linter extends ArcanistExternalLinter {
|
||||
|
||||
public function getInfoName() {
|
||||
return 'Python PEP 8';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'https://pypi.python.org/pypi/pep8';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'pep8 is a tool to check your Python code against some of the '.
|
||||
'style conventions in PEP 8.');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'PEP8';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'pep8';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'pep8';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
if (preg_match('/^(?P<version>\d+\.\d+(?:\.\d+)?)\b/', $stdout, $matches)) {
|
||||
return $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht('Install PEP8 using `%s`.', 'pip install pep8');
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$lines = phutil_split_lines($stdout, false);
|
||||
|
||||
$messages = array();
|
||||
foreach ($lines as $line) {
|
||||
$matches = null;
|
||||
if (!preg_match('/^(.*?):(\d+):(\d+): (\S+) (.*)$/', $line, $matches)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($matches as $key => $match) {
|
||||
$matches[$key] = trim($match);
|
||||
}
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($path);
|
||||
$message->setLine($matches[2]);
|
||||
$message->setChar($matches[3]);
|
||||
$message->setCode($matches[4]);
|
||||
$message->setName('PEP8 '.$matches[4]);
|
||||
$message->setDescription($matches[5]);
|
||||
$message->setSeverity($this->getLintMessageSeverity($matches[4]));
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
protected function getDefaultMessageSeverity($code) {
|
||||
if (preg_match('/^W/', $code)) {
|
||||
return ArcanistLintSeverity::SEVERITY_WARNING;
|
||||
} else {
|
||||
return ArcanistLintSeverity::SEVERITY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getLintCodeFromLinterConfigurationKey($code) {
|
||||
if (!preg_match('/^(E|W)\d+$/', $code)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unrecognized lint message code "%s". Expected a valid PEP8 '.
|
||||
'lint code like "%s" or "%s".',
|
||||
$code,
|
||||
'E101',
|
||||
'W291'));
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Uses "php -l" to detect syntax errors in PHP code.
|
||||
*/
|
||||
final class ArcanistPhpLinter extends ArcanistExternalLinter {
|
||||
|
||||
const LINT_PARSE_ERROR = 1;
|
||||
const LINT_FATAL_ERROR = 2;
|
||||
|
||||
public function getInfoName() {
|
||||
return 'php -l';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'http://php.net/';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht('Checks for syntax errors in PHP files.');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'PHP';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'php';
|
||||
}
|
||||
|
||||
public function getLintNameMap() {
|
||||
return array(
|
||||
self::LINT_PARSE_ERROR => pht('Parse Error'),
|
||||
self::LINT_FATAL_ERROR => pht('Fatal Error'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
return array('-l');
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht('Install PHP.');
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'php';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout) = execx(
|
||||
'%C --run %s',
|
||||
$this->getExecutableCommand(),
|
||||
'echo phpversion();');
|
||||
return $stdout;
|
||||
}
|
||||
|
||||
protected function canCustomizeLintSeverities() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
// Older versions of PHP had both on stdout, newer ones split it.
|
||||
// Combine stdout and stderr for consistency.
|
||||
$stdout = $stderr."\n".$stdout;
|
||||
$matches = array();
|
||||
|
||||
$regex = '/^(PHP )?(?<type>.+) error: +(?<error>.+) in (?<file>.+) '.
|
||||
'on line (?<line>\d+)$/m';
|
||||
if (preg_match($regex, $stdout, $matches)) {
|
||||
$code = $this->getLintCodeFromLinterConfigurationKey($matches['type']);
|
||||
|
||||
$message = id(new ArcanistLintMessage())
|
||||
->setPath($path)
|
||||
->setLine($matches['line'])
|
||||
->setCode($this->getLinterName().$code)
|
||||
->setName($this->getLintMessageName($code))
|
||||
->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR)
|
||||
->setDescription($matches['error']);
|
||||
|
||||
// `php -l` only returns the first error.
|
||||
return array($message);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function getLintCodeFromLinterConfigurationKey($code) {
|
||||
switch (phutil_utf8_strtolower($code)) {
|
||||
case 'parse':
|
||||
return self::LINT_PARSE_ERROR;
|
||||
|
||||
case 'fatal':
|
||||
return self::LINT_FATAL_ERROR;
|
||||
|
||||
default:
|
||||
throw new Exception(pht('Unrecognized lint message code: "%s"', $code));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Uses "PHP_CodeSniffer" to detect checkstyle errors in PHP code.
|
||||
*/
|
||||
final class ArcanistPhpcsLinter extends ArcanistExternalLinter {
|
||||
|
||||
private $standard;
|
||||
|
||||
public function getInfoName() {
|
||||
return 'PHP_CodeSniffer';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'http://pear.php.net/package/PHP_CodeSniffer/';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and '.
|
||||
'detects violations of a defined set of coding standards.');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'PHPCS';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'phpcs';
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht('Install PHPCS with `%s`.', 'pear install PHP_CodeSniffer');
|
||||
}
|
||||
|
||||
public function getLinterConfigurationOptions() {
|
||||
$options = array(
|
||||
'phpcs.standard' => array(
|
||||
'type' => 'optional string',
|
||||
'help' => pht('The name or path of the coding standard to use.'),
|
||||
),
|
||||
);
|
||||
|
||||
return $options + parent::getLinterConfigurationOptions();
|
||||
}
|
||||
|
||||
public function setLinterConfigurationValue($key, $value) {
|
||||
switch ($key) {
|
||||
case 'phpcs.standard':
|
||||
$this->standard = $value;
|
||||
return;
|
||||
|
||||
default:
|
||||
return parent::setLinterConfigurationValue($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
$options = array('--report=xml');
|
||||
|
||||
if ($this->standard) {
|
||||
$options[] = '--standard='.$this->standard;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'phpcs';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
$regex = '/^PHP_CodeSniffer version (?P<version>\d+\.\d+\.\d+)\b/';
|
||||
if (preg_match($regex, $stdout, $matches)) {
|
||||
return $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
// NOTE: Some version of PHPCS after 1.4.6 stopped printing a valid, empty
|
||||
// XML document to stdout in the case of no errors. If PHPCS exits with
|
||||
// error 0, just ignore output.
|
||||
if (!$err) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$report_dom = new DOMDocument();
|
||||
$ok = @$report_dom->loadXML($stdout);
|
||||
if (!$ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$files = $report_dom->getElementsByTagName('file');
|
||||
$messages = array();
|
||||
foreach ($files as $file) {
|
||||
foreach ($file->childNodes as $child) {
|
||||
if (!($child instanceof DOMElement)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($child->tagName == 'error') {
|
||||
$prefix = 'E';
|
||||
} else {
|
||||
$prefix = 'W';
|
||||
}
|
||||
|
||||
$source = $child->getAttribute('source');
|
||||
$code = 'PHPCS.'.$prefix.'.'.$source;
|
||||
|
||||
$message = id(new ArcanistLintMessage())
|
||||
->setPath($path)
|
||||
->setName($source)
|
||||
->setLine($child->getAttribute('line'))
|
||||
->setChar($child->getAttribute('column'))
|
||||
->setCode($code)
|
||||
->setDescription($child->nodeValue)
|
||||
->setSeverity($this->getLintMessageSeverity($code));
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
protected function getDefaultMessageSeverity($code) {
|
||||
if (preg_match('/^PHPCS\\.W\\./', $code)) {
|
||||
return ArcanistLintSeverity::SEVERITY_WARNING;
|
||||
} else {
|
||||
return ArcanistLintSeverity::SEVERITY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getLintCodeFromLinterConfigurationKey($code) {
|
||||
if (!preg_match('/^PHPCS\\.(E|W)\\./', $code)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
"Invalid severity code '%s', should begin with '%s.'.",
|
||||
$code,
|
||||
'PHPCS'));
|
||||
}
|
||||
return $code;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* A linter for Puppet files.
|
||||
*/
|
||||
final class ArcanistPuppetLintLinter extends ArcanistExternalLinter {
|
||||
|
||||
private $config;
|
||||
|
||||
public function getInfoName() {
|
||||
return 'puppet-lint';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'http://puppet-lint.com/';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'Use `%s` to check that your Puppet manifests '.
|
||||
'conform to the style guide.',
|
||||
'puppet-lint');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'PUPPETLINT';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'puppet-lint';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'puppet-lint';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
$regex = '/^puppet-lint (?P<version>\d+\.\d+\.\d+)$/';
|
||||
if (preg_match($regex, $stdout, $matches)) {
|
||||
return $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht(
|
||||
'Install puppet-lint using `%s`.',
|
||||
'gem install puppet-lint');
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
return array(
|
||||
'--error-level=all',
|
||||
sprintf('--log-format=%s', implode('|', array(
|
||||
'%{linenumber}',
|
||||
'%{column}',
|
||||
'%{kind}',
|
||||
'%{check}',
|
||||
'%{message}',
|
||||
))),
|
||||
);
|
||||
}
|
||||
|
||||
public function getLinterConfigurationOptions() {
|
||||
$options = array(
|
||||
'puppet-lint.config' => array(
|
||||
'type' => 'optional string',
|
||||
'help' => pht('Pass in a custom configuration file path.'),
|
||||
),
|
||||
);
|
||||
|
||||
return $options + parent::getLinterConfigurationOptions();
|
||||
}
|
||||
|
||||
public function setLinterConfigurationValue($key, $value) {
|
||||
switch ($key) {
|
||||
case 'puppet-lint.config':
|
||||
$this->config = $value;
|
||||
return;
|
||||
|
||||
default:
|
||||
return parent::setLinterConfigurationValue($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getDefaultFlags() {
|
||||
$options = array();
|
||||
|
||||
if ($this->config) {
|
||||
$options[] = '--config='.$this->config;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$lines = phutil_split_lines($stdout, false);
|
||||
$messages = array();
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$matches = explode('|', $line, 5);
|
||||
|
||||
if (count($matches) < 5) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$message = id(new ArcanistLintMessage())
|
||||
->setPath($path)
|
||||
->setLine($matches[0])
|
||||
->setChar($matches[1])
|
||||
->setCode($this->getLinterName())
|
||||
->setName(ucwords(str_replace('_', ' ', $matches[3])))
|
||||
->setDescription(ucfirst($matches[4]));
|
||||
|
||||
switch ($matches[2]) {
|
||||
case 'warning':
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
|
||||
break;
|
||||
|
||||
default:
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ADVICE);
|
||||
break;
|
||||
}
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Uses "PyFlakes" to detect various errors in Python code.
|
||||
*/
|
||||
final class ArcanistPyFlakesLinter extends ArcanistExternalLinter {
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'https://pypi.python.org/pypi/pyflakes';
|
||||
}
|
||||
|
||||
public function getInfoName() {
|
||||
return pht('Python PyFlakes');
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'PyFlakes is a simple program which checks Python source files for '.
|
||||
'errors.');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'PYFLAKES';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'pyflakes';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'pyflakes';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
if (preg_match('/^(?P<version>\d+\.\d+\.\d+)$/', $stdout, $matches)) {
|
||||
return $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht('Install pyflakes with `%s`.', 'pip install pyflakes');
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$lines = phutil_split_lines($stdout, false);
|
||||
|
||||
$messages = array();
|
||||
foreach ($lines as $line) {
|
||||
$matches = null;
|
||||
if (!preg_match('/^(.*?):(\d+): (.*)$/', $line, $matches)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($matches as $key => $match) {
|
||||
$matches[$key] = trim($match);
|
||||
}
|
||||
|
||||
$severity = ArcanistLintSeverity::SEVERITY_WARNING;
|
||||
$description = $matches[3];
|
||||
|
||||
$error_regexp = '/(^undefined|^duplicate|before assignment$)/';
|
||||
if (preg_match($error_regexp, $description)) {
|
||||
$severity = ArcanistLintSeverity::SEVERITY_ERROR;
|
||||
}
|
||||
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($path);
|
||||
$message->setLine($matches[2]);
|
||||
$message->setCode($this->getLinterName());
|
||||
$message->setName($this->getLinterName());
|
||||
$message->setDescription($description);
|
||||
$message->setSeverity($severity);
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
protected function canCustomizeLintSeverities() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,183 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Uses "PyLint" to detect various errors in Python code.
|
||||
*/
|
||||
final class ArcanistPyLintLinter extends ArcanistExternalLinter {
|
||||
|
||||
private $config;
|
||||
|
||||
public function getInfoName() {
|
||||
return 'PyLint';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'http://www.pylint.org/';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'PyLint is a Python source code analyzer which looks for '.
|
||||
'programming errors, helps enforcing a coding standard and '.
|
||||
'sniffs for some code smells.');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'PyLint';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'pylint';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'pylint';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
$regex = '/^pylint (?P<version>\d+\.\d+\.\d+),/';
|
||||
if (preg_match($regex, $stdout, $matches)) {
|
||||
return $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht(
|
||||
'Install PyLint using `%s`.',
|
||||
'pip install pylint');
|
||||
}
|
||||
|
||||
public function shouldExpectCommandErrors() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getLinterConfigurationOptions() {
|
||||
$options = array(
|
||||
'pylint.config' => array(
|
||||
'type' => 'optional string',
|
||||
'help' => pht('Pass in a custom configuration file path.'),
|
||||
),
|
||||
);
|
||||
|
||||
return $options + parent::getLinterConfigurationOptions();
|
||||
}
|
||||
|
||||
public function setLinterConfigurationValue($key, $value) {
|
||||
switch ($key) {
|
||||
case 'pylint.config':
|
||||
$this->config = $value;
|
||||
return;
|
||||
|
||||
default:
|
||||
return parent::setLinterConfigurationValue($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
$options = array();
|
||||
|
||||
$options[] = '--reports=no';
|
||||
$options[] = '--msg-template={line}|{column}|{msg_id}|{symbol}|{msg}';
|
||||
|
||||
// Specify an `--rcfile`, either absolute or relative to the project root.
|
||||
// Stupidly, the command line args above are overridden by rcfile, so be
|
||||
// careful.
|
||||
$config = $this->config;
|
||||
if ($config !== null) {
|
||||
$options[] = '--rcfile='.$config;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
protected function getDefaultFlags() {
|
||||
$options = array();
|
||||
|
||||
$installed_version = $this->getVersion();
|
||||
$minimum_version = '1.0.0';
|
||||
if (version_compare($installed_version, $minimum_version, '<')) {
|
||||
throw new ArcanistMissingLinterException(
|
||||
pht(
|
||||
'%s is not compatible with the installed version of pylint. '.
|
||||
'Minimum version: %s; installed version: %s.',
|
||||
__CLASS__,
|
||||
$minimum_version,
|
||||
$installed_version));
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
if ($err === 32) {
|
||||
// According to `man pylint` the exit status of 32 means there was a
|
||||
// usage error. That's bad, so actually exit abnormally.
|
||||
return false;
|
||||
}
|
||||
|
||||
$lines = phutil_split_lines($stdout, false);
|
||||
$messages = array();
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$matches = explode('|', $line, 5);
|
||||
|
||||
if (count($matches) < 5) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE: PyLint sometimes returns -1 as the character offset for a
|
||||
// message. If it does, treat it as 0. See T9257.
|
||||
$char = (int)$matches[1];
|
||||
$char = max(0, $char);
|
||||
|
||||
$message = id(new ArcanistLintMessage())
|
||||
->setPath($path)
|
||||
->setLine($matches[0])
|
||||
->setChar($char)
|
||||
->setCode($matches[2])
|
||||
->setSeverity($this->getLintMessageSeverity($matches[2]))
|
||||
->setName(ucwords(str_replace('-', ' ', $matches[3])))
|
||||
->setDescription($matches[4]);
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
protected function getDefaultMessageSeverity($code) {
|
||||
switch (substr($code, 0, 1)) {
|
||||
case 'R':
|
||||
case 'C':
|
||||
return ArcanistLintSeverity::SEVERITY_ADVICE;
|
||||
case 'W':
|
||||
return ArcanistLintSeverity::SEVERITY_WARNING;
|
||||
case 'E':
|
||||
case 'F':
|
||||
return ArcanistLintSeverity::SEVERITY_ERROR;
|
||||
default:
|
||||
return ArcanistLintSeverity::SEVERITY_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getLintCodeFromLinterConfigurationKey($code) {
|
||||
if (!preg_match('/^(R|C|W|E|F)\d{4}$/', $code)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unrecognized lint message code "%s". Expected a valid Pylint '.
|
||||
'lint code like "%s", or "%s", or "%s".',
|
||||
$code,
|
||||
'C0111',
|
||||
'E0602',
|
||||
'W0611'));
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistRuboCopLinter extends ArcanistExternalLinter {
|
||||
|
||||
private $config;
|
||||
|
||||
public function getInfoName() {
|
||||
return 'Ruby static code analyzer';
|
||||
}
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'http://batsov.com/rubocop';
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'RuboCop is a Ruby static code analyzer, based on the community Ruby '.
|
||||
'style guide.');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'RuboCop';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'rubocop';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'rubocop';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
if (preg_match('/^(?P<version>\d+\.\d+\.\d+)$/', $stdout, $matches)) {
|
||||
return $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht('Install RuboCop using `%s`.', 'gem install rubocop');
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
$options = array(
|
||||
'--format=json',
|
||||
);
|
||||
|
||||
if ($this->config) {
|
||||
$options[] = '--config='.$this->config;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function getLinterConfigurationOptions() {
|
||||
$options = array(
|
||||
'rubocop.config' => array(
|
||||
'type' => 'optional string',
|
||||
'help' => pht('A custom configuration file.'),
|
||||
),
|
||||
);
|
||||
|
||||
return $options + parent::getLinterConfigurationOptions();
|
||||
}
|
||||
|
||||
public function setLinterConfigurationValue($key, $value) {
|
||||
switch ($key) {
|
||||
case 'rubocop.config':
|
||||
$this->config = $value;
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::setLinterConfigurationValue($key, $value);
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$results = phutil_json_decode($stdout);
|
||||
$messages = array();
|
||||
|
||||
foreach ($results['files'] as $file) {
|
||||
foreach ($file['offenses'] as $offense) {
|
||||
$message = id(new ArcanistLintMessage())
|
||||
->setPath($file['path'])
|
||||
->setDescription($offense['message'])
|
||||
->setLine($offense['location']['line'])
|
||||
->setChar($offense['location']['column'])
|
||||
->setSeverity($this->getLintMessageSeverity($offense['severity']))
|
||||
->setName($this->getLinterName())
|
||||
->setCode($offense['cop_name']);
|
||||
$messages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the string from RuboCop's severity terminology and return an
|
||||
* @{class:ArcanistLintSeverity}.
|
||||
*/
|
||||
protected function getDefaultMessageSeverity($code) {
|
||||
switch ($code) {
|
||||
case 'convention':
|
||||
case 'refactor':
|
||||
case 'warning':
|
||||
return ArcanistLintSeverity::SEVERITY_WARNING;
|
||||
case 'error':
|
||||
case 'fatal':
|
||||
return ArcanistLintSeverity::SEVERITY_ERROR;
|
||||
default:
|
||||
return ArcanistLintSeverity::SEVERITY_ADVICE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Uses `ruby` to detect various errors in Ruby code.
|
||||
*/
|
||||
final class ArcanistRubyLinter extends ArcanistExternalLinter {
|
||||
|
||||
public function getInfoURI() {
|
||||
return 'https://www.ruby-lang.org/';
|
||||
}
|
||||
|
||||
public function getInfoName() {
|
||||
return pht('Ruby');
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'Use `%s` to check for syntax errors in Ruby source files.',
|
||||
'ruby');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'RUBY';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'ruby';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'ruby';
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
list($stdout) = execx('%C --version', $this->getExecutableCommand());
|
||||
|
||||
$matches = array();
|
||||
$regex = '/^ruby (?P<version>\d+\.\d+\.\d+)+/';
|
||||
if (preg_match($regex, $stdout, $matches)) {
|
||||
return $matches['version'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht('Install `%s` from <%s>.', 'ruby', 'http://www.ruby-lang.org/');
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
// -w: turn on warnings
|
||||
// -c: check syntax
|
||||
return array('-w', '-c');
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
$lines = phutil_split_lines($stderr, false);
|
||||
|
||||
$messages = array();
|
||||
foreach ($lines as $line) {
|
||||
$matches = null;
|
||||
|
||||
if (!preg_match('/(.*?):(\d+): (.*?)$/', $line, $matches)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($matches as $key => $match) {
|
||||
$matches[$key] = trim($match);
|
||||
}
|
||||
|
||||
$code = head(explode(',', $matches[3]));
|
||||
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($path);
|
||||
$message->setLine($matches[2]);
|
||||
$message->setCode($this->getLinterName());
|
||||
$message->setName(pht('Syntax Error'));
|
||||
$message->setDescription($matches[3]);
|
||||
$message->setSeverity($this->getLintMessageSeverity($code));
|
||||
|
||||
$messages[] = $message;
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,392 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Simple glue linter which runs some script on each path, and then uses a
|
||||
* regex to parse lint messages from the script's output. (This linter uses a
|
||||
* script and a regex to interpret the results of some real linter, it does
|
||||
* not itself lint both scripts and regexes).
|
||||
*
|
||||
* Configure this linter by setting these keys in your .arclint section:
|
||||
*
|
||||
* - `script-and-regex.script` Script command to run. This can be
|
||||
* the path to a linter script, but may also include flags or use shell
|
||||
* features (see below for examples).
|
||||
* - `script-and-regex.regex` The regex to process output with. This
|
||||
* regex uses named capturing groups (detailed below) to interpret output.
|
||||
*
|
||||
* The script will be invoked from the project root, so you can specify a
|
||||
* relative path like `scripts/lint.sh` or an absolute path like
|
||||
* `/opt/lint/lint.sh`.
|
||||
*
|
||||
* This linter is necessarily more limited in its capabilities than a normal
|
||||
* linter which can perform custom processing, but may be somewhat simpler to
|
||||
* configure.
|
||||
*
|
||||
* == Script... ==
|
||||
*
|
||||
* The script will be invoked once for each file that is to be linted, with
|
||||
* the file passed as the first argument. The file may begin with a "-"; ensure
|
||||
* your script will not interpret such files as flags (perhaps by ending your
|
||||
* script configuration with "--", if its argument parser supports that).
|
||||
*
|
||||
* Note that when run via `arc diff`, the list of files to be linted includes
|
||||
* deleted files and files that were moved away by the change. The linter should
|
||||
* not assume the path it is given exists, and it is not an error for the
|
||||
* linter to be invoked with paths which are no longer there. (Every affected
|
||||
* path is subject to lint because some linters may raise errors in other files
|
||||
* when a file is removed, or raise an error about its removal.)
|
||||
*
|
||||
* The script should emit lint messages to stdout, which will be parsed with
|
||||
* the provided regex.
|
||||
*
|
||||
* For example, you might use a configuration like this:
|
||||
*
|
||||
* /opt/lint/lint.sh --flag value --other-flag --
|
||||
*
|
||||
* stderr is ignored. If you have a script which writes messages to stderr,
|
||||
* you can redirect stderr to stdout by using a configuration like this:
|
||||
*
|
||||
* sh -c '/opt/lint/lint.sh "$0" 2>&1'
|
||||
*
|
||||
* The return code of the script must be 0, or an exception will be raised
|
||||
* reporting that the linter failed. If you have a script which exits nonzero
|
||||
* under normal circumstances, you can force it to always exit 0 by using a
|
||||
* configuration like this:
|
||||
*
|
||||
* sh -c '/opt/lint/lint.sh "$0" || true'
|
||||
*
|
||||
* Multiple instances of the script will be run in parallel if there are
|
||||
* multiple files to be linted, so they should not use any unique resources.
|
||||
* For instance, this configuration would not work properly, because several
|
||||
* processes may attempt to write to the file at the same time:
|
||||
*
|
||||
* COUNTEREXAMPLE
|
||||
* sh -c '/opt/lint/lint.sh --output /tmp/lint.out "$0" && cat /tmp/lint.out'
|
||||
*
|
||||
* There are necessary limits to how gracefully this linter can deal with
|
||||
* edge cases, because it is just a script and a regex. If you need to do
|
||||
* things that this linter can't handle, you can write a phutil linter and move
|
||||
* the logic to handle those cases into PHP. PHP is a better general-purpose
|
||||
* programming language than regular expressions are, if only by a small margin.
|
||||
*
|
||||
* == ...and Regex ==
|
||||
*
|
||||
* The regex must be a valid PHP PCRE regex, including delimiters and flags.
|
||||
*
|
||||
* The regex will be matched against the entire output of the script, so it
|
||||
* should generally be in this form if messages are one-per-line:
|
||||
*
|
||||
* /^...$/m
|
||||
*
|
||||
* The regex should capture these named patterns with `(?P<name>...)`:
|
||||
*
|
||||
* - `message` (required) Text describing the lint message. For example,
|
||||
* "This is a syntax error.".
|
||||
* - `name` (optional) Text summarizing the lint message. For example,
|
||||
* "Syntax Error".
|
||||
* - `severity` (optional) The word "error", "warning", "autofix", "advice",
|
||||
* or "disabled", in any combination of upper and lower case. Instead, you
|
||||
* may match groups called `error`, `warning`, `advice`, `autofix`, or
|
||||
* `disabled`. These allow you to match output formats like "E123" and
|
||||
* "W123" to indicate errors and warnings, even though the word "error" is
|
||||
* not present in the output. If no severity capturing group is present,
|
||||
* messages are raised with "error" severity. If multiple severity capturing
|
||||
* groups are present, messages are raised with the highest captured
|
||||
* severity. Capturing groups like `error` supersede the `severity`
|
||||
* capturing group.
|
||||
* - `error` (optional) Match some nonempty substring to indicate that this
|
||||
* message has "error" severity.
|
||||
* - `warning` (optional) Match some nonempty substring to indicate that this
|
||||
* message has "warning" severity.
|
||||
* - `advice` (optional) Match some nonempty substring to indicate that this
|
||||
* message has "advice" severity.
|
||||
* - `autofix` (optional) Match some nonempty substring to indicate that this
|
||||
* message has "autofix" severity.
|
||||
* - `disabled` (optional) Match some nonempty substring to indicate that this
|
||||
* message has "disabled" severity.
|
||||
* - `file` (optional) The name of the file to raise the lint message in. If
|
||||
* not specified, defaults to the linted file. It is generally not necessary
|
||||
* to capture this unless the linter can raise messages in files other than
|
||||
* the one it is linting.
|
||||
* - `line` (optional) The line number of the message. If no text is
|
||||
* captured, the message is assumed to affect the entire file.
|
||||
* - `char` (optional) The character offset of the message.
|
||||
* - `offset` (optional) The byte offset of the message. If captured, this
|
||||
* supersedes `line` and `char`.
|
||||
* - `original` (optional) The text the message affects.
|
||||
* - `replacement` (optional) The text that the range captured by `original`
|
||||
* should be automatically replaced by to resolve the message.
|
||||
* - `code` (optional) A short error type identifier which can be used
|
||||
* elsewhere to configure handling of specific types of messages. For
|
||||
* example, "EXAMPLE1", "EXAMPLE2", etc., where each code identifies a
|
||||
* class of message like "syntax error", "missing whitespace", etc. This
|
||||
* allows configuration to later change the severity of all whitespace
|
||||
* messages, for example.
|
||||
* - `ignore` (optional) Match some nonempty substring to ignore the match.
|
||||
* You can use this if your linter sometimes emits text like "No lint
|
||||
* errors".
|
||||
* - `stop` (optional) Match some nonempty substring to stop processing input.
|
||||
* Remaining matches for this file will be discarded, but linting will
|
||||
* continue with other linters and other files.
|
||||
* - `halt` (optional) Match some nonempty substring to halt all linting of
|
||||
* this file by any linter. Linting will continue with other files.
|
||||
* - `throw` (optional) Match some nonempty substring to throw an error, which
|
||||
* will stop `arc` completely. You can use this to fail abruptly if you
|
||||
* encounter unexpected output. All processing will abort.
|
||||
*
|
||||
* Numbered capturing groups are ignored.
|
||||
*
|
||||
* For example, if your lint script's output looks like this:
|
||||
*
|
||||
* error:13 Too many goats!
|
||||
* warning:22 Not enough boats.
|
||||
*
|
||||
* ...you could use this regex to parse it:
|
||||
*
|
||||
* /^(?P<severity>warning|error):(?P<line>\d+) (?P<message>.*)$/m
|
||||
*
|
||||
* The simplest valid regex for line-oriented output is something like this:
|
||||
*
|
||||
* /^(?P<message>.*)$/m
|
||||
*
|
||||
* @task lint Linting
|
||||
* @task linterinfo Linter Information
|
||||
* @task parse Parsing Output
|
||||
* @task config Validating Configuration
|
||||
*/
|
||||
final class ArcanistScriptAndRegexLinter extends ArcanistLinter {
|
||||
|
||||
private $script = null;
|
||||
private $regex = null;
|
||||
private $output = array();
|
||||
|
||||
public function getInfoName() {
|
||||
return pht('Script and Regex');
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht(
|
||||
'Run an external script, then parse its output with a regular '.
|
||||
'expression. This is a generic binding that can be used to '.
|
||||
'run custom lint scripts.');
|
||||
}
|
||||
|
||||
/* -( Linting )------------------------------------------------------------ */
|
||||
|
||||
|
||||
/**
|
||||
* Run the script on each file to be linted.
|
||||
*
|
||||
* @task lint
|
||||
*/
|
||||
public function willLintPaths(array $paths) {
|
||||
$root = $this->getProjectRoot();
|
||||
|
||||
$futures = array();
|
||||
foreach ($paths as $path) {
|
||||
$future = new ExecFuture('%C %s', $this->script, $path);
|
||||
$future->setCWD($root);
|
||||
$futures[$path] = $future;
|
||||
}
|
||||
|
||||
$futures = id(new FutureIterator($futures))
|
||||
->limit(4);
|
||||
foreach ($futures as $path => $future) {
|
||||
list($stdout) = $future->resolvex();
|
||||
$this->output[$path] = $stdout;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the regex on the output of the script.
|
||||
*
|
||||
* @task lint
|
||||
*/
|
||||
public function lintPath($path) {
|
||||
$output = idx($this->output, $path);
|
||||
if (!strlen($output)) {
|
||||
// No output, but it exited 0, so just move on.
|
||||
return;
|
||||
}
|
||||
|
||||
$matches = null;
|
||||
if (!preg_match_all($this->regex, $output, $matches, PREG_SET_ORDER)) {
|
||||
// Output with no matches. This might be a configuration error, but more
|
||||
// likely it's something like "No lint errors." and the user just hasn't
|
||||
// written a sufficiently powerful/ridiculous regexp to capture it into an
|
||||
// 'ignore' group. Don't make them figure this out; advanced users can
|
||||
// capture 'throw' to handle this case.
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($matches as $match) {
|
||||
if (!empty($match['throw'])) {
|
||||
$throw = $match['throw'];
|
||||
throw new ArcanistUsageException(
|
||||
pht(
|
||||
"%s: configuration captured a '%s' named capturing group, ".
|
||||
"'%s'. Script output:\n%s",
|
||||
__CLASS__,
|
||||
'throw',
|
||||
$throw,
|
||||
$output));
|
||||
}
|
||||
|
||||
if (!empty($match['halt'])) {
|
||||
$this->stopAllLinters();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!empty($match['stop'])) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!empty($match['ignore'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list($line, $char) = $this->getMatchLineAndChar($match, $path);
|
||||
|
||||
$dict = array(
|
||||
'path' => idx($match, 'file', $path),
|
||||
'line' => $line,
|
||||
'char' => $char,
|
||||
'code' => idx($match, 'code', $this->getLinterName()),
|
||||
'severity' => $this->getMatchSeverity($match),
|
||||
'name' => idx($match, 'name', 'Lint'),
|
||||
'description' => idx($match, 'message', pht('Undefined Lint Message')),
|
||||
);
|
||||
|
||||
$original = idx($match, 'original');
|
||||
if ($original !== null) {
|
||||
$dict['original'] = $original;
|
||||
}
|
||||
|
||||
$replacement = idx($match, 'replacement');
|
||||
if ($replacement !== null) {
|
||||
$dict['replacement'] = $replacement;
|
||||
}
|
||||
|
||||
$lint = ArcanistLintMessage::newFromDictionary($dict);
|
||||
$this->addLintMessage($lint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* -( Linter Information )------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Return the short name of the linter.
|
||||
*
|
||||
* @return string Short linter identifier.
|
||||
*
|
||||
* @task linterinfo
|
||||
*/
|
||||
public function getLinterName() {
|
||||
return 'S&RX';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'script-and-regex';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationOptions() {
|
||||
// These fields are optional only to avoid breaking things.
|
||||
$options = array(
|
||||
'script-and-regex.script' => array(
|
||||
'type' => 'string',
|
||||
'help' => pht('Script to execute.'),
|
||||
),
|
||||
'script-and-regex.regex' => array(
|
||||
'type' => 'regex',
|
||||
'help' => pht('The regex to process output with.'),
|
||||
),
|
||||
);
|
||||
|
||||
return $options + parent::getLinterConfigurationOptions();
|
||||
}
|
||||
|
||||
public function setLinterConfigurationValue($key, $value) {
|
||||
switch ($key) {
|
||||
case 'script-and-regex.script':
|
||||
$this->script = $value;
|
||||
return;
|
||||
case 'script-and-regex.regex':
|
||||
$this->regex = $value;
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::setLinterConfigurationValue($key, $value);
|
||||
}
|
||||
|
||||
/* -( Parsing Output )----------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the line and character of the message from the regex match.
|
||||
*
|
||||
* @param dict Captured groups from regex.
|
||||
* @return pair<int|null,int|null> Line and character of the message.
|
||||
*
|
||||
* @task parse
|
||||
*/
|
||||
private function getMatchLineAndChar(array $match, $path) {
|
||||
if (!empty($match['offset'])) {
|
||||
list($line, $char) = $this->getEngine()->getLineAndCharFromOffset(
|
||||
idx($match, 'file', $path),
|
||||
$match['offset']);
|
||||
return array($line + 1, $char + 1);
|
||||
}
|
||||
|
||||
$line = idx($match, 'line');
|
||||
if (strlen($line)) {
|
||||
$line = (int)$line;
|
||||
if (!$line) {
|
||||
$line = 1;
|
||||
}
|
||||
} else {
|
||||
$line = null;
|
||||
}
|
||||
|
||||
$char = idx($match, 'char');
|
||||
if ($char) {
|
||||
$char = (int)$char;
|
||||
} else {
|
||||
$char = null;
|
||||
}
|
||||
|
||||
return array($line, $char);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the regex matching groups to a message severity. We look for either
|
||||
* a nonempty severity name group like 'error', or a group called 'severity'
|
||||
* with a valid name.
|
||||
*
|
||||
* @param dict Captured groups from regex.
|
||||
* @return const @{class:ArcanistLintSeverity} constant.
|
||||
*
|
||||
* @task parse
|
||||
*/
|
||||
private function getMatchSeverity(array $match) {
|
||||
$map = array(
|
||||
'error' => ArcanistLintSeverity::SEVERITY_ERROR,
|
||||
'warning' => ArcanistLintSeverity::SEVERITY_WARNING,
|
||||
'autofix' => ArcanistLintSeverity::SEVERITY_AUTOFIX,
|
||||
'advice' => ArcanistLintSeverity::SEVERITY_ADVICE,
|
||||
'disabled' => ArcanistLintSeverity::SEVERITY_DISABLED,
|
||||
);
|
||||
|
||||
$severity_name = strtolower(idx($match, 'severity'));
|
||||
|
||||
foreach ($map as $name => $severity) {
|
||||
if (!empty($match[$name])) {
|
||||
return $severity;
|
||||
} else if ($severity_name == $name) {
|
||||
return $severity;
|
||||
}
|
||||
}
|
||||
|
||||
return ArcanistLintSeverity::SEVERITY_ERROR;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* A linter which uses [[http://php.net/simplexml | SimpleXML]] to detect
|
||||
* errors and potential problems in XML files.
|
||||
*/
|
||||
final class ArcanistXMLLinter extends ArcanistLinter {
|
||||
|
||||
public function getInfoName() {
|
||||
return pht('SimpleXML Linter');
|
||||
}
|
||||
|
||||
public function getInfoDescription() {
|
||||
return pht('Uses SimpleXML to detect formatting errors in XML files.');
|
||||
}
|
||||
|
||||
public function getLinterName() {
|
||||
return 'XML';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'xml';
|
||||
}
|
||||
|
||||
public function canRun() {
|
||||
return extension_loaded('libxml') && extension_loaded('simplexml');
|
||||
}
|
||||
|
||||
public function getCacheVersion() {
|
||||
return LIBXML_VERSION;
|
||||
}
|
||||
|
||||
public function lintPath($path) {
|
||||
libxml_use_internal_errors(true);
|
||||
libxml_clear_errors();
|
||||
|
||||
if (simplexml_load_string($this->getData($path))) {
|
||||
// XML appears to be valid.
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (libxml_get_errors() as $error) {
|
||||
$message = id(new ArcanistLintMessage())
|
||||
->setPath($path)
|
||||
->setLine($error->line)
|
||||
->setChar($error->column ? $error->column : null)
|
||||
->setCode($this->getLintMessageFullCode($error->code))
|
||||
->setName(pht('LibXML Error'))
|
||||
->setDescription(trim($error->message));
|
||||
|
||||
switch ($error->level) {
|
||||
case LIBXML_ERR_NONE:
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_DISABLED);
|
||||
break;
|
||||
|
||||
case LIBXML_ERR_WARNING:
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
|
||||
break;
|
||||
|
||||
case LIBXML_ERR_ERROR:
|
||||
case LIBXML_ERR_FATAL:
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
|
||||
break;
|
||||
|
||||
default:
|
||||
$message->setSeverity(ArcanistLintSeverity::SEVERITY_ADVICE);
|
||||
break;
|
||||
}
|
||||
|
||||
$this->addLintMessage($message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistCSSLintLinterTestCase
|
||||
extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/csslint/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistClosureLinterTestCase
|
||||
extends ArcanistExternalLinterTestCase {
|
||||
|
||||
protected function getLinter() {
|
||||
$linter = new ArcanistClosureLinter();
|
||||
$linter->setFlags(array('--additional_extensions=lint-test'));
|
||||
return $linter;
|
||||
}
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/gjslint/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistCoffeeLintLinterTestCase
|
||||
extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/coffeelint/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistCppcheckLinterTestCase
|
||||
extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/cppcheck/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistCpplintLinterTestCase
|
||||
extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/cpplint/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
abstract class ArcanistExternalLinterTestCase extends ArcanistLinterTestCase {
|
||||
|
||||
final public function testVersion() {
|
||||
try {
|
||||
$version = $this->getLinter()->getVersion();
|
||||
$this->assertTrue(
|
||||
$version !== false,
|
||||
pht('Failed to parse version from command.'));
|
||||
} catch (ArcanistMissingLinterException $ex) {
|
||||
$this->assertSkipped($ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistFlake8LinterTestCase
|
||||
extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/flake8/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistGoLintLinterTestCase
|
||||
extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/golint/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistHLintLinterTestCase extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/hlint/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistJSHintLinterTestCase
|
||||
extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/jshint/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistJSONLintLinterTestCase
|
||||
extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/jsonlint/');
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
final class ArcanistJSONLinterTestCase extends ArcanistLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/jsonlint/');
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/json/');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistJscsLinterTestCase extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/jscs/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistLesscLinterTestCase extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/lessc/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistPEP8LinterTestCase extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/pep8/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistPhpLinterTestCase extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/php/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistPhpcsLinterTestCase extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/phpcs/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistPuppetLintLinterTestCase
|
||||
extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/puppet-lint/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistPyFlakesLinterTestCase
|
||||
extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/pyflakes/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistPyLintLinterTestCase
|
||||
extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/pylint/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistRuboCopLinterTestCase
|
||||
extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/rubocop/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistRubyLinterTestCase extends ArcanistExternalLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/ruby/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Test cases were mostly taken from
|
||||
* https://git.gnome.org/browse/libxml2/tree/test.
|
||||
*/
|
||||
final class ArcanistXMLLinterTestCase extends ArcanistLinterTestCase {
|
||||
|
||||
public function testLinter() {
|
||||
$this->executeTestsInDirectory(dirname(__FILE__).'/xml/');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
class boaConstrictor
|
||||
~~~~~~~~~~
|
||||
error:1:
|
|
@ -1,18 +0,0 @@
|
|||
class SomeThing
|
||||
getConfig: ->
|
||||
one = 1
|
||||
one = 5
|
||||
@config =
|
||||
keyA: one
|
||||
keyB: one
|
||||
keyA: 2
|
||||
getConfig: ->
|
||||
@config =
|
||||
foo: 1
|
||||
|
||||
@getConfig: ->
|
||||
config =
|
||||
foo: 1
|
||||
~~~~~~~~~~
|
||||
error:8:
|
||||
error:9:
|
|
@ -1,7 +0,0 @@
|
|||
twoSpaces = () ->
|
||||
fourSpaces = () ->
|
||||
eightSpaces = () ->
|
||||
'this is valid CoffeeScript'
|
||||
~~~~~~~~~~
|
||||
error:3:
|
||||
error:4:
|
|
@ -1,3 +0,0 @@
|
|||
#--------------------------------------------------------------------------------
|
||||
~~~~~~~~~~
|
||||
error:1:
|
|
@ -1,3 +0,0 @@
|
|||
`with(document) alert(height);`
|
||||
~~~~~~~~~~
|
||||
error:1:
|
|
@ -1,3 +0,0 @@
|
|||
debugger
|
||||
~~~~~~~~~~
|
||||
warning:1:
|
|
@ -1,6 +0,0 @@
|
|||
x = () ->
|
||||
y = () ->
|
||||
return 1234
|
||||
~~~~~~~~~~
|
||||
error:2:
|
||||
error:3:
|
|
@ -1,3 +0,0 @@
|
|||
throw "i made a boo boo"
|
||||
~~~~~~~~~~
|
||||
error:1:
|
|
@ -1,3 +0,0 @@
|
|||
alert('end of line');
|
||||
~~~~~~~~~~
|
||||
error:1:
|
|
@ -1,4 +0,0 @@
|
|||
x = 1234
|
||||
y = 1
|
||||
~~~~~~~~~~
|
||||
error:1:
|
|
@ -1,9 +0,0 @@
|
|||
int main()
|
||||
{
|
||||
char a[10];
|
||||
a[10] = 0;
|
||||
return 0;
|
||||
}
|
||||
~~~~~~~~~~
|
||||
error:4:
|
||||
warning:4:
|
|
@ -1,7 +0,0 @@
|
|||
void f() {
|
||||
char arr[5];
|
||||
// cppcheck-suppress arrayIndexOutOfBounds
|
||||
// cppcheck-suppress unreadVariable
|
||||
arr[10] = 0;
|
||||
}
|
||||
~~~~~~~~~~
|
|
@ -1,5 +0,0 @@
|
|||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
~~~~~~~~~~
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* Taken from http://www.slideshare.net/zblair/cppcheck-10316379
|
||||
*/
|
||||
void foo(char* str) {
|
||||
char* buf = new char[8];
|
||||
strcpy(buf, str);
|
||||
|
||||
FILE* file = fopen("out.txt", "w");
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
for (char* c = buf; *c; ++c)
|
||||
fputc((int)*c, file);
|
||||
|
||||
delete buf;
|
||||
}
|
||||
~~~~~~~~~~
|
||||
error:10:
|
||||
error:16:
|
|
@ -1,10 +0,0 @@
|
|||
#include "library.cpp"
|
||||
#include <stdio>
|
||||
|
||||
void main()
|
||||
{
|
||||
}
|
||||
~~~~~~~~~~
|
||||
warning:0:
|
||||
warning:2:
|
||||
warning:5:
|
|
@ -1,10 +0,0 @@
|
|||
.rule {
|
||||
font-weight: bold;
|
||||
font-weight: bold;
|
||||
font-weight: bold;
|
||||
font-weight: bold;
|
||||
}
|
||||
~~~~~~~~~~
|
||||
warning:3:3
|
||||
warning:4:3
|
||||
warning:5:3
|
|
@ -1,3 +0,0 @@
|
|||
.rule { }
|
||||
~~~~~~~~~~
|
||||
warning:1:1
|
|
@ -1,9 +0,0 @@
|
|||
h1 {
|
||||
font-weight: bold;
|
||||
}
|
||||
h1 {
|
||||
font-weight: bold;
|
||||
}
|
||||
~~~~~~~~~~
|
||||
warning::
|
||||
warning:4:1
|
|
@ -1,6 +0,0 @@
|
|||
li {
|
||||
float: left;
|
||||
float left;
|
||||
}
|
||||
~~~~~~~~~~
|
||||
error:3:9
|
|
@ -1,7 +0,0 @@
|
|||
x = 'y'
|
||||
|
||||
def hello():
|
||||
return foo
|
||||
~~~~~~~~~~
|
||||
error:3:1
|
||||
error:4:12
|
|
@ -1,20 +0,0 @@
|
|||
var x = 'some really really really really really really really long string';
|
||||
var y = 'some really really really really really really really really really long string',
|
||||
z = 14;
|
||||
var obj = {name : 'vm'};
|
||||
var x
|
||||
var result = true
|
||||
|| false;
|
||||
/**
|
||||
* @param x
|
||||
* @return {string}
|
||||
*/
|
||||
function foo(x) {
|
||||
return 'bar';
|
||||
}
|
||||
~~~~~~~~~~
|
||||
error:2:
|
||||
error:4:
|
||||
error:5:
|
||||
error:7:
|
||||
error:9:
|
|
@ -1,18 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"rand"
|
||||
)
|
||||
|
||||
type Exported string
|
||||
|
||||
func main() {
|
||||
if rand.Intn(10) > 5 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
~~~~~~~~~~
|
||||
advice:7:6
|
||||
advice:12:12
|
|
@ -1,3 +0,0 @@
|
|||
f xs = length xs == 0
|
||||
~~~~~~~~~~
|
||||
warning:1:8
|
|
@ -1,5 +0,0 @@
|
|||
f x = x
|
||||
|
||||
test x = f x
|
||||
~~~~~~~~~~
|
||||
error:3:1
|
|
@ -1,2 +0,0 @@
|
|||
main = map (f . g) xs
|
||||
~~~~~~~~~~
|
|
@ -1,13 +0,0 @@
|
|||
function foo() {
|
||||
if (true) return "foo"; else return "bar";
|
||||
}
|
||||
~~~~~~~~~~
|
||||
error:2:3
|
||||
error:2:27
|
||||
~~~~~~~~~~
|
||||
~~~~~~~~~~
|
||||
{
|
||||
"config": {
|
||||
"jscs.preset": "jquery"
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
var args = {};
|
||||
args['foo'] = 'bar';
|
||||
args['bar'] = 'baz';
|
||||
~~~~~~~~~~
|
||||
warning:2:5
|
||||
warning:3:5
|
|
@ -1,6 +0,0 @@
|
|||
var foo;
|
||||
if (foo = 'bar') {
|
||||
return true;
|
||||
}
|
||||
~~~~~~~~~~
|
||||
warning:2:16
|
|
@ -1,12 +0,0 @@
|
|||
function f() {
|
||||
for (ii = 0; ii < 3; ii++) {
|
||||
g()
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
~~~~~~~~~~
|
||||
warning:3:8
|
||||
error:7:1
|
||||
error:9:
|
|
@ -1,3 +0,0 @@
|
|||
console.log('foobar')
|
||||
~~~~~~~~~~
|
||||
warning:1:22
|
|
@ -1,5 +0,0 @@
|
|||
/* jshint maxerr: 1 */
|
||||
console.log('foobar')
|
||||
~~~~~~~~~~
|
||||
disabled:2:22
|
||||
warning:2:22
|
|
@ -1,5 +0,0 @@
|
|||
function main() {
|
||||
return 'Hello, World!';
|
||||
};
|
||||
~~~~~~~~~~
|
||||
warning:3:2
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue