added collision check for '_this'

This commit is contained in:
Vladimir Matveev 2014-07-18 16:13:21 -07:00
parent 1abedc30c4
commit 30ecd19029
21 changed files with 417 additions and 6 deletions

View file

@ -59,6 +59,7 @@ module ts {
var mergedSymbols: Symbol[] = [];
var symbolLinks: SymbolLinks[] = [];
var nodeLinks: NodeLinks[] = [];
var potentialThisCollisions: Node[] = [];
var diagnostics: Diagnostic[] = [];
var diagnosticsModified: boolean = false;
@ -3210,6 +3211,7 @@ module ts {
getNodeLinks(node).resolvedSymbol = symbol;
checkCollisionWithCapturedSuperVariable(node, node);
checkCollisionWithCapturedThisVariable(node, node);
checkCollisionWithIndexVariableInGeneratedCode(node, node);
return getTypeOfSymbol(getExportSymbolOfValueSymbolIfExported(symbol));
@ -4436,6 +4438,7 @@ module ts {
}
checkCollisionWithCapturedSuperVariable(node, node.name);
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithArgumentsInGeneratedCode(node);
if (program.getCompilerOptions().noImplicitAny && !node.type) {
switch (node.kind) {
@ -4970,21 +4973,59 @@ module ts {
}
}
function checkCollisionWithCapturedSuperVariable(node: Node, name: Identifier) {
if (!(name && name.text === "_super")) {
return;
function needCollisionCheckForIdentifier(node: Node, identifier: Identifier, name: string): boolean {
if (!(identifier && identifier.text === name)) {
return false;
}
if (node.kind === SyntaxKind.Property ||
node.kind === SyntaxKind.Method ||
node.kind === SyntaxKind.GetAccessor ||
node.kind === SyntaxKind.SetAccessor) {
// it is ok to have member named '_super' - member access is always qualified
return;
// it is ok to have member named '_super' or '_this' - member access is always qualified
return false
}
if (isInAmbientContext(node)) {
// ambient context - no codegen impact
return false;
}
if (node.kind === SyntaxKind.Parameter && !(<FunctionDeclaration>node.parent).body) {
// just an overload - no codegen impact
return false;
}
return true;
}
function checkCollisionWithCapturedThisVariable(node: Node, name: Identifier): void {
if (!needCollisionCheckForIdentifier(node, name, "_this")) {
return;
}
potentialThisCollisions.push(node);
}
// this function will run after checking the source file so 'CaptureThis' for all nodes
function checkIfThisIsCapturedInEnclosingScope(node: Node): void {
var current = node;
while (current) {
if (getNodeCheckFlags(current) & NodeCheckFlags.CaptureThis) {
var isDeclaration = node.kind !== SyntaxKind.Identifier;
if (isDeclaration) {
error((<Declaration>node).name, Diagnostics.Duplicate_identifier_this_Compiler_uses_variable_declaration_this_to_capture_this_reference);
}
else {
error(node, Diagnostics.Expression_resolves_to_variable_declaration_this_that_compiler_uses_to_capture_this_reference);
}
return;
}
current = current.parent;
}
}
function checkCollisionWithCapturedSuperVariable(node: Node, name: Identifier) {
if (!needCollisionCheckForIdentifier(node, name, "_super")) {
return;
}
@ -5029,6 +5070,7 @@ module ts {
}
checkCollisionWithCapturedSuperVariable(node, node.name);
checkCollisionWithCapturedThisVariable(node, node.name);
if (!useTypeFromValueDeclaration) {
// TypeScript 1.0 spec (April 2014): 5.1
// Multiple declarations for the same variable name in the same declaration space are permitted,
@ -5298,6 +5340,7 @@ module ts {
checkDeclarationModifiers(node);
checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0);
checkTypeParameters(node.typeParameters);
checkCollisionWithCapturedThisVariable(node, node.name);
var symbol = getSymbolOfNode(node);
var type = <InterfaceType>getDeclaredTypeOfSymbol(symbol);
var staticType = <ObjectType>getTypeOfSymbol(symbol);
@ -5498,6 +5541,7 @@ module ts {
function checkEnumDeclaration(node: EnumDeclaration) {
checkDeclarationModifiers(node);
checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0);
checkCollisionWithCapturedThisVariable(node, node.name);
var enumSymbol = getSymbolOfNode(node);
var enumType = getDeclaredTypeOfSymbol(enumSymbol);
var autoValue = 0;
@ -5569,6 +5613,7 @@ module ts {
function checkModuleDeclaration(node: ModuleDeclaration) {
checkDeclarationModifiers(node);
checkCollisionWithCapturedThisVariable(node, node.name);
var symbol = getSymbolOfNode(node);
if (symbol.flags & SymbolFlags.ValueModule && symbol.declarations.length > 1 && !isInAmbientContext(node)) {
var classOrFunc = getFirstNonAmbientClassOrFunctionDeclaration(symbol);
@ -5594,6 +5639,7 @@ module ts {
function checkImportDeclaration(node: ImportDeclaration) {
checkDeclarationModifiers(node);
checkCollisionWithCapturedThisVariable(node, node.name);
var symbol = getSymbolOfNode(node);
var target: Symbol;
@ -5751,6 +5797,7 @@ module ts {
var links = getNodeLinks(node);
if (!(links.flags & NodeCheckFlags.TypeChecked)) {
emitExtends = false;
potentialThisCollisions.length = 0;
forEach(node.statements, checkSourceElement);
if (node.flags & NodeFlags.ExternalModule) {
var symbol = getExportAssignmentSymbol(node.symbol);
@ -5759,6 +5806,10 @@ module ts {
getSymbolLinks(symbol).referenced = true;
}
}
if (potentialThisCollisions.length) {
forEach(potentialThisCollisions, checkIfThisIsCapturedInEnclosingScope);
potentialThisCollisions.length = 0;
}
if (emitExtends) links.flags |= NodeCheckFlags.EmitExtends;
links.flags |= NodeCheckFlags.TypeChecked;
}

View file

@ -135,7 +135,9 @@ module ts {
Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class: { code: 2194, category: DiagnosticCategory.Error, key: "Return type of constructor signature must be assignable to the instance type of the class" },
Ambient_external_module_declaration_cannot_specify_relative_module_name: { code: 2196, category: DiagnosticCategory.Error, key: "Ambient external module declaration cannot specify relative module name." },
Import_declaration_in_an_ambient_external_module_declaration_cannot_reference_external_module_through_relative_external_module_name: { code: 2197, category: DiagnosticCategory.Error, key: "Import declaration in an ambient external module declaration cannot reference external module through relative external module name." },
Duplicate_identifier_this_Compiler_uses_variable_declaration_this_to_capture_this_reference: { code: 2200, category: DiagnosticCategory.Error, key: "Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference." },
Duplicate_identifier_super_Compiler_uses_super_to_capture_base_class_reference: { code: 2205, category: DiagnosticCategory.Error, key: "Duplicate identifier '_super'. Compiler uses '_super' to capture base class reference." },
Expression_resolves_to_variable_declaration_this_that_compiler_uses_to_capture_this_reference: { code: 2206, category: DiagnosticCategory.Error, key: "Expression resolves to variable declaration '_this' that compiler uses to capture 'this' reference." },
Expression_resolves_to_super_that_compiler_uses_to_capture_base_class_reference: { code: 2207, category: DiagnosticCategory.Error, key: "Expression resolves to '_super' that compiler uses to capture base class reference." },
Duplicate_identifier_i_Compiler_uses_i_to_initialize_rest_parameter: { code: 2224, category: DiagnosticCategory.Error, key: "Duplicate identifier '_i'. Compiler uses '_i' to initialize rest parameter." },
Duplicate_identifier_arguments_Compiler_uses_arguments_to_initialize_rest_parameters: { code: 2225, category: DiagnosticCategory.Error, key: "Duplicate identifier 'arguments'. Compiler uses 'arguments' to initialize rest parameters." },

View file

@ -534,10 +534,18 @@
"category": "Error",
"code": 2197
},
"Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.": {
"category": "Error",
"code": 2200
},
"Duplicate identifier '_super'. Compiler uses '_super' to capture base class reference.": {
"category": "Error",
"code": 2205
},
"Expression resolves to variable declaration '_this' that compiler uses to capture 'this' reference.": {
"category": "Error",
"code": 2206
},
"Expression resolves to '_super' that compiler uses to capture base class reference.": {
"category": "Error",
"code": 2207

View file

@ -0,0 +1,8 @@
==== tests/cases/compiler/collisionThisExpressionAndAliasInGlobal.ts (1 errors) ====
module a {
export var b = 10;
}
var f = () => this;
import _this = a; // Error
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.

View file

@ -0,0 +1,7 @@
==== tests/cases/compiler/collisionThisExpressionAndAmbientClassInGlobal.ts (1 errors) ====
declare class _this { // no error - as no code generation
}
var f = () => this;
var a = new _this(); // Error
~~~~~
!!! Expression resolves to variable declaration '_this' that compiler uses to capture 'this' reference.

View file

@ -0,0 +1,6 @@
==== tests/cases/compiler/collisionThisExpressionAndAmbientVarInGlobal.ts (1 errors) ====
declare var _this: number; // no error as no code gen
var f = () => this;
_this = 10; // Error
~~~~~
!!! Expression resolves to variable declaration '_this' that compiler uses to capture 'this' reference.

View file

@ -0,0 +1,6 @@
==== tests/cases/compiler/collisionThisExpressionAndClassInGlobal.ts (1 errors) ====
class _this {
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
}
var f = () => this;

View file

@ -0,0 +1,8 @@
==== tests/cases/compiler/collisionThisExpressionAndEnumInGlobal.ts (1 errors) ====
enum _this { // Error
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
_thisVal1,
_thisVal2,
}
var f = () => this;

View file

@ -0,0 +1,7 @@
==== tests/cases/compiler/collisionThisExpressionAndFunctionInGlobal.ts (1 errors) ====
function _this() { //Error
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
return 10;
}
var f = () => this;

View file

@ -1,4 +1,4 @@
==== tests/cases/compiler/collisionThisExpressionAndLocalVarInAccessors.ts (4 errors) ====
==== tests/cases/compiler/collisionThisExpressionAndLocalVarInAccessors.ts (8 errors) ====
class class1 {
get a(): number {
~
@ -6,6 +6,8 @@
var x2 = {
doStuff: (callback) => () => {
var _this = 2;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
return callback(this);
}
}
@ -18,6 +20,8 @@
var x2 = {
doStuff: (callback) => () => {
var _this = 2;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
return callback(this);
}
}
@ -30,6 +34,8 @@
~
!!! Accessors are only available when targeting ECMAScript 5 and higher.
var _this = 2;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
var x2 = {
doStuff: (callback) => () => {
return callback(this);
@ -42,6 +48,8 @@
~
!!! Accessors are only available when targeting ECMAScript 5 and higher.
var _this = 2;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
var x2 = {
doStuff: (callback) => () => {
return callback(this);

View file

@ -0,0 +1,26 @@
==== tests/cases/compiler/collisionThisExpressionAndLocalVarInConstructor.ts (2 errors) ====
class class1 {
constructor() {
var x2 = {
doStuff: (callback) => () => {
var _this = 2;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
return callback(this);
}
}
}
}
class class2 {
constructor() {
var _this = 2;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
var x2 = {
doStuff: (callback) => () => {
return callback(this);
}
}
}
}

View file

@ -0,0 +1,10 @@
==== tests/cases/compiler/collisionThisExpressionAndLocalVarInFunction.ts (1 errors) ====
var console: {
log(val: any);
}
function x() {
var _this = 5;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
x => { console.log(this.x); };
}

View file

@ -0,0 +1,12 @@
==== tests/cases/compiler/collisionThisExpressionAndLocalVarInLambda.ts (1 errors) ====
declare function alert(message?: any): void;
var x = {
doStuff: (callback) => () => {
var _this = 2;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
return callback(this);
}
}
alert(x.doStuff(x => alert(x)));

View file

@ -0,0 +1,23 @@
==== tests/cases/compiler/collisionThisExpressionAndLocalVarInMethod.ts (2 errors) ====
class a {
method1() {
return {
doStuff: (callback) => () => {
var _this = 2;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
return callback(this);
}
}
}
method2() {
var _this = 2;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
return {
doStuff: (callback) => () => {
return callback(this);
}
}
}
}

View file

@ -0,0 +1,24 @@
==== tests/cases/compiler/collisionThisExpressionAndLocalVarInProperty.ts (2 errors) ====
class class1 {
public prop1 = {
doStuff: (callback) => () => {
var _this = 2;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
return callback(this);
}
}
}
class class2 {
constructor() {
var _this = 2;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
}
public prop1 = {
doStuff: (callback) => () => {
return callback(this);
}
}
}

View file

@ -0,0 +1,23 @@
==== tests/cases/compiler/collisionThisExpressionAndLocalVarWithSuperExperssion.ts (2 errors) ====
class a {
public foo() {
}
}
class b extends a {
public foo() {
var _this = 10;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
var f = () => super.foo();
}
}
class b2 extends a {
public foo() {
var f = () => {
var _this = 10;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
return super.foo()
}
}
}

View file

@ -0,0 +1,8 @@
==== tests/cases/compiler/collisionThisExpressionAndModuleInGlobal.ts (1 errors) ====
module _this { //Error
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
class c {
}
}
var f = () => this;

View file

@ -0,0 +1,15 @@
==== tests/cases/compiler/collisionThisExpressionAndNameResolution.ts (1 errors) ====
var console : {
log(message: any);
}
class Foo {
x() {
var _this = 10; // Local var. No this capture in x(), so no conflict.
function inner() {
console.log(_this); // Error as this doesnt not resolve to user defined _this
~~~~~
!!! Expression resolves to variable declaration '_this' that compiler uses to capture 'this' reference.
return x => this; // New scope. So should inject new _this capture into function inner
}
}
}

View file

@ -0,0 +1,110 @@
==== tests/cases/compiler/collisionThisExpressionAndParameter.ts (8 errors) ====
class Foo {
x() {
var _this = 10; // Local var. No this capture in x(), so no conflict.
function inner(_this: number) { // Error
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
return x => this; // New scope. So should inject new _this capture into function inner
}
}
y() {
var lamda = (_this: number) => { // Error
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
return x => this; // New scope. So should inject new _this capture
}
}
z(_this: number) { // Error
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
var lambda = () => {
return x => this; // New scope. So should inject new _this capture
}
}
x1() {
var _this = 10; // Local var. No this capture in x(), so no conflict.
function inner(_this: number) { // No Error
}
}
y1() {
var lamda = (_this: number) => { // No Error
}
}
z1(_this: number) { // No Error
var lambda = () => {
}
}
}
class Foo1 {
constructor(_this: number) { // Error
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
var x2 = {
doStuff: (callback) => () => {
return callback(this);
}
}
}
}
declare var console: {
log(msg: any);
}
function f1(_this: number) {
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
x => { console.log(this.x); };
}
declare class Foo2 {
constructor(_this: number); // no error - no code gen
z(_this: number); // no error - no code gen
}
declare function f2(_this: number); // no error
class Foo3 {
constructor(_this: string); // no code gen - no error
constructor(_this: number); // no code gen - no error
constructor(_this: any) { // Error
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
var x2 = {
doStuff: (callback) => () => {
return callback(this);
}
}
}
z(_this: string); // no code gen - no error
z(_this: number); // no code gen - no error
z(_this: any) { // Error
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
var lambda = () => {
return x => this; // New scope. So should inject new _this capture
}
}
}
declare var console: {
log(msg: any);
}
function f3(_this: number); // no code gen - no error
function f3(_this: string); // no code gen - no error
function f3(_this: any) {
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
x => { console.log(this.x); };
}
declare class Foo4 {
constructor(_this: string); // no code gen - no error
constructor(_this: number); // no code gen - no error
z(_this: string); // no code gen - no error
z(_this: number); // no code gen - no error
}
declare function f4(_this: number); // no code gen - no error
declare function f4(_this: string); // no code gen - no error

View file

@ -0,0 +1,44 @@
==== tests/cases/compiler/collisionThisExpressionAndPropertyNameAsConstuctorParameter.ts (4 errors) ====
class Foo2 {
constructor(_this: number) { //Error
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
var lambda = () => {
return x => this; // New scope. So should inject new _this capture
}
}
}
class Foo3 {
constructor(private _this: number) { // Error
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
var lambda = () => {
return x => this; // New scope. So should inject new _this capture
}
}
}
class Foo4 {
constructor(_this: number); // No code gen - no error
constructor(_this: string); // No code gen - no error
constructor(_this: any) { // Error
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
var lambda = () => {
return x => this; // New scope. So should inject new _this capture
}
}
}
class Foo5 {
constructor(_this: number); // No code gen - no error
constructor(_this: string); // No code gen - no error
constructor(private _this: any) { // Error
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
var lambda = () => {
return x => this; // New scope. So should inject new _this capture
}
}
}

View file

@ -0,0 +1,5 @@
==== tests/cases/compiler/collisionThisExpressionAndVarInGlobal.ts (1 errors) ====
var _this = 1;
~~~~~
!!! Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.
var f = () => this;