diff --git a/lib/rules/template-no-obsolete-elements.js b/lib/rules/template-no-obsolete-elements.js index af82f4e007..31f97a07e3 100644 --- a/lib/rules/template-no-obsolete-elements.js +++ b/lib/rules/template-no-obsolete-elements.js @@ -29,6 +29,16 @@ const OBSOLETE = [ 'tt', 'xmp', ]; + +function hasBindingInScopeChain(scope, name) { + for (let s = scope; s; s = s.upper) { + if (s.set && s.set.has(name)) { + return true; + } + } + return false; +} + /** @type {import('eslint').Rule.RuleModule} */ module.exports = { meta: { @@ -50,26 +60,21 @@ module.exports = { }, create(context) { const obsolete = new Set(OBSOLETE); - const blockParamsInScope = []; + const sourceCode = context.sourceCode; return { - GlimmerBlockStatement(node) { - const params = node.program?.blockParams || []; - blockParamsInScope.push(...params); - }, - 'GlimmerBlockStatement:exit'(node) { - const params = node.program?.blockParams || []; - for (let i = 0; i < params.length; i++) { - blockParamsInScope.pop(); - } - }, GlimmerElementNode(node) { - if (blockParamsInScope.includes(node.tag)) { + if (!obsolete.has(node.tag)) { return; } - if (obsolete.has(node.tag)) { - context.report({ node, messageId: 'obsolete', data: { element: node.tag } }); + // Use the parent's scope so that the element's own `as |x|` params + // (which attach a block scope to this node) don't shadow its own tag + // name. e.g. `` must still flag the outer tag. + const scope = sourceCode.getScope(node.parent); + if (hasBindingInScopeChain(scope, node.tag)) { + return; } + context.report({ node, messageId: 'obsolete', data: { element: node.tag } }); }, }; }, diff --git a/tests/lib/rules/template-no-obsolete-elements.js b/tests/lib/rules/template-no-obsolete-elements.js index 5c995da69b..16221c7ee6 100644 --- a/tests/lib/rules/template-no-obsolete-elements.js +++ b/tests/lib/rules/template-no-obsolete-elements.js @@ -11,6 +11,9 @@ ruleTester.run('template-no-obsolete-elements', rule, { `