code-auditor-mcp 5.0.2 → 5.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +326 -3
- package/README.md +24 -2
- package/configs/hhra-compat.json +12 -118
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js +11 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +57 -52
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
- package/dist/analyzers/provenance.d.ts +9 -4
- package/dist/analyzers/provenance.d.ts.map +1 -1
- package/dist/analyzers/provenance.js +8 -25
- package/dist/analyzers/provenance.js.map +1 -1
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +35 -14
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/ruleRegistry.js +4 -4
- package/dist/analyzers/ruleRegistry.js.map +1 -1
- package/dist/analyzers/ruleTiming.d.ts +1 -1
- package/dist/analyzers/ruleTiming.js +2 -2
- package/dist/analyzers/ruleTiming.js.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +22 -26
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +0 -2
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +4 -5
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +32 -15
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +189 -115
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +0 -2
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +0 -5
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +2 -10
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js +7 -49
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.js +51 -3
- package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -1
- package/dist/analyzers/universal/schema/config.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/config.js +0 -2
- package/dist/analyzers/universal/schema/config.js.map +1 -1
- package/dist/analyzers/universal/schema/types.d.ts +0 -2
- package/dist/analyzers/universal/schema/types.d.ts.map +1 -1
- package/dist/analyzers/vendorSurfaces.d.ts +126 -0
- package/dist/analyzers/vendorSurfaces.d.ts.map +1 -0
- package/dist/analyzers/vendorSurfaces.js +580 -0
- package/dist/analyzers/vendorSurfaces.js.map +1 -0
- package/dist/auditRunner.d.ts +9 -1
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +198 -146
- package/dist/auditRunner.js.map +1 -1
- package/dist/cacheGc.d.ts +88 -0
- package/dist/cacheGc.d.ts.map +1 -0
- package/dist/cacheGc.js +196 -0
- package/dist/cacheGc.js.map +1 -0
- package/dist/churn/churnExtractor.d.ts +0 -1
- package/dist/churn/churnExtractor.d.ts.map +1 -1
- package/dist/churn/churnExtractor.js +2 -4
- package/dist/churn/churnExtractor.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +260 -47
- package/dist/cli.js.map +1 -1
- package/dist/codeIndex/migrations.d.ts.map +1 -1
- package/dist/codeIndex/migrations.js +4 -3
- package/dist/codeIndex/migrations.js.map +1 -1
- package/dist/codeIndexDB.d.ts +11 -2
- package/dist/codeIndexDB.d.ts.map +1 -1
- package/dist/codeIndexDB.js +27 -2
- package/dist/codeIndexDB.js.map +1 -1
- package/dist/config/configLoader.d.ts +60 -7
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +252 -60
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts +137 -43
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +174 -101
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/effectiveConfig.d.ts +1 -1
- package/dist/config/effectiveConfig.d.ts.map +1 -1
- package/dist/config/effectiveConfig.js +4 -6
- package/dist/config/effectiveConfig.js.map +1 -1
- package/dist/config/pathProfiles.d.ts +4 -3
- package/dist/config/pathProfiles.d.ts.map +1 -1
- package/dist/config/pathProfiles.js +3 -2
- package/dist/config/pathProfiles.js.map +1 -1
- package/dist/constants.d.ts +2 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +4 -0
- package/dist/constants.js.map +1 -1
- package/dist/daemon/main.d.ts +0 -1
- package/dist/daemon/main.d.ts.map +1 -1
- package/dist/daemon/main.js +11 -4
- package/dist/daemon/main.js.map +1 -1
- package/dist/dataPaths.d.ts +32 -9
- package/dist/dataPaths.d.ts.map +1 -1
- package/dist/dataPaths.js +69 -16
- package/dist/dataPaths.js.map +1 -1
- package/dist/hookLog.d.ts +54 -0
- package/dist/hookLog.d.ts.map +1 -0
- package/dist/hookLog.js +105 -0
- package/dist/hookLog.js.map +1 -0
- package/dist/hookLogPath.generated.d.ts +3 -0
- package/dist/hookLogPath.generated.d.ts.map +1 -0
- package/dist/hookLogPath.generated.js +8 -0
- package/dist/hookLogPath.generated.js.map +1 -0
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -7
- package/dist/index.js.map +1 -1
- package/dist/installConfig.d.ts +15 -2
- package/dist/installConfig.d.ts.map +1 -1
- package/dist/installConfig.js +17 -2
- package/dist/installConfig.js.map +1 -1
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +2 -1
- package/dist/installer.js.map +1 -1
- package/dist/languages/adapterBridge.d.ts +11 -0
- package/dist/languages/adapterBridge.d.ts.map +1 -1
- package/dist/languages/adapterBridge.js +11 -0
- package/dist/languages/adapterBridge.js.map +1 -1
- package/dist/languages/types.d.ts +8 -0
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +13 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +135 -18
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
- package/dist/ledger.d.ts +7 -0
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +7 -0
- package/dist/ledger.js.map +1 -1
- package/dist/mcp-tools/workflowGuide.js +8 -8
- package/dist/mcp-tools/workflowGuide.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js +5 -2
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/mcp-ui-simple.d.ts +5 -0
- package/dist/mcp-ui-simple.d.ts.map +1 -1
- package/dist/mcp-ui-simple.js +9 -2
- package/dist/mcp-ui-simple.js.map +1 -1
- package/dist/mcp.d.ts +0 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +36 -33
- package/dist/mcp.js.map +1 -1
- package/dist/mcpAuditJobs.d.ts +10 -0
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +10 -1
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/mcpAutoIndex.d.ts.map +1 -1
- package/dist/mcpAutoIndex.js +2 -1
- package/dist/mcpAutoIndex.js.map +1 -1
- package/dist/mcpDiagnostics.d.ts +2 -0
- package/dist/mcpDiagnostics.d.ts.map +1 -1
- package/dist/mcpDiagnostics.js +2 -0
- package/dist/mcpDiagnostics.js.map +1 -1
- package/dist/phase/candidates.d.ts +53 -0
- package/dist/phase/candidates.d.ts.map +1 -0
- package/dist/phase/candidates.js +67 -0
- package/dist/phase/candidates.js.map +1 -0
- package/dist/phase/colorMath.d.ts +18 -5
- package/dist/phase/colorMath.d.ts.map +1 -1
- package/dist/phase/colorMath.js +88 -5
- package/dist/phase/colorMath.js.map +1 -1
- package/dist/phase/dataAccessCallsCandidates.js +1 -1
- package/dist/phase/dataAccessCallsCandidates.js.map +1 -1
- package/dist/phase/goFunctionAnalysis.d.ts +2 -2
- package/dist/phase/goFunctionAnalysis.js +2 -2
- package/dist/phase/goFunctions.d.ts +2 -2
- package/dist/phase/goFunctions.js +2 -2
- package/dist/phase/goImports.d.ts +2 -2
- package/dist/phase/goImports.js +2 -2
- package/dist/phase/goSwitches.d.ts +2 -2
- package/dist/phase/goSwitches.js +2 -2
- package/dist/phase/imports.d.ts.map +1 -1
- package/dist/phase/imports.js +1 -0
- package/dist/phase/imports.js.map +1 -1
- package/dist/phase/loopQueryCandidates.js +1 -1
- package/dist/phase/loopQueryCandidates.js.map +1 -1
- package/dist/phase/oracles.d.ts +28 -0
- package/dist/phase/oracles.d.ts.map +1 -1
- package/dist/phase/oracles.js +28 -0
- package/dist/phase/oracles.js.map +1 -1
- package/dist/phase/phaseModel.d.ts +47 -8
- package/dist/phase/phaseModel.d.ts.map +1 -1
- package/dist/phase/phaseModel.js +44 -8
- package/dist/phase/phaseModel.js.map +1 -1
- package/dist/phase/producers.d.ts.map +1 -1
- package/dist/phase/producers.js +24 -17
- package/dist/phase/producers.js.map +1 -1
- package/dist/phase/reachability.d.ts +44 -1
- package/dist/phase/reachability.d.ts.map +1 -1
- package/dist/phase/reachability.js +79 -0
- package/dist/phase/reachability.js.map +1 -1
- package/dist/phase/rules/conventions.d.ts.map +1 -1
- package/dist/phase/rules/conventions.js +6 -2
- package/dist/phase/rules/conventions.js.map +1 -1
- package/dist/phase/rules/crossDomain.d.ts.map +1 -1
- package/dist/phase/rules/crossDomain.js +16 -6
- package/dist/phase/rules/crossDomain.js.map +1 -1
- package/dist/phase/rules/dataAccess.d.ts.map +1 -1
- package/dist/phase/rules/dataAccess.js +13 -0
- package/dist/phase/rules/dataAccess.js.map +1 -1
- package/dist/phase/rules/documentation.d.ts.map +1 -1
- package/dist/phase/rules/documentation.js +11 -1
- package/dist/phase/rules/documentation.js.map +1 -1
- package/dist/phase/rules/dry.d.ts +16 -7
- package/dist/phase/rules/dry.d.ts.map +1 -1
- package/dist/phase/rules/dry.js +51 -32
- package/dist/phase/rules/dry.js.map +1 -1
- package/dist/phase/rules/goRules.d.ts +2 -2
- package/dist/phase/rules/goRules.js +2 -2
- package/dist/phase/rules/secrets.d.ts +22 -8
- package/dist/phase/rules/secrets.d.ts.map +1 -1
- package/dist/phase/rules/secrets.js +75 -69
- package/dist/phase/rules/secrets.js.map +1 -1
- package/dist/phase/rules/styles.d.ts +23 -9
- package/dist/phase/rules/styles.d.ts.map +1 -1
- package/dist/phase/rules/styles.js +61 -15
- package/dist/phase/rules/styles.js.map +1 -1
- package/dist/phase/rules/unreferencedModule.d.ts.map +1 -1
- package/dist/phase/rules/unreferencedModule.js +32 -10
- package/dist/phase/rules/unreferencedModule.js.map +1 -1
- package/dist/phase/runner.d.ts +16 -7
- package/dist/phase/runner.d.ts.map +1 -1
- package/dist/phase/runner.js +32 -20
- package/dist/phase/runner.js.map +1 -1
- package/dist/phase/stringLiterals.d.ts.map +1 -1
- package/dist/phase/stringLiterals.js +16 -7
- package/dist/phase/stringLiterals.js.map +1 -1
- package/dist/phase/stylesCss.d.ts.map +1 -1
- package/dist/phase/stylesCss.js +4 -0
- package/dist/phase/stylesCss.js.map +1 -1
- package/dist/phase/stylesMarkup.d.ts.map +1 -1
- package/dist/phase/stylesMarkup.js +4 -0
- package/dist/phase/stylesMarkup.js.map +1 -1
- package/dist/phase/stylesSource.d.ts.map +1 -1
- package/dist/phase/stylesSource.js +6 -0
- package/dist/phase/stylesSource.js.map +1 -1
- package/dist/phase/testProgram.d.ts +57 -0
- package/dist/phase/testProgram.d.ts.map +1 -0
- package/dist/phase/testProgram.js +244 -0
- package/dist/phase/testProgram.js.map +1 -0
- package/dist/phase/typeDeclarations.d.ts +2 -2
- package/dist/phase/typeDeclarations.js +2 -2
- package/dist/phase/types.d.ts +104 -0
- package/dist/phase/types.d.ts.map +1 -1
- package/dist/pipeline.d.ts +4 -0
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +4 -0
- package/dist/pipeline.js.map +1 -1
- package/dist/pipelineAdapters.d.ts +1 -171
- package/dist/pipelineAdapters.d.ts.map +1 -1
- package/dist/pipelineAdapters.js +47 -1281
- package/dist/pipelineAdapters.js.map +1 -1
- package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
- package/dist/reporting/csvReportGenerator.js +0 -6
- package/dist/reporting/csvReportGenerator.js.map +1 -1
- package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
- package/dist/reporting/htmlReportGenerator.js +0 -4
- package/dist/reporting/htmlReportGenerator.js.map +1 -1
- package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
- package/dist/reporting/jsonReportGenerator.js +0 -1
- package/dist/reporting/jsonReportGenerator.js.map +1 -1
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
- package/dist/reporting/sarifReportGenerator.js +0 -7
- package/dist/reporting/sarifReportGenerator.js.map +1 -1
- package/dist/services/auditJobService.d.ts +15 -0
- package/dist/services/auditJobService.d.ts.map +1 -1
- package/dist/services/auditJobService.js +15 -0
- package/dist/services/auditJobService.js.map +1 -1
- package/dist/services/projectTaskHelpers.d.ts +7 -0
- package/dist/services/projectTaskHelpers.d.ts.map +1 -1
- package/dist/services/projectTaskHelpers.js +7 -0
- package/dist/services/projectTaskHelpers.js.map +1 -1
- package/dist/services/whitelistService.d.ts +28 -0
- package/dist/services/whitelistService.d.ts.map +1 -1
- package/dist/services/whitelistService.js +28 -0
- package/dist/services/whitelistService.js.map +1 -1
- package/dist/sqlite/driver.d.ts +17 -1
- package/dist/sqlite/driver.d.ts.map +1 -1
- package/dist/sqlite/driver.js +28 -5
- package/dist/sqlite/driver.js.map +1 -1
- package/dist/styles/cssInJsHarvest.d.ts +36 -0
- package/dist/styles/cssInJsHarvest.d.ts.map +1 -0
- package/dist/styles/cssInJsHarvest.js +78 -0
- package/dist/styles/cssInJsHarvest.js.map +1 -0
- package/dist/styles/definedClasses.d.ts +95 -0
- package/dist/styles/definedClasses.d.ts.map +1 -0
- package/dist/styles/definedClasses.js +129 -0
- package/dist/styles/definedClasses.js.map +1 -0
- package/dist/styles/markupStyleHarvest.d.ts +33 -0
- package/dist/styles/markupStyleHarvest.d.ts.map +1 -0
- package/dist/styles/markupStyleHarvest.js +73 -0
- package/dist/styles/markupStyleHarvest.js.map +1 -0
- package/dist/styles/styleExtractor.d.ts +37 -0
- package/dist/styles/styleExtractor.d.ts.map +1 -1
- package/dist/styles/styleExtractor.js +89 -33
- package/dist/styles/styleExtractor.js.map +1 -1
- package/dist/styles/styleIndexer.d.ts.map +1 -1
- package/dist/styles/styleIndexer.js +11 -10
- package/dist/styles/styleIndexer.js.map +1 -1
- package/dist/styles/tailwindExpander.d.ts.map +1 -1
- package/dist/styles/tailwindExpander.js +31 -10
- package/dist/styles/tailwindExpander.js.map +1 -1
- package/dist/styles/tailwindUtilityExpander.d.ts +4 -0
- package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
- package/dist/styles/tailwindUtilityExpander.js +4 -0
- package/dist/styles/tailwindUtilityExpander.js.map +1 -1
- package/dist/telemetry.d.ts +67 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +100 -20
- package/dist/telemetry.js.map +1 -1
- package/dist/types/crossLanguage.d.ts +1 -0
- package/dist/types/crossLanguage.d.ts.map +1 -1
- package/dist/types.d.ts +73 -56
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -4
- package/dist/types.js.map +1 -1
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/examples/.auditrc.example.json +7 -22
- package/examples/.auditrc.json +12 -155
- package/examples/.code-auditor.json +8 -51
- package/examples/nextjs.auditrc.json +7 -34
- package/examples/node-api.auditrc.json +6 -46
- package/examples/react.auditrc.json +8 -25
- package/examples/tolerant-solid.auditrc.json +12 -44
- package/package.json +8 -5
- package/plugin/.claude-plugin/manifest.json +10 -9
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/hooks/hooks.json +4 -4
- package/plugin/scripts/hook-audit.sh +25 -10
- package/plugin/scripts/hook-common.sh +12 -7
- package/plugin/scripts/hook-drift-check.sh +10 -1
- package/plugin/scripts/hook-log-path.sh +7 -0
- package/plugin/scripts/hook-self-audit.sh +21 -6
- package/plugin/scripts/hook-warm.sh +17 -11
- package/plugin/skills/code-auditor/SKILL.md +26 -3
- package/server.json +2 -2
- package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts +0 -6
- package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-20/scripts/tool.js +0 -12
- package/dist/__tests__/fixtures/spec-20/scripts/tool.js.map +0 -1
- package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts +0 -7
- package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-20/src/demo-module.js +0 -13
- package/dist/__tests__/fixtures/spec-20/src/demo-module.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.d.ts +0 -14
- package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.js +0 -16
- package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.d.ts +0 -15
- package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.js +0 -17
- package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts +0 -14
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js +0 -19
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts +0 -18
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js +0 -19
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.d.ts +0 -19
- package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.js +0 -19
- package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts +0 -17
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js +0 -19
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts +0 -25
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js +0 -21
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.d.ts +0 -15
- package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.js +0 -19
- package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.d.ts +0 -14
- package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.js +0 -14
- package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.js.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts +0 -15
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts.map +0 -1
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js +0 -16
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js.map +0 -1
- package/dist/languages/go/analyzer-darwin-amd64 +0 -0
- package/dist/languages/go/analyzer-darwin-arm64 +0 -0
- package/dist/languages/go/analyzer-linux-amd64 +0 -0
- package/dist/languages/go/analyzer-linux-arm64 +0 -0
- package/dist/languages/go/analyzer-src/analyzer.go +0 -643
- package/dist/languages/go/analyzer-src/dataaccess.go +0 -388
- package/dist/languages/go/analyzer-src/go.mod +0 -3
- package/dist/languages/go/analyzer-src/indexer.go +0 -268
- package/dist/languages/go/analyzer-src/parser.go +0 -461
- package/dist/languages/go/analyzer-src/solid.go +0 -311
- package/dist/languages/go/analyzer-src/testconventions.go +0 -25
- package/dist/languages/go/analyzer-src/types.go +0 -156
- package/dist/languages/go/analyzer-windows-amd64.exe +0 -0
- package/dist/languages/go/go.mod +0 -7
- package/dist/languages/go/main.go +0 -203
- package/dist/native/ast-grep-napi.darwin-arm64.node +0 -0
- package/dist/native/ast-grep-napi.darwin-x64.node +0 -0
- package/dist/native/ast-grep-napi.linux-arm64-gnu.node +0 -0
- package/dist/native/ast-grep-napi.linux-arm64-musl.node +0 -0
- package/dist/native/ast-grep-napi.linux-x64-gnu.node +0 -0
- package/dist/native/ast-grep-napi.linux-x64-musl.node +0 -0
- package/dist/native/ast-grep-napi.win32-x64-msvc.node +0 -0
- package/dist/native-bootstrap.d.ts +0 -15
- package/dist/native-bootstrap.d.ts.map +0 -1
- package/dist/native-bootstrap.js +0 -75
- package/dist/native-bootstrap.js.map +0 -1
|
@@ -1,643 +0,0 @@
|
|
|
1
|
-
package analyzer
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"go/ast"
|
|
5
|
-
"go/token"
|
|
6
|
-
"sort"
|
|
7
|
-
"strconv"
|
|
8
|
-
"strings"
|
|
9
|
-
"time"
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
// Analyzer is the main Go code analyzer
|
|
13
|
-
type Analyzer struct {
|
|
14
|
-
options AnalysisOptions
|
|
15
|
-
parser *Parser
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// NewAnalyzer creates a new Go analyzer
|
|
19
|
-
func NewAnalyzer(options AnalysisOptions) *Analyzer {
|
|
20
|
-
parser := NewParser(options)
|
|
21
|
-
return &Analyzer{
|
|
22
|
-
options: options,
|
|
23
|
-
parser: parser,
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Analyze performs comprehensive analysis of Go files
|
|
28
|
-
func (a *Analyzer) Analyze(files []string) (*AnalysisResult, error) {
|
|
29
|
-
startTime := time.Now()
|
|
30
|
-
|
|
31
|
-
// Exclude *_test.go files before parsing: test code is not production API.
|
|
32
|
-
// Mirrors languages/testConventions.ts go.filePatterns.
|
|
33
|
-
files = excludeTestFiles(files)
|
|
34
|
-
|
|
35
|
-
// Parse all files
|
|
36
|
-
if err := a.parser.ParseFiles(files); err != nil {
|
|
37
|
-
return nil, err
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
result := &AnalysisResult{
|
|
41
|
-
Violations: []Violation{},
|
|
42
|
-
IndexEntries: []IndexEntry{},
|
|
43
|
-
Metrics: Metrics{
|
|
44
|
-
FilesAnalyzed: int64(len(files)),
|
|
45
|
-
ExecutionTime: 0, // Will be set at the end
|
|
46
|
-
},
|
|
47
|
-
Errors: []Error{},
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Run enabled analyzers
|
|
51
|
-
a.runEnabledAnalyzers(result)
|
|
52
|
-
|
|
53
|
-
// Generate index entries
|
|
54
|
-
indexer := NewIndexer(a.parser)
|
|
55
|
-
result.IndexEntries = indexer.GenerateIndexEntries()
|
|
56
|
-
|
|
57
|
-
// Filter violations by severity
|
|
58
|
-
result.Violations = a.filterViolationsBySeverity(result.Violations)
|
|
59
|
-
|
|
60
|
-
// Calculate execution time
|
|
61
|
-
result.Metrics.ExecutionTime = time.Since(startTime).Milliseconds()
|
|
62
|
-
|
|
63
|
-
return result, nil
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// runEnabledAnalyzers runs each enabled analyzer and appends its violations to
|
|
67
|
-
// the result. Shared by Analyze and AnalyzeContent to avoid duplicated dispatch.
|
|
68
|
-
func (a *Analyzer) runEnabledAnalyzers(result *AnalysisResult) {
|
|
69
|
-
for _, analyzerName := range a.options.Analyzers {
|
|
70
|
-
switch analyzerName {
|
|
71
|
-
case "solid":
|
|
72
|
-
result.Violations = append(result.Violations, a.runSOLIDAnalysis()...)
|
|
73
|
-
case "imports":
|
|
74
|
-
result.Violations = append(result.Violations, a.runImportAnalysis()...)
|
|
75
|
-
case "errors":
|
|
76
|
-
result.Violations = append(result.Violations, a.runErrorAnalysis()...)
|
|
77
|
-
case "goroutines":
|
|
78
|
-
result.Violations = append(result.Violations, a.runGoroutineAnalysis()...)
|
|
79
|
-
case "channels":
|
|
80
|
-
result.Violations = append(result.Violations, a.runChannelAnalysis()...)
|
|
81
|
-
case "data-access":
|
|
82
|
-
result.Violations = append(result.Violations, a.runDataAccessAnalysis()...)
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// AnalyzeContent performs analysis of Go content from a string
|
|
88
|
-
func (a *Analyzer) AnalyzeContent(filePath, content string) (*AnalysisResult, error) {
|
|
89
|
-
startTime := time.Now()
|
|
90
|
-
|
|
91
|
-
// A *_test.go file is test code, not production API — exempt it the same
|
|
92
|
-
// way the multi-file path does (languages/testConventions.ts go.filePatterns).
|
|
93
|
-
if isTestFile(filePath) {
|
|
94
|
-
return &AnalysisResult{
|
|
95
|
-
Violations: []Violation{},
|
|
96
|
-
IndexEntries: []IndexEntry{},
|
|
97
|
-
Metrics: Metrics{
|
|
98
|
-
FilesAnalyzed: 0,
|
|
99
|
-
ExecutionTime: 0,
|
|
100
|
-
},
|
|
101
|
-
Errors: []Error{},
|
|
102
|
-
}, nil
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Parse content instead of file
|
|
106
|
-
if err := a.parser.ParseContent(filePath, content); err != nil {
|
|
107
|
-
return nil, err
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
result := &AnalysisResult{
|
|
111
|
-
Violations: []Violation{},
|
|
112
|
-
IndexEntries: []IndexEntry{},
|
|
113
|
-
Metrics: Metrics{
|
|
114
|
-
FilesAnalyzed: 1,
|
|
115
|
-
ExecutionTime: 0, // Will be set at the end
|
|
116
|
-
},
|
|
117
|
-
Errors: []Error{},
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Run enabled analyzers
|
|
121
|
-
a.runEnabledAnalyzers(result)
|
|
122
|
-
|
|
123
|
-
// Generate index entries
|
|
124
|
-
indexer := NewIndexer(a.parser)
|
|
125
|
-
result.IndexEntries = indexer.GenerateIndexEntries()
|
|
126
|
-
|
|
127
|
-
// Filter violations by severity
|
|
128
|
-
result.Violations = a.filterViolationsBySeverity(result.Violations)
|
|
129
|
-
|
|
130
|
-
// Calculate execution time
|
|
131
|
-
result.Metrics.ExecutionTime = time.Since(startTime).Milliseconds()
|
|
132
|
-
|
|
133
|
-
return result, nil
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// runSOLIDAnalysis runs SOLID principle analysis
|
|
137
|
-
func (a *Analyzer) runSOLIDAnalysis() []Violation {
|
|
138
|
-
solidAnalyzer := NewSOLIDAnalyzer(a.parser)
|
|
139
|
-
return solidAnalyzer.Analyze()
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// runImportAnalysis analyzes import usage and organization.
|
|
143
|
-
//
|
|
144
|
-
// The old `import-organization` predicate was a raw import count
|
|
145
|
-
// (`len(file.Imports) > 10`). Count is not "organization" — a file with twelve
|
|
146
|
-
// well-grouped imports is fine, and a file with two mis-grouped imports is not.
|
|
147
|
-
// The honest reading is grouping: Go convention (goimports/gofmt) requires
|
|
148
|
-
// standard-library imports first, then third-party, then local, each block
|
|
149
|
-
// sorted. A stdlib import appearing after a third-party import is the
|
|
150
|
-
// "mixed-up imports" case a reviewer actually flags. "Unnecessary deps" is out
|
|
151
|
-
// of scope — the Go compiler already rejects unused imports at build time, so a
|
|
152
|
-
// static analyzer adds no signal there.
|
|
153
|
-
func (a *Analyzer) runImportAnalysis() []Violation {
|
|
154
|
-
var violations []Violation
|
|
155
|
-
|
|
156
|
-
for filePath, file := range a.parser.files {
|
|
157
|
-
// Detect import grouping violations (stdlib vs third-party vs local).
|
|
158
|
-
if pos, grouped := firstImportGroupViolation(file.Imports); grouped > 0 {
|
|
159
|
-
line := a.parser.fileSet.Position(pos).Line
|
|
160
|
-
violations = append(violations, Violation{
|
|
161
|
-
File: filePath,
|
|
162
|
-
Line: line,
|
|
163
|
-
Severity: "high",
|
|
164
|
-
Message: "Import block mixes standard library and third-party imports without grouping",
|
|
165
|
-
Details: map[string]interface{}{
|
|
166
|
-
"importCount": len(file.Imports),
|
|
167
|
-
"groupingViolations": grouped,
|
|
168
|
-
},
|
|
169
|
-
Suggestion: "Group standard library imports first, then third-party imports, each block separated by a blank line",
|
|
170
|
-
Analyzer: "go",
|
|
171
|
-
Rule: "import-organization",
|
|
172
|
-
})
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Check for dot imports (considered bad practice)
|
|
176
|
-
for _, importSpec := range file.Imports {
|
|
177
|
-
if importSpec.Name != nil && importSpec.Name.Name == "." {
|
|
178
|
-
pos := a.parser.fileSet.Position(importSpec.Pos())
|
|
179
|
-
violations = append(violations, Violation{
|
|
180
|
-
File: filePath,
|
|
181
|
-
Line: pos.Line,
|
|
182
|
-
Severity: "high",
|
|
183
|
-
Message: "Dot import detected - can lead to namespace pollution",
|
|
184
|
-
Details: map[string]interface{}{
|
|
185
|
-
"import": importSpec.Path.Value,
|
|
186
|
-
},
|
|
187
|
-
Suggestion: "Use explicit import names instead of dot imports",
|
|
188
|
-
Analyzer: "go",
|
|
189
|
-
Rule: "import-style",
|
|
190
|
-
})
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return violations
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// runErrorAnalysis analyzes error handling patterns.
|
|
199
|
-
//
|
|
200
|
-
// It performs an errcheck-style walk: an error identifier assigned from a call
|
|
201
|
-
// result that is never compared, returned, passed to another call, or
|
|
202
|
-
// explicitly ignored is a dropped error — a likely bug regardless of what the
|
|
203
|
-
// enclosing function is named. This replaces the old name-substring proxy
|
|
204
|
-
// ("handle"/"check"/"validate"/"verify") that fired on any function whose name
|
|
205
|
-
// merely contained one of those words.
|
|
206
|
-
func (a *Analyzer) runErrorAnalysis() []Violation {
|
|
207
|
-
var violations []Violation
|
|
208
|
-
|
|
209
|
-
for filePath, file := range a.parser.files {
|
|
210
|
-
ast.Inspect(file, func(n ast.Node) bool {
|
|
211
|
-
funcDecl, ok := n.(*ast.FuncDecl)
|
|
212
|
-
if !ok || funcDecl.Body == nil || isTestFunction(funcDecl.Name.Name) {
|
|
213
|
-
return true
|
|
214
|
-
}
|
|
215
|
-
if !a.functionDropsError(funcDecl) {
|
|
216
|
-
return true
|
|
217
|
-
}
|
|
218
|
-
pos := a.parser.fileSet.Position(funcDecl.Pos())
|
|
219
|
-
violations = append(violations, Violation{
|
|
220
|
-
File: filePath,
|
|
221
|
-
Line: pos.Line,
|
|
222
|
-
Severity: "severe",
|
|
223
|
-
Message: "Function assigns an error that is never checked, returned, or propagated",
|
|
224
|
-
Details: map[string]interface{}{
|
|
225
|
-
"function": funcDecl.Name.Name,
|
|
226
|
-
},
|
|
227
|
-
Suggestion: "Check the error, return it, or explicitly ignore it with '_ = err'",
|
|
228
|
-
Analyzer: "go",
|
|
229
|
-
Rule: "error-handling",
|
|
230
|
-
})
|
|
231
|
-
return true
|
|
232
|
-
})
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
return violations
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// runGoroutineAnalysis analyzes goroutine usage for potential issues.
|
|
239
|
-
//
|
|
240
|
-
// It walks the AST for a `go` statement and reports the function when a
|
|
241
|
-
// goroutine is launched with no synchronization mechanism — sync primitives
|
|
242
|
-
// (Add/Done/Wait/Lock/Unlock/RLock/RUnlock) or a channel send/receive —
|
|
243
|
-
// anywhere in the same function. This replaces the old name-substring proxy
|
|
244
|
-
// ("go"/"async"/"concurrent") that fired on any function whose name merely
|
|
245
|
-
// contained those substrings.
|
|
246
|
-
func (a *Analyzer) runGoroutineAnalysis() []Violation {
|
|
247
|
-
var violations []Violation
|
|
248
|
-
|
|
249
|
-
for filePath, file := range a.parser.files {
|
|
250
|
-
ast.Inspect(file, func(n ast.Node) bool {
|
|
251
|
-
funcDecl, ok := n.(*ast.FuncDecl)
|
|
252
|
-
if !ok || funcDecl.Body == nil || isTestFunction(funcDecl.Name.Name) {
|
|
253
|
-
return true
|
|
254
|
-
}
|
|
255
|
-
hasGo, hasSync := a.analyzeConcurrency(funcDecl)
|
|
256
|
-
if !hasGo || hasSync {
|
|
257
|
-
return true
|
|
258
|
-
}
|
|
259
|
-
pos := a.parser.fileSet.Position(funcDecl.Pos())
|
|
260
|
-
violations = append(violations, Violation{
|
|
261
|
-
File: filePath,
|
|
262
|
-
Line: pos.Line,
|
|
263
|
-
Severity: "severe",
|
|
264
|
-
Message: "Function launches a goroutine without synchronization",
|
|
265
|
-
Details: map[string]interface{}{
|
|
266
|
-
"function": funcDecl.Name.Name,
|
|
267
|
-
},
|
|
268
|
-
Suggestion: "Use sync.WaitGroup or a channel to synchronize the goroutine",
|
|
269
|
-
Analyzer: "go",
|
|
270
|
-
Rule: "concurrency",
|
|
271
|
-
})
|
|
272
|
-
return true
|
|
273
|
-
})
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
return violations
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// runChannelAnalysis analyzes channels for the provable same-goroutine deadlock.
|
|
280
|
-
//
|
|
281
|
-
// The old predicate was `containsChannel(function.Signature) && function.Complexity
|
|
282
|
-
// > 3` — a signature substring ("chan") plus a cyclomatic-complexity count standing
|
|
283
|
-
// in for "potential deadlock". Neither is a deadlock: a channel in the signature is
|
|
284
|
-
// not a deadlock, and a 3-statement function can deadlock. The honest, provable
|
|
285
|
-
// signal is a *same-goroutine* deadlock: a channel created unbuffered
|
|
286
|
-
// (`make(chan T)`, no buffer) that is both sent to and received from (or operated
|
|
287
|
-
// on twice) in the same function with no `go` statement in the body. An unbuffered
|
|
288
|
-
// send blocks until a receiver is ready; if the counterpart lives in the same
|
|
289
|
-
// goroutine, the first operation blocks before the second can run — guaranteed,
|
|
290
|
-
// independent of external code. Cross-goroutine deadlock detection is out of scope:
|
|
291
|
-
// it needs inter-procedural escape/dataflow analysis the syntax-only subprocess
|
|
292
|
-
// lacks.
|
|
293
|
-
func (a *Analyzer) runChannelAnalysis() []Violation {
|
|
294
|
-
var violations []Violation
|
|
295
|
-
|
|
296
|
-
for filePath, file := range a.parser.files {
|
|
297
|
-
ast.Inspect(file, func(n ast.Node) bool {
|
|
298
|
-
funcDecl, ok := n.(*ast.FuncDecl)
|
|
299
|
-
if !ok || funcDecl.Body == nil || isTestFunction(funcDecl.Name.Name) {
|
|
300
|
-
return true
|
|
301
|
-
}
|
|
302
|
-
channel := a.deadlockChannel(funcDecl)
|
|
303
|
-
if channel == "" {
|
|
304
|
-
return true
|
|
305
|
-
}
|
|
306
|
-
pos := a.parser.fileSet.Position(funcDecl.Pos())
|
|
307
|
-
violations = append(violations, Violation{
|
|
308
|
-
File: filePath,
|
|
309
|
-
Line: pos.Line,
|
|
310
|
-
Severity: "critical",
|
|
311
|
-
Message: "Guaranteed deadlock: unbuffered channel is both sent to and received from in the same goroutine",
|
|
312
|
-
Details: map[string]interface{}{
|
|
313
|
-
"function": funcDecl.Name.Name,
|
|
314
|
-
"channel": channel,
|
|
315
|
-
},
|
|
316
|
-
Suggestion: "Buffer the channel, or run one side (send or receive) in its own goroutine",
|
|
317
|
-
Analyzer: "go",
|
|
318
|
-
Rule: "channel-deadlock",
|
|
319
|
-
})
|
|
320
|
-
return true
|
|
321
|
-
})
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return violations
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// filterViolationsBySeverity filters violations based on minimum severity
|
|
328
|
-
func (a *Analyzer) filterViolationsBySeverity(violations []Violation) []Violation {
|
|
329
|
-
if a.options.MinSeverity == "" {
|
|
330
|
-
return violations
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
severityOrder := map[string]int{
|
|
334
|
-
"high": 1,
|
|
335
|
-
"severe": 2,
|
|
336
|
-
"critical": 3,
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
minLevel := severityOrder[a.options.MinSeverity]
|
|
340
|
-
if minLevel == 0 {
|
|
341
|
-
return violations
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// A non-nil empty slice so "no violations" serializes to [] not null — a
|
|
345
|
-
// nil slice marshals to JSON null, which the TypeScript caller reads as a
|
|
346
|
-
// crash rather than an empty result (the same silent-empty class this whole
|
|
347
|
-
// subprocess boundary is built to remove).
|
|
348
|
-
filtered := make([]Violation, 0, len(violations))
|
|
349
|
-
for _, violation := range violations {
|
|
350
|
-
if severityOrder[violation.Severity] >= minLevel {
|
|
351
|
-
filtered = append(filtered, violation)
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
return filtered
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Helper functions for analysis
|
|
359
|
-
|
|
360
|
-
// syncMethodNames are the selector names on sync primitives that signal a
|
|
361
|
-
// function is synchronizing its goroutines (sync.WaitGroup and sync.Mutex /
|
|
362
|
-
// sync.RWMutex method sets).
|
|
363
|
-
var syncMethodNames = map[string]bool{
|
|
364
|
-
"Add": true,
|
|
365
|
-
"Done": true,
|
|
366
|
-
"Wait": true,
|
|
367
|
-
"Lock": true,
|
|
368
|
-
"Unlock": true,
|
|
369
|
-
"RLock": true,
|
|
370
|
-
"RUnlock": true,
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// functionDropsError reports whether a function body assigns an error from a
|
|
374
|
-
// call result and never checks, returns, passes, or explicitly ignores that
|
|
375
|
-
// same error on the same path. It collects the source positions of every `err`
|
|
376
|
-
// binding-from-call and every "checking" use, then flags an assignment whose
|
|
377
|
-
// next use of `err` is a reassignment (or the end of the function) rather than
|
|
378
|
-
// a check. Position ordering (not identifier pointer identity) is what links a
|
|
379
|
-
// use to its binding, since each `err` occurrence is a distinct AST node.
|
|
380
|
-
func (a *Analyzer) functionDropsError(funcDecl *ast.FuncDecl) bool {
|
|
381
|
-
var assignPositions []token.Pos
|
|
382
|
-
var checkPositions []token.Pos
|
|
383
|
-
|
|
384
|
-
// A function with a named `err` result propagates that error on a bare
|
|
385
|
-
// `return` — Go returns the current value of the named result. Treating
|
|
386
|
-
// bare returns as a check is what keeps `func (w *W) Write(...) (n int,
|
|
387
|
-
// err error) { n, err = io.Write(...); return }` from being flagged as a
|
|
388
|
-
// dropped error (the dominant named-return idiom).
|
|
389
|
-
hasNamedErr := funcDeclHasNamedErr(funcDecl)
|
|
390
|
-
|
|
391
|
-
ast.Inspect(funcDecl.Body, func(n ast.Node) bool {
|
|
392
|
-
switch node := n.(type) {
|
|
393
|
-
case *ast.AssignStmt:
|
|
394
|
-
if assignHasCallRHS(node) {
|
|
395
|
-
for _, lhs := range node.Lhs {
|
|
396
|
-
if ident, ok := lhs.(*ast.Ident); ok && ident.Name == "err" {
|
|
397
|
-
assignPositions = append(assignPositions, ident.Pos())
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
// Explicit ignore: `_ = err`
|
|
402
|
-
if len(node.Lhs) == 1 && len(node.Rhs) == 1 && isBlankIdent(node.Lhs[0]) {
|
|
403
|
-
if ident, ok := node.Rhs[0].(*ast.Ident); ok && ident.Name == "err" {
|
|
404
|
-
checkPositions = append(checkPositions, ident.Pos())
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
case *ast.BinaryExpr:
|
|
408
|
-
// `err != nil` / `err == nil`
|
|
409
|
-
if node.Op == token.NEQ || node.Op == token.EQL {
|
|
410
|
-
for _, side := range []ast.Expr{node.X, node.Y} {
|
|
411
|
-
if ident, ok := side.(*ast.Ident); ok && ident.Name == "err" {
|
|
412
|
-
checkPositions = append(checkPositions, ident.Pos())
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
case *ast.ReturnStmt:
|
|
417
|
-
if len(node.Results) == 0 && hasNamedErr {
|
|
418
|
-
checkPositions = append(checkPositions, node.Pos())
|
|
419
|
-
}
|
|
420
|
-
for _, res := range node.Results {
|
|
421
|
-
if ident, ok := res.(*ast.Ident); ok && ident.Name == "err" {
|
|
422
|
-
checkPositions = append(checkPositions, ident.Pos())
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
case *ast.CallExpr:
|
|
426
|
-
for _, arg := range node.Args {
|
|
427
|
-
if ident, ok := arg.(*ast.Ident); ok && ident.Name == "err" {
|
|
428
|
-
checkPositions = append(checkPositions, ident.Pos())
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
return true
|
|
433
|
-
})
|
|
434
|
-
|
|
435
|
-
sort.Slice(assignPositions, func(i, j int) bool { return assignPositions[i] < assignPositions[j] })
|
|
436
|
-
sort.Slice(checkPositions, func(i, j int) bool { return checkPositions[i] < checkPositions[j] })
|
|
437
|
-
|
|
438
|
-
// An assigned `err` is handled iff a check falls strictly after it and
|
|
439
|
-
// before the next assignment (or the end of the function).
|
|
440
|
-
for i, assign := range assignPositions {
|
|
441
|
-
var upper token.Pos
|
|
442
|
-
if i+1 < len(assignPositions) {
|
|
443
|
-
upper = assignPositions[i+1]
|
|
444
|
-
} else {
|
|
445
|
-
upper = token.Pos(1<<62 - 1) // function end (effectively +inf)
|
|
446
|
-
}
|
|
447
|
-
handled := false
|
|
448
|
-
for _, check := range checkPositions {
|
|
449
|
-
if check > assign && check < upper {
|
|
450
|
-
handled = true
|
|
451
|
-
break
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
if !handled {
|
|
455
|
-
return true
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
return false
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// funcDeclHasNamedErr reports whether the function's result list names an
|
|
462
|
-
// error result `err` (the `(err error)` named-return shape).
|
|
463
|
-
func funcDeclHasNamedErr(funcDecl *ast.FuncDecl) bool {
|
|
464
|
-
if funcDecl.Type == nil || funcDecl.Type.Results == nil {
|
|
465
|
-
return false
|
|
466
|
-
}
|
|
467
|
-
for _, field := range funcDecl.Type.Results.List {
|
|
468
|
-
for _, name := range field.Names {
|
|
469
|
-
if name.Name == "err" {
|
|
470
|
-
return true
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
return false
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// assignHasCallRHS reports whether any right-hand side of an assignment is a
|
|
478
|
-
// call expression (the `x, err := f()` shape that produces an error).
|
|
479
|
-
func assignHasCallRHS(stmt *ast.AssignStmt) bool {
|
|
480
|
-
for _, rhs := range stmt.Rhs {
|
|
481
|
-
if _, ok := rhs.(*ast.CallExpr); ok {
|
|
482
|
-
return true
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
return false
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// isBlankIdent reports whether an expression is the blank identifier `_`.
|
|
489
|
-
func isBlankIdent(expr ast.Expr) bool {
|
|
490
|
-
ident, ok := expr.(*ast.Ident)
|
|
491
|
-
return ok && ident.Name == "_"
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// analyzeConcurrency reports whether a function body launches a goroutine
|
|
495
|
-
// (hasGo) and whether it contains any synchronization signal (hasSync): a
|
|
496
|
-
// sync.Add/Done/Wait/Lock/Unlock/RLock/RUnlock call or a channel send/receive.
|
|
497
|
-
func (a *Analyzer) analyzeConcurrency(funcDecl *ast.FuncDecl) (hasGo, hasSync bool) {
|
|
498
|
-
ast.Inspect(funcDecl.Body, func(n ast.Node) bool {
|
|
499
|
-
switch node := n.(type) {
|
|
500
|
-
case *ast.GoStmt:
|
|
501
|
-
hasGo = true
|
|
502
|
-
case *ast.SendStmt:
|
|
503
|
-
hasSync = true
|
|
504
|
-
case *ast.UnaryExpr:
|
|
505
|
-
if node.Op == token.ARROW {
|
|
506
|
-
hasSync = true
|
|
507
|
-
}
|
|
508
|
-
case *ast.SelectorExpr:
|
|
509
|
-
if syncMethodNames[node.Sel.Name] {
|
|
510
|
-
hasSync = true
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
return true
|
|
514
|
-
})
|
|
515
|
-
return hasGo, hasSync
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
// deadlockChannel reports the name of an unbuffered channel that is operated on
|
|
519
|
-
// twice (sent to or received from) at the top level of a function body with no
|
|
520
|
-
// `go` statement — a guaranteed same-goroutine deadlock. Returns "" when no such
|
|
521
|
-
// channel exists. A buffered channel (`make(chan T, N)`) never qualifies because
|
|
522
|
-
// its send does not block; and any `go` statement makes the deadlock unprovable
|
|
523
|
-
// (the counterpart could live in the spawned goroutine), so the function is
|
|
524
|
-
// cleared.
|
|
525
|
-
func (a *Analyzer) deadlockChannel(funcDecl *ast.FuncDecl) string {
|
|
526
|
-
unbuffered := make(map[string]bool)
|
|
527
|
-
ops := make(map[string]int)
|
|
528
|
-
hasGo := false
|
|
529
|
-
|
|
530
|
-
for _, stmt := range funcDecl.Body.List {
|
|
531
|
-
ast.Inspect(stmt, func(n ast.Node) bool {
|
|
532
|
-
switch node := n.(type) {
|
|
533
|
-
case *ast.GoStmt:
|
|
534
|
-
hasGo = true
|
|
535
|
-
case *ast.AssignStmt:
|
|
536
|
-
// `ch := make(chan T)` / `ch = make(chan T)` — unbuffered creation.
|
|
537
|
-
for i, rhs := range node.Rhs {
|
|
538
|
-
if isUnbufferedMakeChan(rhs) && i < len(node.Lhs) {
|
|
539
|
-
if ident, ok := node.Lhs[i].(*ast.Ident); ok {
|
|
540
|
-
unbuffered[ident.Name] = true
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
case *ast.SendStmt:
|
|
545
|
-
if ident, ok := node.Chan.(*ast.Ident); ok {
|
|
546
|
-
ops[ident.Name]++
|
|
547
|
-
}
|
|
548
|
-
case *ast.UnaryExpr:
|
|
549
|
-
if node.Op == token.ARROW {
|
|
550
|
-
if ident, ok := node.X.(*ast.Ident); ok {
|
|
551
|
-
ops[ident.Name]++
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
return true
|
|
556
|
-
})
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
if hasGo {
|
|
560
|
-
return ""
|
|
561
|
-
}
|
|
562
|
-
for name := range unbuffered {
|
|
563
|
-
if ops[name] >= 2 {
|
|
564
|
-
return name
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
return ""
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
// isUnbufferedMakeChan reports whether expr is `make(chan T)` — a single-argument
|
|
571
|
-
// make of a channel type, i.e. an unbuffered channel. `make(chan T, N)` has two
|
|
572
|
-
// arguments (a buffer), so its send does not block.
|
|
573
|
-
func isUnbufferedMakeChan(expr ast.Expr) bool {
|
|
574
|
-
call, ok := expr.(*ast.CallExpr)
|
|
575
|
-
if !ok || len(call.Args) != 1 {
|
|
576
|
-
return false
|
|
577
|
-
}
|
|
578
|
-
ident, ok := call.Fun.(*ast.Ident)
|
|
579
|
-
if !ok || ident.Name != "make" {
|
|
580
|
-
return false
|
|
581
|
-
}
|
|
582
|
-
_, ok = call.Args[0].(*ast.ChanType)
|
|
583
|
-
return ok
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// importGroup classifies an unquoted import path into a grouping tier:
|
|
587
|
-
// 0 = standard library (first path segment has no '.'), 1 = third-party (first
|
|
588
|
-
// segment has a '.'), 2 = local (relative './' or '../'). This is the
|
|
589
|
-
// stdlib-before-third-party-before-local convention goimports/gofmt enforce.
|
|
590
|
-
func importGroup(path string) int {
|
|
591
|
-
if strings.HasPrefix(path, ".") {
|
|
592
|
-
return 2
|
|
593
|
-
}
|
|
594
|
-
first := path
|
|
595
|
-
if idx := strings.IndexByte(path, '/'); idx >= 0 {
|
|
596
|
-
first = path[:idx]
|
|
597
|
-
}
|
|
598
|
-
if strings.Contains(first, ".") {
|
|
599
|
-
return 1
|
|
600
|
-
}
|
|
601
|
-
return 0
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
// firstImportGroupViolation reports the position of the first import that breaks
|
|
605
|
-
// the stdlib-before-third-party-before-local grouping, and how many imports in
|
|
606
|
-
// total are out of group order. It returns (token.NoPos, 0) when the import
|
|
607
|
-
// block is grouped (the group sequence is non-decreasing).
|
|
608
|
-
func firstImportGroupViolation(imports []*ast.ImportSpec) (token.Pos, int) {
|
|
609
|
-
maxGroup := -1
|
|
610
|
-
grouped := 0
|
|
611
|
-
firstPos := token.NoPos
|
|
612
|
-
for _, spec := range imports {
|
|
613
|
-
path, err := strconv.Unquote(spec.Path.Value)
|
|
614
|
-
if err != nil {
|
|
615
|
-
path = spec.Path.Value
|
|
616
|
-
}
|
|
617
|
-
g := importGroup(path)
|
|
618
|
-
switch {
|
|
619
|
-
case g > maxGroup:
|
|
620
|
-
maxGroup = g
|
|
621
|
-
case g < maxGroup:
|
|
622
|
-
grouped++
|
|
623
|
-
if firstPos == token.NoPos {
|
|
624
|
-
firstPos = spec.Pos()
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
return firstPos, grouped
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
// excludeTestFiles removes *_test.go files from a file list. Test files are
|
|
632
|
-
// compiled only by `go test`, never as production API — they are exempt from
|
|
633
|
-
// analysis and indexing the same way the TypeScript pipeline excludes test
|
|
634
|
-
// files at discovery (see testconventions.go).
|
|
635
|
-
func excludeTestFiles(files []string) []string {
|
|
636
|
-
out := make([]string, 0, len(files))
|
|
637
|
-
for _, f := range files {
|
|
638
|
-
if !isTestFile(f) {
|
|
639
|
-
out = append(out, f)
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
return out
|
|
643
|
-
}
|