Support definite assignment assertion x!: i32 (#260)

This commit is contained in:
Andy Hanson 2018-10-27 09:29:59 -07:00 committed by Daniel Wirtz
parent e58582e9e9
commit 0bb5cb829e
9 changed files with 84 additions and 17 deletions

View File

@ -36,44 +36,46 @@ export enum CommonFlags {
GET = 1 << 11,
/** Has a `set` modifier. */
SET = 1 << 12,
/** Has a definite assignment assertion `!` as in `x!: i32;`. */
DEFINITE_ASSIGNMENT = 1 << 13,
// Extended modifiers usually derived from basic modifiers
/** Is ambient, that is either declared or nested in a declared element. */
AMBIENT = 1 << 13,
AMBIENT = 1 << 14,
/** Is generic. */
GENERIC = 1 << 14,
GENERIC = 1 << 15,
/** Is part of a generic context. */
GENERIC_CONTEXT = 1 << 15,
GENERIC_CONTEXT = 1 << 16,
/** Is an instance member. */
INSTANCE = 1 << 16,
INSTANCE = 1 << 17,
/** Is a constructor. */
CONSTRUCTOR = 1 << 17,
CONSTRUCTOR = 1 << 18,
/** Is an arrow function. */
ARROW = 1 << 18,
ARROW = 1 << 19,
/** Is a module export. */
MODULE_EXPORT = 1 << 19,
MODULE_EXPORT = 1 << 20,
/** Is a module import. */
MODULE_IMPORT = 1 << 20,
MODULE_IMPORT = 1 << 21,
// Compilation states
/** Is compiled. */
COMPILED = 1 << 21,
COMPILED = 1 << 22,
/** Has a constant value and is therefore inlined. */
INLINED = 1 << 22,
INLINED = 1 << 23,
/** Is scoped. */
SCOPED = 1 << 23,
SCOPED = 1 << 24,
/** Is a trampoline. */
TRAMPOLINE = 1 << 24,
TRAMPOLINE = 1 << 25,
/** Is a virtual method. */
VIRTUAL = 1 << 25,
VIRTUAL = 1 << 26,
/** Is the main function. */
MAIN = 1 << 26,
MAIN = 1 << 27,
// Other
QUOTED = 1 << 27
QUOTED = 1 << 28
}
/** Path delimiter inserted between file system levels. */

View File

@ -29,6 +29,7 @@ export enum DiagnosticCode {
Constructor_of_class_0_must_not_require_any_arguments = 216,
Function_0_cannot_be_inlined_into_itself = 217,
Cannot_access_method_0_without_calling_it_as_it_requires_this_to_be_set = 218,
Optional_properties_are_not_supported = 219,
Unterminated_string_literal = 1002,
Identifier_expected = 1003,
_0_expected = 1005,
@ -81,6 +82,7 @@ export enum DiagnosticCode {
Decorators_are_not_valid_here = 1206,
_abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration = 1242,
Method_0_cannot_have_an_implementation_because_it_is_marked_abstract = 1245,
A_definite_assignment_assertion_is_not_permitted_in_this_context = 1255,
A_class_may_only_extend_another_class = 1311,
A_parameter_property_cannot_be_declared_using_a_rest_parameter = 1317,
Duplicate_identifier_0 = 2300,
@ -149,6 +151,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 216: return "Constructor of class '{0}' must not require any arguments.";
case 217: return "Function '{0}' cannot be inlined into itself.";
case 218: return "Cannot access method '{0}' without calling it as it requires 'this' to be set.";
case 219: return "Optional properties are not supported.";
case 1002: return "Unterminated string literal.";
case 1003: return "Identifier expected.";
case 1005: return "'{0}' expected.";
@ -201,6 +204,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 1206: return "Decorators are not valid here.";
case 1242: return "'abstract' modifier can only appear on a class, method, or property declaration.";
case 1245: return "Method '{0}' cannot have an implementation because it is marked abstract.";
case 1255: return "A definite assignment assertion '!' is not permitted in this context.";
case 1311: return "A class may only extend another class.";
case 1317: return "A parameter property cannot be declared using a rest parameter.";
case 2300: return "Duplicate identifier '{0}'.";

View File

@ -21,6 +21,7 @@
"Constructor of class '{0}' must not require any arguments.": 216,
"Function '{0}' cannot be inlined into itself.": 217,
"Cannot access method '{0}' without calling it as it requires 'this' to be set.": 218,
"Optional properties are not supported.": 219,
"Unterminated string literal.": 1002,
"Identifier expected.": 1003,
@ -74,6 +75,7 @@
"Decorators are not valid here.": 1206,
"'abstract' modifier can only appear on a class, method, or property declaration.": 1242,
"Method '{0}' cannot have an implementation because it is marked abstract.": 1245,
"A definite assignment assertion '!' is not permitted in this context.": 1255,
"A class may only extend another class.": 1311,
"A parameter property cannot be declared using a rest parameter.": 1317,

View File

@ -990,6 +990,9 @@ export class ASTBuilder {
this.serializeAccessModifiers(node);
this.visitIdentifierExpression(node.name);
var sb = this.sb;
if (node.flags & CommonFlags.DEFINITE_ASSIGNMENT) {
sb.push("!");
}
var type = node.type;
if (type) {
sb.push(": ");
@ -1376,6 +1379,9 @@ export class ASTBuilder {
this.visitIdentifierExpression(node.name);
var type = node.type;
var sb = this.sb;
if (node.flags & CommonFlags.DEFINITE_ASSIGNMENT) {
sb.push("!");
}
if (type) {
sb.push(": ");
this.visitTypeNode(type);

View File

@ -769,6 +769,9 @@ export class Parser extends DiagnosticEmitter {
}
var identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
var flags = parentFlags;
if (tn.skip(Token.EXCLAMATION)) {
flags |= CommonFlags.DEFINITE_ASSIGNMENT;
}
var type: CommonTypeNode | null = null;
if (tn.skip(Token.COLON)) {
@ -800,13 +803,19 @@ export class Parser extends DiagnosticEmitter {
); // recoverable
}
}
var range = Range.join(identifier.range, tn.range());
if ((flags & CommonFlags.DEFINITE_ASSIGNMENT) && initializer) {
this.error(
DiagnosticCode.A_definite_assignment_assertion_is_not_permitted_in_this_context,
range);
}
return Node.createVariableDeclaration(
identifier,
type,
initializer,
parentDecorators,
flags,
Range.join(identifier.range, tn.range())
range
);
}
@ -1926,6 +1935,15 @@ export class Parser extends DiagnosticEmitter {
}
let type: CommonTypeNode | null = null;
if (tn.skip(Token.QUESTION)) {
this.error(
DiagnosticCode.Optional_properties_are_not_supported,
tn.range(startPos, tn.pos)
);
}
if (tn.skip(Token.EXCLAMATION)) {
flags |= CommonFlags.DEFINITE_ASSIGNMENT;
}
if (tn.skip(Token.COLON)) {
type = this.parseType(tn);
if (!type) return null;
@ -1940,13 +1958,20 @@ export class Parser extends DiagnosticEmitter {
initializer = this.parseExpression(tn);
if (!initializer) return null;
}
let range = tn.range(startPos, tn.pos);
if ((flags & CommonFlags.DEFINITE_ASSIGNMENT) && ((flags & CommonFlags.STATIC) || isInterface || initializer)) {
this.error(
DiagnosticCode.A_definite_assignment_assertion_is_not_permitted_in_this_context,
range
);
}
let retField = Node.createFieldDeclaration(
name,
type,
initializer,
decorators,
flags,
tn.range(startPos, tn.pos)
range
);
tn.skip(Token.SEMICOLON);
return retField;

View File

@ -0,0 +1,9 @@
class C {
x!: i32;
x!: i32 = 0;
static x!: i32;
}
function f(): void {
let x!: i32;
let x!: i32 = 0;
}

View File

@ -0,0 +1,12 @@
class C {
x!: i32;
x!: i32 = 0;
static x!: i32;
}
function f(): void {
let x!: i32;
let x!: i32 = 0;
}
// ERROR 1255: "A definite assignment assertion '!' is not permitted in this context." in definite-assignment-assertion.ts:3:10
// ERROR 1255: "A definite assignment assertion '!' is not permitted in this context." in definite-assignment-assertion.ts:4:14
// ERROR 1255: "A definite assignment assertion '!' is not permitted in this context." in definite-assignment-assertion.ts:8:6

View File

@ -0,0 +1,3 @@
class C {
x?: i32;
}

View File

@ -0,0 +1,4 @@
class C {
x: i32;
}
// ERROR 219: "Optional properties are not supported." in optional-property.ts:2:9