diff --git a/packages/@glimmer-workspace/integration-tests/lib/suites/components.ts b/packages/@glimmer-workspace/integration-tests/lib/suites/components.ts
index 56e989173c..d4a60da81c 100644
--- a/packages/@glimmer-workspace/integration-tests/lib/suites/components.ts
+++ b/packages/@glimmer-workspace/integration-tests/lib/suites/components.ts
@@ -268,7 +268,7 @@ export class GlimmerishComponents extends RenderTest {
})
'invoking dynamic component (local) via angle brackets'() {
this.registerComponent('Glimmer', 'Foo', 'hello world!');
- this.render(`{{#with (component 'Foo') as |Other|}}
` is resolved as `modifier:h`. +In loose mode, free variable references in "call" positions are resolved using a contextual namespace. For example, the free variable reference `h` in `(h 123)` is resolved as `helper:h`. The free variable reference `h` in `
` is resolved as `modifier:h`. In strict mode, all free variable references refer to bindings provided by a JavaScript scope that the template will be embedded within. @@ -94,26 +91,6 @@ The `Strict` resolution applies to all free variables encountered while parsing None. Strict mode templates must be embedded in a JavaScript context where all free variable references are in scope. A compile-time error should be produced if free there are variable references that do not correspond to any in-scope variables. -### Fallback Semantics - -When a free variable resolution is said to have "fallback semantics", it means the following algorithm: - -1. Attempt to resolve the name in the namespaces for the resolution, if any. -2. If the name could not be resolved, resolve it as a property on `this`. - -> Note: A free variable resolution has fallback semantics if it's an append or attribute curly without arguments, or if it's a path in argument position. See the summary table below for full details. - -### Eval Mode Semantics - -When a free variable resolution has fallback semantics, it is also said to have "eval mode semantics", which means: - -1. If the template is evaluated in eval mode (i.e. as a partial), dynamically resolve the free variable in the context of the template that invoked the partial (the "invoker"): -1. If the variable name is in the local scope of the invoker, resolve it as a _local_ variable in the invoker's local scope -1. Otherwise: -1. if the invoker is also in eval mode, repeat the process with the invoker's invoker -1. if the invoker is not in eval mode, resolve the free variable using fallback semantics in the invoker's scope -1. Otherwise, resolve the free variable using fallback semantics in the current scope - ### Namespaced Variable Resolution | | | @@ -123,7 +100,6 @@ When a free variable resolution has fallback semantics, it is also said to have | Arguments? | Any | | | | | Namespace | see table below | -| Fallback semantics? | ⛔ | These resolutions occur in syntaxes that are definitely calls (e.g. subexpressions, blocks, modifiers, etc.). @@ -140,16 +116,15 @@ These resolutions occur in syntaxes that are definitely calls (e.g. subexpressio If the variable reference cannot be resolved in its namespace. -### Namespaced Resolution: Ambiguous Component or Helper +### Namespaced Resolution: Append | | | | ------------------- | ----------------------- | | Syntax Positions | append | | Path has dots? | ❌ | -| Arguments? | ➕ | +| Arguments? | Any | | | | | Namespace | `helper` or `component` | -| Fallback semantics? | ⛔ | This resolution occurs in append nodes with at least one argument, and when the path does not have dots (e.g. `{{hello world}}`). @@ -158,6 +133,7 @@ This resolution occurs in append nodes with at least one argument, and when the | situation | variable | namespace | | ------------------- | -------- | ------------------- | | `{{x y}}` as append | `x` | `ComponentOrHelper` | +| `{{x}}` as append | `x` | `ComponentOrHelper` | In this situation, the `x` may refer to: @@ -168,37 +144,7 @@ In this situation, the `x` may refer to: If the variable reference cannot be resolved in the `helper` or `component` namespaces. -### Ambiguous Resolution: Append Ambiguity - -| | | -| ------------------- | --------------------- | -| Syntax Positions | append | -| Path has dots? | ❌ | -| Arguments? | ❌ | -| | | -| Namespace | `helper`, `component` | -| Fallback semantics? | ✅ | - -This resolution occurs in append nodes with zero arguments, and when the path does not have dots (e.g. `{{hello}}`). - -#### Applicable Situations - -| situation | variable | ambiguity | -| ----------------- | -------- | --------- | -| `{{x}}` as append | `x` | `Append` | - -In this situation, the `x` may refer to: - -- a helper `x` -- a component `x` -- a local variable in partial scope -- `this.x`. - -#### Runtime Error Cases - -None. - -### Ambiguous Resolution: Attribute Ambiguity +### Namespaced Resolution: Attribute This resolution context occurs in attribute nodes with zero arguments, and when the path does not have dots. @@ -206,26 +152,24 @@ This resolution context occurs in attribute nodes with zero arguments, and when | ------------------- | ------------------------ | | Syntax Positions | attribute, interpolation | | Path has dots? | ❌ | -| Arguments? | ❌ | +| Arguments? | Any | | | | | Namespace | `helper` | -| Fallback semantics? | ✅ | #### Applicable Situations -| situation | variable | ambiguity | -| --------------------------------------------- | -------- | --------- | -| `
` ` ` = (
* ```
*/
function isSimpleCallee(node: AstCallParts): boolean {
- let path = node.path;
-
- return isSimplePath(path);
+ return isSimplePath(node.path);
}
type SimplePath = ASTv1.PathExpression & { head: ASTv1.VarHead };
@@ -127,10 +118,3 @@ function isSimplePath(node: ASTv1.Expression): node is SimplePath {
return false;
}
}
-
-/**
- * The call expression has at least one argument.
- */
-function isInvokeNode(node: AstCallParts): boolean {
- return node.params.length > 0 || node.hash.pairs.length > 0;
-}
diff --git a/packages/@glimmer/syntax/lib/v2/normalize.ts b/packages/@glimmer/syntax/lib/v2/normalize.ts
index aaf120fac6..ebb814af55 100644
--- a/packages/@glimmer/syntax/lib/v2/normalize.ts
+++ b/packages/@glimmer/syntax/lib/v2/normalize.ts
@@ -240,13 +240,13 @@ class ExpressionNormalizer {
let { path, params, hash } = parts;
let callee = this.normalize(path, context);
- let paramList = params.map((p) => this.normalize(p, ASTv2.ARGUMENT_RESOLUTION));
+ let paramList = params.map((p) => this.normalize(p, ASTv2.STRICT_RESOLUTION));
let paramLoc = SpanList.range(paramList, callee.loc.collapse('end'));
let namedLoc = this.block.loc(hash.loc);
let argsLoc = SpanList.range([paramLoc, namedLoc]);
let positional = this.block.builder.positional(
- params.map((p) => this.normalize(p, ASTv2.ARGUMENT_RESOLUTION)),
+ params.map((p) => this.normalize(p, ASTv2.STRICT_RESOLUTION)),
paramLoc
);
@@ -268,7 +268,7 @@ class ExpressionNormalizer {
return this.block.builder.namedArgument(
new SourceSlice({ chars: pair.key, loc: keyOffsets }),
- this.normalize(pair.value, ASTv2.ARGUMENT_RESOLUTION)
+ this.normalize(pair.value, ASTv2.STRICT_RESOLUTION)
);
}
@@ -375,6 +375,15 @@ class StatementNormalizer {
let { escaped } = mustache;
let loc = this.block.loc(mustache.loc);
+ let resolution = this.block.resolutionFor(mustache, AppendSyntaxContext);
+
+ if (resolution.result === 'error') {
+ throw generateSyntaxError(
+ `You attempted to render a path (\`{{${resolution.path}}}\`), but ${resolution.head} was not in scope`,
+ loc
+ );
+ }
+
// Normalize the call parts in AppendSyntaxContext
let callParts = this.expr.callParts(
{
@@ -382,7 +391,7 @@ class StatementNormalizer {
params: mustache.params,
hash: mustache.hash,
},
- AppendSyntaxContext(mustache)
+ resolution.result
);
let value = callParts.args.isEmpty()
@@ -519,7 +528,7 @@ class ElementNormalizer {
if (resolution.result === 'error') {
throw generateSyntaxError(
- `You attempted to invoke a path (\`{{#${resolution.path}}}\`) as a modifier, but ${resolution.head} was not in scope. Try adding \`this\` to the beginning of the path`,
+ `You attempted to invoke a path (\`{{${resolution.path}}}\`) as a modifier, but ${resolution.head} was not in scope`,
m.loc
);
}
@@ -539,8 +548,17 @@ class ElementNormalizer {
*/
private mustacheAttr(mustache: ASTv1.MustacheStatement): ASTv2.ExpressionNode {
// Normalize the call parts in AttrValueSyntaxContext
+ let resolution = this.ctx.resolutionFor(mustache, AttrValueSyntaxContext);
+
+ if (resolution.result === 'error') {
+ throw generateSyntaxError(
+ `You attempted to render a path (\`{{${resolution.path}}}\`), but ${resolution.head} was not in scope`,
+ mustache.loc
+ );
+ }
+
let sexp = this.ctx.builder.sexp(
- this.expr.callParts(mustache, AttrValueSyntaxContext(mustache)),
+ this.expr.callParts(mustache, resolution.result),
this.ctx.loc(mustache.loc)
);
@@ -597,76 +615,65 @@ class ElementNormalizer {
let offsets = this.ctx.loc(m.loc);
let nameSlice = offsets.sliceStartChars({ chars: m.name.length }).toSlice(m.name);
-
let value = this.attrValue(m.value);
+
return this.ctx.builder.attr(
{ name: nameSlice, value: value.expr, trusting: value.trusting },
offsets
);
}
- private maybeDeprecatedCall(
- arg: SourceSlice,
- part: ASTv1.MustacheStatement | ASTv1.TextNode | ASTv1.ConcatStatement
- ): { expr: ASTv2.DeprecatedCallExpression; trusting: boolean } | null {
- if (this.ctx.strict) {
- return null;
- }
+ // An arg curly
`` | `x` | `Attr` |
+| situation | variable | namespace |
+| ------------------------------------------------- | -------- | --------- |
+| `
`` | `x` | `Helper` |
+| `
`` | `x` | `Helper` |
In this situation, the `x` may refer to:
- a helper `x`
-- a local variable in partial scope
-- `this.x`.
#### Runtime Error Cases
-None.
+If the variable reference cannot be resolved in the `helper` namespaces.
### Summary
@@ -253,7 +197,6 @@ Situations that meet all three of these criteria are syntax errors:
| ------------------- | --- |
| Path has dots? | ❌ |
| Arguments? | Any |
-| Fallback semantics? | ⛔ |
| Syntax Position | Example | | Namespace |
| --------------- | ------------- | --- | ----------- |
@@ -264,11 +207,11 @@ Situations that meet all three of these criteria are syntax errors:
#### Append
-| Syntax Position | Example | Dots? | Args? | | Namespace | Fallback? |
-| --------------- | --------- | ----- | ----- | --- | --------------------- | --------- |
-| `Append` | `{{x}}` | ❌ | ❌ | | `helper`, `component` | ✅ |
-| `Append` | `{{x.y}}` | ➕ | ❌ | | None | ✅ |
-| `Append` | `{{x y}}` | ❌ | ➕ | | `helper`, `component` | ⛔ |
+| Syntax Position | Example | Dots? | Args? | | Namespace |
+| --------------- | --------- | ----- | ----- | --- | --------------------- |
+| `Append` | `{{x}}` | ❌ | ❌ | | `helper`, `component` |
+| `Append` | `{{x.y}}` | ➕ | ❌ | | None |
+| `Append` | `{{x y}}` | ❌ | ➕ | | `helper`, `component` |
#### Attributes
@@ -278,8 +221,8 @@ The `Attribute` syntax position includes:
- the value of an element argument (`@title={{...}}`)
- a part of an interpolation (`href="{{...}}.html"`)
-| Syntax Position | Example | Dots? | Arguments? | | Namespace | Fallback? |
-| --------------- | -------------- | ----- | ---------- | --- | --------- | --------- |
-| `Attribute` | `href={{x}}` | ❌ | ❌ | | `helper` | ✅ |
-| `Attribute` | `href={{x.y}}` | ➕ | ❌ | | None | ✅ |
-| `Attribute` | `href={{x y}}` | ❌ | ➕ | | `helper` | ⛔ |
+| Syntax Position | Example | Dots? | Arguments? | | Namespace |
+| --------------- | -------------- | ----- | ---------- | --- | --------- |
+| `Attribute` | `href={{x}}` | ❌ | ❌ | | `helper` |
+| `Attribute` | `href={{x.y}}` | ➕ | ❌ | | None |
+| `Attribute` | `href={{x y}}` | ❌ | ➕ | | `helper` |
diff --git a/packages/@glimmer/syntax/lib/v2/builders.ts b/packages/@glimmer/syntax/lib/v2/builders.ts
index 007a77ac70..3f109ac995 100644
--- a/packages/@glimmer/syntax/lib/v2/builders.ts
+++ b/packages/@glimmer/syntax/lib/v2/builders.ts
@@ -221,18 +221,6 @@ export class Builder {
});
}
- deprecatedCall(
- arg: SourceSlice,
- callee: ASTv2.FreeVarReference,
- loc: SourceSpan
- ): ASTv2.DeprecatedCallExpression {
- return new ASTv2.DeprecatedCallExpression({
- loc,
- arg,
- callee,
- });
- }
-
interpolate(parts: ASTv2.ExpressionNode[], loc: SourceSpan): ASTv2.InterpolateExpression {
assertPresentArray(parts);
diff --git a/packages/@glimmer/syntax/lib/v2/loose-resolution.ts b/packages/@glimmer/syntax/lib/v2/loose-resolution.ts
index b7cb1dd888..e678059fda 100644
--- a/packages/@glimmer/syntax/lib/v2/loose-resolution.ts
+++ b/packages/@glimmer/syntax/lib/v2/loose-resolution.ts
@@ -34,50 +34,43 @@ export function BlockSyntaxContext(node: ASTv1.BlockStatement): ASTv2.FreeVarRes
if (isSimpleCallee(node)) {
return ASTv2.LooseModeResolution.namespaced(ASTv2.COMPONENT_NAMESPACE);
} else {
- return ASTv2.LooseModeResolution.fallback();
+ return null;
}
}
export function ComponentSyntaxContext(node: ASTv1.PathExpression): ASTv2.FreeVarResolution | null {
if (isSimplePath(node)) {
- return ASTv2.LooseModeResolution.namespaced(ASTv2.FreeVarNamespace.Component, true);
+ return ASTv2.LooseModeResolution.namespaced(ASTv2.COMPONENT_NAMESPACE, true);
} else {
return null;
}
}
/**
- * This corresponds to append positions (text curlies or attribute
- * curlies). In strict mode, this also corresponds to arg curlies.
+ * This corresponds to attribute curlies (