mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-11 03:38:07 +00:00
fix(group): treat value-less local declarations as shadowing the echo import
findBinding collapses "declared without a value" (`var echo Factory`) and "not declared" into null, so isLocalName missed that shadow. Split the walk into lookupBinding, which returns undefined only when the name is not declared before the enclosing function; findBinding keeps its null contract and isLocalName checks for any declaration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7d736f71ca
commit
b1aea8e956
2 changed files with 36 additions and 5 deletions
|
|
@ -199,10 +199,21 @@ function declaresName(node: Parser.SyntaxNode | null, name: string): boolean {
|
|||
* group), or is not bound in scope.
|
||||
*/
|
||||
function findBinding(ident: Parser.SyntaxNode): Parser.SyntaxNode | null {
|
||||
return lookupBinding(ident) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* findBinding's walk, keeping "declared without a traceable value" (null: a
|
||||
* `func` literal parameter, `var x T`, a select receive) apart from "not
|
||||
* declared before reaching the enclosing function declaration" (undefined).
|
||||
*/
|
||||
function lookupBinding(ident: Parser.SyntaxNode): Parser.SyntaxNode | null | undefined {
|
||||
const name = ident.text;
|
||||
let child: Parser.SyntaxNode = ident;
|
||||
for (let node = ident.parent; node; child = node, node = node.parent) {
|
||||
if (node.type === 'function_declaration' || node.type === 'method_declaration') return null;
|
||||
if (node.type === 'function_declaration' || node.type === 'method_declaration') {
|
||||
return undefined;
|
||||
}
|
||||
if (node.type === 'func_literal') {
|
||||
const params = node.childForFieldName('parameters')?.descendantsOfType('identifier') ?? [];
|
||||
if (params.some((p) => p.text === name)) return null;
|
||||
|
|
@ -278,7 +289,7 @@ function findBinding(ident: Parser.SyntaxNode): Parser.SyntaxNode | null {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -319,11 +330,12 @@ function receiverBindsToEchoConstructor(
|
|||
|
||||
/**
|
||||
* Whether `ident` names a local value rather than an imported package: a
|
||||
* binding in scope or a parameter/receiver of an enclosing function. Go lets
|
||||
* either shadow a package qualifier (`func f(echo *Factory) { echo.New() }`).
|
||||
* declaration in scope (with or without a value — `var echo Factory` shadows
|
||||
* too) or a parameter/receiver of an enclosing function. Go lets either
|
||||
* shadow a package qualifier (`func f(echo *Factory) { echo.New() }`).
|
||||
*/
|
||||
function isLocalName(ident: Parser.SyntaxNode): boolean {
|
||||
if (findBinding(ident) !== null) return true;
|
||||
if (lookupBinding(ident) !== undefined) return true;
|
||||
for (let node = ident.parent; node; node = node.parent) {
|
||||
if (
|
||||
node.type !== 'func_literal' &&
|
||||
|
|
|
|||
|
|
@ -596,6 +596,25 @@ func routes(echo *Factory) {
|
|||
).toEqual([{ method: 'GET', path: '/x', name: 'Middleware' }]);
|
||||
});
|
||||
|
||||
it('does not mistake a value-less local declaration of echo for the import', () => {
|
||||
// `var echo Factory` declares a local without an initializer; it still
|
||||
// shadows the package qualifier, so `echo.New()` proves nothing.
|
||||
expect(
|
||||
providers(`package main
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func routes() {
|
||||
var echo Factory
|
||||
e := echo.New()
|
||||
e.GET("/x", h.Handler, auth.Middleware)
|
||||
}
|
||||
`),
|
||||
).toEqual([{ method: 'GET', path: '/x', name: 'Middleware' }]);
|
||||
});
|
||||
|
||||
it('declines a route whose Group chain exceeds the depth cap', () => {
|
||||
// Past MAX_GROUP_DEPTH (32) the full prefix — and, in a mixed file, the
|
||||
// framework order — is unprovable: emitting the outer prefixes alone (or
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue