fix(regexpcompileinfunction): resolve package identity via type checker instead of identifier name#39773
Conversation
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…einfunction linter Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
✅ Test Quality Sentinel completed test quality analysis. No test files were added or modified in this PR. The PR modifies pkg/linters/regexpcompileinfunction/regexpcompileinfunction.go (production code) and adds two testdata fixture files (alias_import.go, shadowed_identifier.go) under testdata/src/. These fixture files are Go source inputs analyzed by the existing regexpcompileinfunction_test.go, not test implementations themselves. Test Quality Sentinel skipped. |
|
✅ Design Decision Gate 🏗️ completed the design decision gate check. No ADR enforcement needed: PR #39773 does not have the 'implementation' label (has_implementation_label=false) and has only 49 new lines of code in business logic directories (≤100 threshold). Neither Condition A nor Condition B is met. |
|
✅ PR Code Quality Reviewer completed the code quality review. |
There was a problem hiding this comment.
Pull request overview
Updates the regexpcompileinfunction Go analyzer to identify regexp.Compile / regexp.MustCompile calls based on type-checked package identity (fixing alias-import false negatives and shadowing false positives), and adds test fixtures to validate the behavior.
Changes:
- Resolve the selector qualifier via
pass.TypesInfo(*types.PkgName) instead of comparing identifier names to"regexp". - Add testdata fixtures for
import re "regexp"and for a shadowedregexpidentifier. - Regenerate/update several workflow
.lock.ymlschemas (not described in the PR text).
Show a summary per file
| File | Description |
|---|---|
| pkg/linters/regexpcompileinfunction/regexpcompileinfunction.go | Uses type checker to confirm the selector targets the stdlib regexp package. |
| pkg/linters/regexpcompileinfunction/testdata/src/regexpcompileinfunction/alias_import.go | Adds alias-import fixture to ensure re.MustCompile / re.Compile are flagged inside functions. |
| pkg/linters/regexpcompileinfunction/testdata/src/regexpcompileinfunction/shadowed_identifier.go | Adds shadowing fixture to ensure a local regexp identifier is not treated as the stdlib package. |
| .github/workflows/test-quality-sentinel.lock.yml | Updates pull_request_number schema validation key. |
| .github/workflows/smoke-copilot.lock.yml | Updates pull_request_number schema validation key. |
| .github/workflows/smoke-copilot-arm.lock.yml | Updates pull_request_number schema validation key. |
| .github/workflows/smoke-copilot-aoai-entra.lock.yml | Updates pull_request_number schema validation key. |
| .github/workflows/smoke-copilot-aoai-apikey.lock.yml | Updates pull_request_number schema validation key. |
| .github/workflows/smoke-claude.lock.yml | Updates pull_request_number schema validation key. |
| .github/workflows/security-review.lock.yml | Updates pull_request_number schema validation key. |
| .github/workflows/refiner.lock.yml | Updates pull_request_number schema validation key. |
| .github/workflows/pr-triage-agent.lock.yml | Updates pull_request_number schema validation key. |
| .github/workflows/pr-nitpick-reviewer.lock.yml | Updates pull_request_number schema validation key. |
| .github/workflows/pr-code-quality-reviewer.lock.yml | Updates pull_request_number schema validation key. |
| .github/workflows/mattpocock-skills-reviewer.lock.yml | Updates pull_request_number schema validation key. |
| .github/workflows/grumpy-reviewer.lock.yml | Updates pull_request_number schema validation key. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 16/16 changed files
- Comments generated: 2
| func GoodShadowedRegexpIdentifier(s string) bool { | ||
| regexp := customRegexp{} | ||
| _ = regexp.MustCompile(s) | ||
| return true | ||
| } |
There was a problem hiding this comment.
Fixed in commit fix: address review feedback.... The fixture now uses a compile-time constant string pattern (^[a-z]+$) and the unused parameter s has been removed, so it properly exercises the false-positive case against the old name-based check.
| ] | ||
| }, | ||
| "pull_request_number": { | ||
| "optionalPositiveInteger": true | ||
| "issueOrPRNumber": true | ||
| }, |
There was a problem hiding this comment.
The lock.yml changes were auto-generated by make recompile during the initial plan commit and are unrelated to this linter fix. Reverted all 13 lock.yml files to their base state in the latest commit.
There was a problem hiding this comment.
🔎 Code quality review by PR Code Quality Reviewer
| } | ||
| return ident.Name == "regexp" && (sel.Sel.Name == "MustCompile" || sel.Sel.Name == "Compile") | ||
| pkgName, ok := obj.(*types.PkgName) | ||
| return ok && pkgName.Imported().Path() == "regexp" |
There was a problem hiding this comment.
Potential nil dereference on pkgName.Imported(): when the type checker's error-recovery path creates a PkgName for an unresolvable import, Imported() returns nil, and the .Path() call will panic the linter instead of gracefully returning false.
💡 Suggested fix
pkgName, ok := obj.(*types.PkgName)
if !ok || pkgName.Imported() == nil {
return false
}
return pkgName.Imported().Path() == "regexp"The go/types resolver (see resolver.go) calls types.NewPkgName(..., nil) when it cannot resolve an import during error recovery. Any linter run against a package with a broken or missing import dependency will hit this nil and crash. The existing pattern of guarding obj == nil makes the missing guard here inconsistent and easy to overlook.
There was a problem hiding this comment.
Fixed in the latest commit. Added the nil guard as suggested:
pkgName, ok := obj.(*types.PkgName)
if !ok || pkgName.Imported() == nil {
return false
}
return pkgName.Imported().Path() == "regexp"| @@ -0,0 +1,20 @@ | |||
| package regexpcompileinfunction | |||
|
|
|||
| import re "regexp" | |||
There was a problem hiding this comment.
Undocumented false negative: import . "regexp" is silently skipped. With a dot import, call sites are bare identifiers (MustCompile(...)) rather than selector expressions, so isRegexpCompileCall exits early at the *ast.SelectorExpr guard and produces no diagnostic.
💡 Details
This is a pre-existing limitation, not introduced by this PR, but the PR adds test fixtures specifically targeting import-aliasing edge cases without mentioning this gap. Either add a comment in the function doc noting the limitation, or add a dot_import.go fixture that documents the known miss:
package regexpcompileinfunction
import . "regexp"
// NOT flagged (known limitation: dot imports are not selector expressions).
func DotImportExample(s string) bool {
r := MustCompile(`^[a-z]+$`) // no // want — dot-import calls are silently skipped
return r.MatchString(s)
}Without this, a future refactor that adds dot-import detection would have no regression test to verify the change.
There was a problem hiding this comment.
Added dot_import.go in the latest commit to document this known limitation. The fixture confirms dot-import calls are silently skipped (no // want annotation) and includes a comment explaining why: dot imports produce bare identifier calls, not selector expressions, so isRegexpCompileCall exits early at the *ast.SelectorExpr guard.
|
@copilot run pr-finisher skill |
- Guard pkgName.Imported() against nil before calling .Path() - Fix shadowed_identifier.go fixture to use constant string pattern - Add dot_import.go fixture documenting dot-import known limitation - Revert unrelated lock.yml changes (optionalPositiveInteger -> issueOrPRNumber) Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
All review threads addressed in the latest commit. Summary of changes made:
|
isRegexpCompileCallmatchedregexp.MustCompile/regexp.Compileby comparing the AST identifier's.Nameto the string"regexp"— no type-checker involvement. This produces false negatives for aliased imports (import re "regexp") and false positives for any local identifier namedregexpwith aCompile/MustCompilemethod.Changes
regexpcompileinfunction.goisRegexpCompileCallnow accepts*analysis.Passand resolves the caller's package viapass.TypesInfo.ObjectOf(ident).(*types.PkgName), then gates onpkgName.Imported().Path() == "regexp"— same pattern as thesortslicelinterpkgName.Imported()to prevent panic when the type checker's error-recovery path creates aPkgNamewith a nil imported packageTest fixtures
alias_import.go—import re "regexp"with in-functionre.MustCompile/re.Compile; both annotated// wantto verify they are now flagged (FN fix)shadowed_identifier.go— local variable namedregexpof a custom type withMustCompilecalled with a constant string pattern; no// want, verifying it is not flagged (FP fix)dot_import.go— documents the known limitation thatimport . "regexp"calls are not flagged, since dot imports produce bare identifier calls rather than selector expressions