Skip to content

fix(writebytestring): emit string() cast for named string types; add RunWithSuggestedFixes#44208

Merged
pelikhan merged 3 commits into
mainfrom
copilot/fix-writebytestring-fix
Jul 8, 2026
Merged

fix(writebytestring): emit string() cast for named string types; add RunWithSuggestedFixes#44208
pelikhan merged 3 commits into
mainfrom
copilot/fix-writebytestring-fix

Conversation

Copilot AI commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

writebytestring accepted named string types (e.g. type MyStr string) via Underlying() but emitted io.WriteString(w, s) verbatim — non-compiling, since io.WriteString requires a predeclared string. The bug was invisible because the test used analysistest.Run (diagnostic-only) instead of RunWithSuggestedFixes, which would have compiled the fix output.

Changes

  • writebytestring.go — add isExactString(types.Type) bool using a direct *types.Basic assertion (not .Underlying()) to distinguish the predeclared string from named string types. Compute sExpr in run: pass through for exact string, wrap with string(...) for named types. Both the diagnostic message and buildFix use sExpr.

  • writebytestring_test.go — switch to analysistest.RunWithSuggestedFixes, matching all sibling linters (appendbytestring, stringsindexcontains, sprintfint).

  • testdata writebytestring.go — update badNamedString // want regex to expect io.WriteString(&buf, string(s)).

  • writebytestring.go.golden (new) — golden file compiled by the harness; badNamedString reflects the string(s) wrap, all other cases unchanged.

Before / After

type myString string
s := myString("hello")
buf.Write([]byte(s))
// Before fix → io.WriteString(&buf, s)         // compile error: cannot use myString as string
// After fix  → io.WriteString(&buf, string(s)) // compiles correctly

Copilot AI and others added 2 commits July 8, 2026 05:56
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
… add RunWithSuggestedFixes test

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix auto-fix for writebytestring linter on named string types fix(writebytestring): emit string() cast for named string types; add RunWithSuggestedFixes Jul 8, 2026
Copilot AI requested a review from pelikhan July 8, 2026 06:06
@pelikhan pelikhan marked this pull request as ready for review July 8, 2026 06:10
Copilot AI review requested due to automatic review settings July 8, 2026 06:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a correctness bug in the writebytestring analyzer where the suggested io.WriteString fix could fail to compile when the original argument was a named string type (e.g. type MyStr string), and strengthens the test harness to compile-check suggested fixes.

Changes:

  • Update writebytestring to emit string(<expr>) casts when the argument is a named string type so io.WriteString fixes compile.
  • Switch the analyzer test to analysistest.RunWithSuggestedFixes and add/update golden + want expectations to validate compiled fix output.
  • Includes regenerated workflow .lock.yml updates (currently not described in the PR description).
Show a summary per file
File Description
pkg/linters/writebytestring/writebytestring.go Adjusts fix/message generation to wrap named string types with string(...); adds isExactString.
pkg/linters/writebytestring/writebytestring_test.go Ensures suggested fixes are applied/compiled via RunWithSuggestedFixes.
pkg/linters/writebytestring/testdata/src/writebytestring/writebytestring.go Updates // want expectation for named string case.
pkg/linters/writebytestring/testdata/src/writebytestring/writebytestring.go.golden Adds golden output reflecting the compiled suggested fix (including string(s) for named string).
.github/workflows/lint-monster.lock.yml Regenerated lock workflow content (Safe Outputs config heredoc/config JSON changes).
.github/workflows/eslint-monster.lock.yml Regenerated lock workflow content (Safe Outputs config heredoc/config JSON changes).

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 6/6 changed files
  • Comments generated: 3
  • Review effort level: Low

