diff --git a/src/helpers.ts b/src/helpers.ts index 93599231..74c2dcea 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -174,7 +174,9 @@ export function nodeToPlainText(node: SgNode) { function toText(one: SgNode) { const children = one.children() if (!children.length) { - out.push(one.text()) + if (!one.is('comment')) { + out.push(one.text()) + } } else { children.forEach((child) => toText(child)) } @@ -205,19 +207,37 @@ export function nodeToPlainText(node: SgNode) { * validatorArgs.forEach(arg => console.log('Validator argument:', arg.text())) */ export function inspectMethodArguments(node: SgNode, methodCalls: string[]): SgNode[] { - const matchingExpressions = node.findAll({ - rule: { - any: methodCalls.map((methodCall) => { - return { - pattern: { - context: `${methodCall}($$$ARGUMENTS)`, - selector: 'call_expression', - }, - } - }), - }, + const exactMethodCalls = new Set() + const receiverMethodCalls: string[] = [] + + methodCalls.forEach((methodCall) => { + const separator = methodCall.indexOf('.') + if (methodCall.startsWith('$') && separator !== -1) { + receiverMethodCalls.push(methodCall.slice(separator + 1)) + } else { + exactMethodCalls.add(methodCall) + } }) + const matchingExpressions = node + .findAll({ rule: { kind: 'call_expression' } }) + .filter((expression) => { + const functionNode = expression.field('function') + if (!functionNode) { + return false + } + const functionName = nodeToPlainText(functionNode) + + return ( + exactMethodCalls.has(functionName) || + receiverMethodCalls.some((methodCall) => { + return ( + functionName.endsWith(`.${methodCall}`) && !functionName.endsWith(`?.${methodCall}`) + ) + }) + ) + }) + return matchingExpressions.flatMap((matchingExpression) => { return matchingExpression.findAll({ rule: { kind: 'arguments' } }) }) diff --git a/tests/helpers/inspect_method_arguments.spec.ts b/tests/helpers/inspect_method_arguments.spec.ts index a30fcab4..efe9e897 100644 --- a/tests/helpers/inspect_method_arguments.spec.ts +++ b/tests/helpers/inspect_method_arguments.spec.ts @@ -76,6 +76,36 @@ test.group('Inspect method arguments', () => { }`, output: ['createCarValidator', 'createVehicleValidator'], }, + { + input: `class UsersController { + async store(ctx: HttpContext) { + await ctx.request.tryValidateUsing(admin.createUserValidator) + await this.ctx.request.validateUsing(createUserValidator) + } + }`, + output: ['admin.createUserValidator', 'createUserValidator'], + }, + { + input: `class UsersController { + async store() { + await request /* request comment */ . validateUsing( + factory(createUserValidator) + ) + await vine.tryValidate(user.createUserValidator) + } + }`, + output: ['factory', 'createUserValidator', 'user.createUserValidator'], + }, + { + input: `class UsersController { + async store(ctx: HttpContext) { + await request?.validateUsing(optionalValidator) + await ctx?.request.validateUsing(optionalContextValidator) + await validator.validate(request.all()) + } + }`, + output: [], + }, ]) .run(({ assert }, { input, output }) => { const root = parse(Lang.TypeScript, input).root() @@ -87,7 +117,11 @@ test.group('Inspect method arguments', () => { const validatorArguments = inspectMethodArguments(storeMethod, [ 'request.validateUsing', + 'request.tryValidateUsing', + '$CTX.request.validateUsing', + '$CTX.request.tryValidateUsing', 'vine.validate', + 'vine.tryValidate', ]).map((node) => { return nodeToPlainText( node.find({ rule: { any: [{ kind: 'identifier' }, { kind: 'member_expression' }] } })!