Adds support for looking up past Blocks in expando objects (#38031)

* Adds support for looking up past Blocks in expando objects

* Adds JS tests to validate the JS parsing also works

* Get the top level block expando tests green
This commit is contained in:
Orta 2020-04-29 10:35:33 -04:00 committed by GitHub
parent d9ad27f2dd
commit 0258db2210
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 275 additions and 3 deletions

View file

@ -2964,7 +2964,7 @@ namespace ts {
function bindSpecialPropertyAssignment(node: BindablePropertyAssignmentExpression) {
// Class declarations in Typescript do not allow property declarations
const parentSymbol = lookupSymbolForPropertyAccess(node.left.expression);
const parentSymbol = lookupSymbolForPropertyAccess(node.left.expression, container) || lookupSymbolForPropertyAccess(node.left.expression, blockScopeContainer) ;
if (!isInJSFile(node) && !isFunctionSymbol(parentSymbol)) {
return;
}
@ -3073,7 +3073,7 @@ namespace ts {
}
function bindPropertyAssignment(name: BindableStaticNameExpression, propertyAccess: BindableStaticAccessExpression, isPrototypeProperty: boolean, containerIsClass: boolean) {
let namespaceSymbol = lookupSymbolForPropertyAccess(name);
let namespaceSymbol = lookupSymbolForPropertyAccess(name, container) || lookupSymbolForPropertyAccess(name, blockScopeContainer);
const isToplevel = isTopLevelNamespaceAssignment(propertyAccess);
namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, propertyAccess.expression, isToplevel, isPrototypeProperty, containerIsClass);
bindPotentiallyNewExpandoMemberToNamespace(propertyAccess, namespaceSymbol, isPrototypeProperty);

View file

@ -2016,7 +2016,7 @@ namespace ts {
/**
* Get the assignment 'initializer' -- the righthand side-- when the initializer is container-like (See getExpandoInitializer).
* We treat the right hand side of assignments with container-like initalizers as declarations.
* We treat the right hand side of assignments with container-like initializers as declarations.
*/
export function getAssignedExpandoInitializer(node: Node | undefined): Expression | undefined {
if (node && node.parent && isBinaryExpression(node.parent) && node.parent.operatorToken.kind === SyntaxKind.EqualsToken) {

View file

@ -0,0 +1,100 @@
=== tests/cases/compiler/check.ts ===
// https://github.com/microsoft/TypeScript/issues/31972
// https://github.com/microsoft/TypeScript/issues/31972
interface Person {
>Person : Symbol(Person, Decl(check.ts, 0, 0))
first: string;
>first : Symbol(Person.first, Decl(check.ts, 5, 18))
last: string;
>last : Symbol(Person.last, Decl(check.ts, 6, 16))
}
{
const dice = () => Math.floor(Math.random() * 6);
>dice : Symbol(dice, Decl(check.ts, 11, 7))
>Math.floor : Symbol(Math.floor, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>floor : Symbol(Math.floor, Decl(lib.es5.d.ts, --, --))
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
dice.first = 'Rando';
>dice.first : Symbol(dice.first, Decl(check.ts, 11, 51))
>dice : Symbol(dice, Decl(check.ts, 11, 7))
>first : Symbol(dice.first, Decl(check.ts, 11, 51))
dice.last = 'Calrissian';
>dice.last : Symbol(dice.last, Decl(check.ts, 12, 23))
>dice : Symbol(dice, Decl(check.ts, 11, 7))
>last : Symbol(dice.last, Decl(check.ts, 12, 23))
const diceP: Person = dice;
>diceP : Symbol(diceP, Decl(check.ts, 14, 7))
>Person : Symbol(Person, Decl(check.ts, 0, 0))
>dice : Symbol(dice, Decl(check.ts, 11, 7))
}
=== tests/cases/compiler/check.js ===
// Creates a type { first:string, last: string }
/**
* @typedef {Object} Human - creates a new type named 'SpecialType'
* @property {string} first - a string property of SpecialType
* @property {string} last - a number property of SpecialType
*/
/**
* @param {Human} param used as a validation tool
*/
function doHumanThings(param) {}
>doHumanThings : Symbol(doHumanThings, Decl(check.js, 0, 0))
>param : Symbol(param, Decl(check.js, 10, 23))
const dice1 = () => Math.floor(Math.random() * 6);
>dice1 : Symbol(dice1, Decl(check.js, 12, 5), Decl(check.js, 12, 50))
>Math.floor : Symbol(Math.floor, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>floor : Symbol(Math.floor, Decl(lib.es5.d.ts, --, --))
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
// dice1.first = 'Rando';
dice1.last = 'Calrissian';
>dice1.last : Symbol(dice1.last, Decl(check.js, 12, 50))
>dice1 : Symbol(dice1, Decl(check.js, 12, 5), Decl(check.js, 12, 50))
>last : Symbol(dice1.last, Decl(check.js, 12, 50))
// doHumanThings(dice)
// but inside a block... you can't call a human
{
const dice2 = () => Math.floor(Math.random() * 6);
>dice2 : Symbol(dice2, Decl(check.js, 20, 7))
>Math.floor : Symbol(Math.floor, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>floor : Symbol(Math.floor, Decl(lib.es5.d.ts, --, --))
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
dice2.first = 'Rando';
>dice2.first : Symbol(dice2.first, Decl(check.js, 20, 52))
>dice2 : Symbol(dice2, Decl(check.js, 20, 7))
>first : Symbol(dice2.first, Decl(check.js, 20, 52))
dice2.last = 'Calrissian';
>dice2.last : Symbol(dice2.last, Decl(check.js, 21, 24))
>dice2 : Symbol(dice2, Decl(check.js, 20, 7))
>last : Symbol(dice2.last, Decl(check.js, 21, 24))
doHumanThings(dice2)
>doHumanThings : Symbol(doHumanThings, Decl(check.js, 0, 0))
>dice2 : Symbol(dice2, Decl(check.js, 20, 7))
}

View file

@ -0,0 +1,123 @@
=== tests/cases/compiler/check.ts ===
// https://github.com/microsoft/TypeScript/issues/31972
// https://github.com/microsoft/TypeScript/issues/31972
interface Person {
first: string;
>first : string
last: string;
>last : string
}
{
const dice = () => Math.floor(Math.random() * 6);
>dice : { (): number; first: string; last: string; }
>() => Math.floor(Math.random() * 6) : { (): number; first: string; last: string; }
>Math.floor(Math.random() * 6) : number
>Math.floor : (x: number) => number
>Math : Math
>floor : (x: number) => number
>Math.random() * 6 : number
>Math.random() : number
>Math.random : () => number
>Math : Math
>random : () => number
>6 : 6
dice.first = 'Rando';
>dice.first = 'Rando' : "Rando"
>dice.first : string
>dice : { (): number; first: string; last: string; }
>first : string
>'Rando' : "Rando"
dice.last = 'Calrissian';
>dice.last = 'Calrissian' : "Calrissian"
>dice.last : string
>dice : { (): number; first: string; last: string; }
>last : string
>'Calrissian' : "Calrissian"
const diceP: Person = dice;
>diceP : Person
>dice : { (): number; first: string; last: string; }
}
=== tests/cases/compiler/check.js ===
// Creates a type { first:string, last: string }
/**
* @typedef {Object} Human - creates a new type named 'SpecialType'
* @property {string} first - a string property of SpecialType
* @property {string} last - a number property of SpecialType
*/
/**
* @param {Human} param used as a validation tool
*/
function doHumanThings(param) {}
>doHumanThings : (param: Human) => void
>param : Human
const dice1 = () => Math.floor(Math.random() * 6);
>dice1 : { (): number; last: string; }
>() => Math.floor(Math.random() * 6) : { (): number; last: string; }
>Math.floor(Math.random() * 6) : number
>Math.floor : (x: number) => number
>Math : Math
>floor : (x: number) => number
>Math.random() * 6 : number
>Math.random() : number
>Math.random : () => number
>Math : Math
>random : () => number
>6 : 6
// dice1.first = 'Rando';
dice1.last = 'Calrissian';
>dice1.last = 'Calrissian' : "Calrissian"
>dice1.last : string
>dice1 : { (): number; last: string; }
>last : string
>'Calrissian' : "Calrissian"
// doHumanThings(dice)
// but inside a block... you can't call a human
{
const dice2 = () => Math.floor(Math.random() * 6);
>dice2 : { (): number; first: string; last: string; }
>() => Math.floor(Math.random() * 6) : { (): number; first: string; last: string; }
>Math.floor(Math.random() * 6) : number
>Math.floor : (x: number) => number
>Math : Math
>floor : (x: number) => number
>Math.random() * 6 : number
>Math.random() : number
>Math.random : () => number
>Math : Math
>random : () => number
>6 : 6
dice2.first = 'Rando';
>dice2.first = 'Rando' : "Rando"
>dice2.first : string
>dice2 : { (): number; first: string; last: string; }
>first : string
>'Rando' : "Rando"
dice2.last = 'Calrissian';
>dice2.last = 'Calrissian' : "Calrissian"
>dice2.last : string
>dice2 : { (): number; first: string; last: string; }
>last : string
>'Calrissian' : "Calrissian"
doHumanThings(dice2)
>doHumanThings(dice2) : void
>doHumanThings : (param: Human) => void
>dice2 : { (): number; first: string; last: string; }
}

View file

@ -0,0 +1,49 @@
// https://github.com/microsoft/TypeScript/issues/31972
// @allowJs: true
// @noEmit: true
// @checkJs: true
// @filename: check.ts
// https://github.com/microsoft/TypeScript/issues/31972
interface Person {
first: string;
last: string;
}
{
const dice = () => Math.floor(Math.random() * 6);
dice.first = 'Rando';
dice.last = 'Calrissian';
const diceP: Person = dice;
}
// @filename: check.js
// Creates a type { first:string, last: string }
/**
* @typedef {Object} Human - creates a new type named 'SpecialType'
* @property {string} first - a string property of SpecialType
* @property {string} last - a number property of SpecialType
*/
/**
* @param {Human} param used as a validation tool
*/
function doHumanThings(param) {}
const dice1 = () => Math.floor(Math.random() * 6);
// dice1.first = 'Rando';
dice1.last = 'Calrissian';
// doHumanThings(dice)
// but inside a block... you can't call a human
{
const dice2 = () => Math.floor(Math.random() * 6);
dice2.first = 'Rando';
dice2.last = 'Calrissian';
doHumanThings(dice2)
}