Comment on lines +169 to +176
// isExactString reports whether t is the predeclared string type, not a named
// type whose underlying type is string. io.WriteString(w Writer, s string)
// requires a predeclared string; named string types need an explicit string(...)
// conversion to satisfy the parameter type.
func isExactString(t types.Type) bool {
b, ok := t.(*types.Basic)
return ok && b.Kind() == types.String
}
Comment on lines 536 to +541
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_0caeba2ce3ff905f_EOF'
{"assign_to_agent":{"allowed":["copilot"],"max":3,"target":"*"},"close_issue":{"max":10,"required_title_prefix":"[lint-monster] ","state_reason":"duplicate"},"create_discussion":{"category":"audits","close_older_discussions":true,"expires":48,"fallback_to_issue":true,"max":1,"title_prefix":"[lint-monster] "},"create_issue":{"expires":168,"labels":["automation","lint","cookie"],"max":3,"title_prefix":"[lint-monster] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"update_issue":{"allow_body":true,"max":10,"required_title_prefix":"[lint-monster] ","title_prefix":"[lint-monster] "}}
GH_AW_SAFE_OUTPUTS_CONFIG_0caeba2ce3ff905f_EOF
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_77a7106ac1879fb2_EOF'
{"assign_to_agent":{"allowed":["copilot"],"max":3,"target":"*"},"close_issue":{"max":10,"required_title_prefix":"[lint-monster] ","state_reason":"duplicate"},"create_discussion":{"category":"audits","close_older_discussions":true,"expires":48,"fallback_to_issue":true,"max":1,"title_prefix":"[lint-monster] "},"create_issue":{"expires":168,"labels":["automation","lint","cookie"],"max":3,"title_prefix":"[lint-monster] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"update_issue":{"allow_body":true,"max":10,"title_prefix":"[lint-monster] "}}
GH_AW_SAFE_OUTPUTS_CONFIG_77a7106ac1879fb2_EOF
Comment on lines 539 to +546
- name: Generate Safe Outputs Config
run: |
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_eaee126711029267_EOF'
{"assign_to_agent":{"allowed":["copilot"],"max":3,"target":"*"},"close_issue":{"max":10,"required_title_prefix":"[eslint-monster] ","state_reason":"duplicate"},"create_discussion":{"category":"audits","close_older_discussions":true,"expires":48,"fallback_to_issue":true,"max":1,"title_prefix":"[eslint-monster] "},"create_issue":{"expires":168,"labels":["automation","eslint","cookie"],"max":3,"title_prefix":"[eslint-monster] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"update_issue":{"allow_body":true,"max":10,"required_title_prefix":"[eslint-monster] ","title_prefix":"[eslint-monster] "}}
GH_AW_SAFE_OUTPUTS_CONFIG_eaee126711029267_EOF
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_a70707033a3aa7b2_EOF'
{"assign_to_agent":{"allowed":["copilot"],"max":3,"target":"*"},"close_issue":{"max":10,"required_title_prefix":"[eslint-monster] ","state_reason":"duplicate"},"create_discussion":{"category":"audits","close_older_discussions":true,"expires":48,"fallback_to_issue":true,"max":1,"title_prefix":"[eslint-monster] "},"create_issue":{"expires":168,"labels":["automation","eslint","cookie"],"max":3,"title_prefix":"[eslint-monster] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"update_issue":{"allow_body":true,"max":10,"title_prefix":"[eslint-monster] "}}
GH_AW_SAFE_OUTPUTS_CONFIG_a70707033a3aa7b2_EOF
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR Code Quality Reviewer failed during code quality review.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (95 additions across 6 files, all in .github/skills/).

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 85/100 — Excellent

Analyzed 1 test(s): 1 design, 0 implementation, 0 violation(s).

📊 Metrics (1 test)
Metric Value
Analyzed 1 (Go: 1, JS: 0)
✅ Design 1 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 1 (100%)
Duplicate clusters 0
Inflation No
🚨 Violations 0
Test File Classification Issues
TestAnalyzer writebytestring_test.go:12 design_test None

Verdict

Passed. 0% implementation tests (threshold: 30%). The test was upgraded from analysistest.Run to analysistest.RunWithSuggestedFixes, adding golden-file validation of the emitted io.WriteString rewrites. The testdata covers five behavioral scenarios: pointer-receiver buffers (&buf), os.File, custom io.Writer implementations, named string types requiring string(s) cast, and interface-typed writers — plus nolint suppression and true-negative cases.

References: §28921605289

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 26.4 AIC · ⌖ 9.45 AIC · ⊞ 6.8K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Test Quality Sentinel: 85/100. 0% implementation tests (threshold: 30%). The upgrade to RunWithSuggestedFixes with a .golden file meaningfully expands behavioral coverage of the emitted rewrite fixes.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: writebytestring — named string type cast fix ✅

The fix is correct and complete.

isExactString uses a direct t.(*types.Basic) assertion (not .Underlying()) — the precise tool for the job. It correctly distinguishes the predeclared string from a named type like type myString string, while type aliases (type MyStr = string) pass through without an unnecessary cast.

isStringType continues to use .Underlying() as the entry gate, which is right — we want to flag both plain string and named-string types.

sExpr guard (st != nil && !isExactString(st)) is safe: since isStringType already succeeded, TypeOf should never return nil here, but the nil guard is a good defensive touch.

Golden file — the preserved // want trailing comments in the fixed output are expected; RunWithSuggestedFixes replaces only the call.Pos()→call.End() span, leaving trailing comments intact.

Lock file changes are auto-generated and unrelated to the linter fix.

No blocking issues found.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 59.9 AIC · ⌖ 6.25 AIC · ⊞ 4.8K

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /tdd and /diagnosing-bugs — approving with two minor observations.

📋 Key Themes & Highlights

Key Themes

  • Root cause well-addressed: The fix correctly distinguishes *types.Basic (predeclared string) from named string types by using a direct type assertion rather than .Underlying() — exactly the right approach.
  • Test harness upgrade: Switching to RunWithSuggestedFixes is the correct move and aligns with all sibling linters; it would have caught this bug immediately.
  • Two edge cases to consider: (1) type aliases (type S = string) are handled correctly by the *types.Basic assertion but are untested; (2) the golden file retains // want comments — verify this is correct for the RunWithSuggestedFixes harness.

Positive Highlights

  • isExactString is a clean, minimal helper with an accurate doc comment explaining the io.WriteString signature constraint
  • ✅ Both the diagnostic message and buildFix are updated consistently to use sExpr
  • ✅ The PR description is thorough and includes a clear before/after example
  • ✅ The golden file covers all existing test cases, preserving regression coverage

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 93.9 AIC · ⌖ 5.64 AIC · ⊞ 6.6K
Comment /matt to run again

// conversion to satisfy the parameter type.
func isExactString(t types.Type) bool {
b, ok := t.(*types.Basic)
return ok && b.Kind() == types.String

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] isExactString correctly handles type aliases (type MyStr = string) — aliases share the same *types.Basic node as the predeclared string so no string(...) wrap is emitted. This edge case is untested; adding a badAliasString testdata case would pin this behaviour as a regression guard.

💡 Suggested testdata addition
type stringAlias = string

func badAliasString() {
    var buf bytes.Buffer
    s := stringAlias("hello")
    buf.Write([]byte(s)) // want `...io\.WriteString\(&buf, s\)...`
                         // alias IS the predeclared string — no string() wrap
}

This documents the alias boundary and prevents a regression if isExactString is ever changed to use .Underlying().

@copilot please address this.

func bad() {
var buf bytes.Buffer
s := "hello"
io.WriteString(&buf, s) // want `buf\.Write\(\[\]byte\(s\)\) can be replaced with io\.WriteString\(&buf, s\) to potentially avoid a \[\]byte allocation if the writer implements io\.StringWriter`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnosing-bugs] The golden file retains // want regex comments in the fixed output. The golden file represents the rewritten source after fixes are applied — these // want annotations are left over from the original and will cause the test harness to re-run diagnostics against the already-fixed code, producing unexpected second-pass diagnostics.

Check how sibling linters handle this: appendbytestring.go.golden also retains // want comments, so this may be intentional harness behaviour. If RunWithSuggestedFixes applies fixes and re-runs the analyser on the result, these comments would need to match the new diagnostics (on io.WriteString(...) calls which the linter does not flag). Consider verifying this is intentional and add a brief code comment explaining the pattern.

@copilot please address this.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

🤖 PR Triage

Field Value
Category bug
Risk 🟢 Low
Score 55/100 (impact:22 urgency:18 quality:15)
Action batch_reviewpr-batch:linter-fixes

Rationale: Fixes silent linter false-fix: writebytestring emitted non-compiling code for named string types. Adds RunWithSuggestedFixes and golden file. Small, well-scoped. CI failing — needs investigation.


Run §28924016278

Generated by 🔧 PR Triage Agent · 101 AIC · ⌖ 9.82 AIC · ⊞ 5.4K ·

@pelikhan pelikhan merged commit 392092c into main Jul 8, 2026
95 of 96 checks passed
@pelikhan pelikhan deleted the copilot/fix-writebytestring-fix branch July 8, 2026 09:17
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.82.5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

3 participants