Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENHANCEMENT+CLEANUP] Better compile time errors for strict mode #1557

Merged
merged 1 commit into from
Mar 20, 2024

Conversation

chancancode
Copy link
Contributor

Removing support for implicit this fallback {{foo}} -> {{this.foo}}

When a strict mode template references an undefined variable, it really should produce a compile-time error, arguably that is the point of the feature.

However, it seems like previously a lot of these errors ended up propagating and is actually handled by the runtime compiler.

The original/primary goal here started out as cleaning up all the code for implicit this fallback, but as I delete code it eventually revaled all the places where strict mode was inappropiately using the same infrastructure ("unresolved free variables", which isn't really a sensible thing).

Also:

  • Removes {{#with}}
  • Removes "deprecated call" @foo={{bar}}
  • Removes unused opcodes
  • Syncs keyword list with Ember

@chancancode chancancode force-pushed the cleanup-this-fallback branch from 6500ab8 to da81f20 Compare February 1, 2024 14:09
`You attempted to render a path (\`{{${resolution.path}}}\`), but ${resolution.head} was not in scope`,
loc
);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO: consider deferring these to the validation pass

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Basically reconsider all the syntax errors this whole file

`* \`${arg.name}={{(${resolution.path})}}\` if this is meant to invoke the resolved helper, or\n` +
`* \`${arg.name}={{helper "${resolution.path}"}}\` if this is meant to pass the resolved helper by value`,
arg.loc
);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note: this is actually a loose mode only error

const Foo = defineComponent({}, '{{bar}}', {
definition: class extends GlimmerishComponent {
bar = 'Hello, world!';
this.assert.throws(
Copy link
Contributor

Choose a reason for hiding this comment

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

🙌

@@ -58,11 +58,11 @@ class ArrayTest extends RenderTest {
@test
'binds multiple values when variables are used'() {
this.render(
strip`{{#with (array this.personOne this.personTwo) as |people|}}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm glad I never used with 😅

meta: { moduleName: 'test-module' },
});
},
syntaxErrorFor(
Copy link
Contributor

Choose a reason for hiding this comment

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

this is a really nice error! folks will appreciate this!

@@ -691,14 +693,11 @@ function getSymbolForVar(
export function expressionContextOp(context: VariableResolutionContext): GetContextualFreeOpcode {
switch (context) {
case VariableResolutionContext.Strict:
debugger;
Copy link
Contributor

Choose a reason for hiding this comment

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

stray debugger

packages/@glimmer/compiler/lib/builder/builder.ts Outdated Show resolved Hide resolved
@@ -112,13 +110,6 @@ export class NormalizeExpressions {
}
}

DeprecaedCallExpression(
Copy link
Contributor

Choose a reason for hiding this comment

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

no need to fix a typo when we can just delete 🎉


InElement(inElement: mir.InElement): Result<null> {
return this.Expression(inElement.destination)
// Unfortunately we lost the `insertBefore=` part of the span
Copy link
Contributor

Choose a reason for hiding this comment

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

why did we lose it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is no AST/mir node (and hence span) for the “name=” part of named arguments; there is AST/mir node for the entire “name=value” thing, but the InElement mir node did not preserve it since it happen to know it will always have (or add one during normalization) the insertBefore key. So we just have the expression node and the entry node was thrown away in earlier normalization pass

@@ -101,6 +84,8 @@ export function resolveComponent(
let type = expr[0];

if (import.meta.env.DEV && expr[0] === SexpOpcodes.GetStrictKeyword) {
assert(!meta.isStrictMode, 'Strict mode errors should already be handled at compile time');
Copy link
Contributor

Choose a reason for hiding this comment

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

glimmer's assert matches node's assert, which is opposite ember's assert? 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah 🤷🏻‍♂️

trusting: false,
};
if (resolution.result === 'error' && resolution.path !== 'has-block') {
throw generateSyntaxError(
Copy link
Contributor

Choose a reason for hiding this comment

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

this is a great error!! thanks!

ComponentOrHelper = 'component or helper',
}

export default class StrictModeValidationPass {
Copy link
Contributor

Choose a reason for hiding this comment

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

does this have tests? I didn't seem the explicitly -- but maybe I missed them?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is what’s generating the syntax errors in the strict mode tests

chancancode added a commit that referenced this pull request Mar 9, 2024
This was dropped on Ember side a while back, just never cleaned up

Ironically this probably meant when Ember dropped the deprecation/
error, nothing was actually stopping `{{#with}}` from anymore, so
perhaps for a while we have actually _re-introduced_ support for
the syntax for some time?

Extracted from #1557
Removing support for implicit this fallback {{foo}} -> {{this.foo}}

When a strict mode template references an undefined variable, it
really should produce a compile-time error, arguably that is the
point of the feature.

However, it seems like previously a lot of these errors ended up
propagating and is actually handled by the runtime compiler.

The original/primary goal here started out as cleaning up all the
code for implicit this fallback, but as I delete code it eventually
revaled all the places where strict mode was inappropiately using
the same infrastructure ("unresolved free variables", which isn't
really a sensible thing).

Also:

* Removes "deprecated call" @foo={{bar}}
* Removes unused opcodes
@chancancode chancancode force-pushed the cleanup-this-fallback branch from da81f20 to 8685023 Compare March 9, 2024 03:39
@wycats wycats self-assigned this Mar 20, 2024
Copy link
Contributor

@wycats wycats left a comment

Choose a reason for hiding this comment

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

I think these changes are solid.

@chancancode chancancode merged commit 8f79790 into main Mar 20, 2024
5 of 6 checks passed
@chancancode chancancode deleted the cleanup-this-fallback branch March 20, 2024 17:42
chancancode added a commit that referenced this pull request Apr 4, 2024
Recently in #1557 we started emitting build time error for strict
mode templates containing undefined references. Previously these
were not handled until runtime and so we can only emit runtime
errors.

However, the previous setup allowed for the host environment to
implement additional keywords – these are resolved at runtime with
the `lookupBuildInHelper` and `lookupBuiltInModifier` hooks, and
the error is only emitted if these hooks returned `null`.

To allow for this possibility, `precompile` now accept an option
for additional strict mode keywords that the host environment is
expected to provide.
chancancode added a commit that referenced this pull request Apr 4, 2024
Recently in #1557 we started emitting build time error for strict
mode templates containing undefined references. Previously these
were not handled until runtime and so we can only emit runtime
errors.

However, the previous setup allowed for the host environment to
implement additional keywords – these are resolved at runtime with
the `lookupBuildInHelper` and `lookupBuiltInModifier` hooks, and
the error is only emitted if these hooks returned `null`.

To allow for this possibility, `precompile` now accept an option
for additional strict mode keywords that the host environment is
expected to provide.
chancancode added a commit that referenced this pull request Apr 4, 2024
Recently in #1557 we started emitting build time error for strict
mode templates containing undefined references. Previously these
were not handled until runtime and so we can only emit runtime
errors.

However, the previous setup allowed for the host environment to
implement additional keywords – these are resolved at runtime with
the `lookupBuildInHelper` and `lookupBuiltInModifier` hooks, and
the error is only emitted if these hooks returned `null`.

To allow for this possibility, `precompile` now accept an option
for additional strict mode keywords that the host environment is
expected to provide.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants