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

Readonly properties and index signatures #6532

Merged
merged 34 commits into from
Jan 27, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b46efc9
Allow modifies on type members + introduce "readonly" modifier
ahejlsberg Jan 14, 2016
07b7008
Accepting new baselines
ahejlsberg Jan 14, 2016
d504357
Check readonly in assignments and type relations
ahejlsberg Jan 16, 2016
8319f2e
Accepting new baselines
ahejlsberg Jan 16, 2016
07763ed
Removing unused function
ahejlsberg Jan 17, 2016
5b233e4
Refactor to introduce IndexInfo type
ahejlsberg Jan 17, 2016
b8cd0e5
Support readonly indexers + include readonly modifier in typeToString
ahejlsberg Jan 18, 2016
17b5899
Fixing fourslash test
ahejlsberg Jan 18, 2016
13f763d
Accepting new baselines
ahejlsberg Jan 18, 2016
2a20e91
Allow assignments to readonly properties in constructors
ahejlsberg Jan 19, 2016
01c9686
Allow 'readonly' only on property and index signature declarations
ahejlsberg Jan 19, 2016
d3be38e
Updating error messages
ahejlsberg Jan 19, 2016
723475b
Accepting new baselines
ahejlsberg Jan 19, 2016
a499607
Merge branch 'master' into readonlyMembers
ahejlsberg Jan 19, 2016
dc8ab95
Chaning "read-write" to "writable" in error messages
ahejlsberg Jan 19, 2016
4abf717
Accepting new baselines
ahejlsberg Jan 19, 2016
cac3a32
Add ReadonlyArray<T> to core.d.ts
ahejlsberg Jan 19, 2016
8bb00fe
Consider ReadonlyArray<T> an array-like type
ahejlsberg Jan 19, 2016
fc6947b
Make get only accessor compatible with writable property in type rela…
ahejlsberg Jan 21, 2016
9562353
Fixing fourslash test
ahejlsberg Jan 21, 2016
b6f43e6
Accepting new baselines
ahejlsberg Jan 21, 2016
fac1bf6
Disallow assigments to exported variables from external modules
ahejlsberg Jan 22, 2016
e506378
Accepting new baselines
ahejlsberg Jan 22, 2016
ee0060b
No readonly checks in type relationships + No assignments through nam…
ahejlsberg Jan 23, 2016
7405a4b
Accepting new baselines
ahejlsberg Jan 23, 2016
49dd54e
Include readonly in type equality checks + Treat more symbols as read…
ahejlsberg Jan 24, 2016
ea4e3af
Fix tests
ahejlsberg Jan 24, 2016
c78ee72
Accepting new baselines
ahejlsberg Jan 24, 2016
c826a90
Merge branch 'master' into readonlyMembers
ahejlsberg Jan 24, 2016
7561642
Adding const/readonly to core.d.ts and es6.d.ts
ahejlsberg Jan 24, 2016
0b1f0d8
Accepting new baselines
ahejlsberg Jan 24, 2016
77d174a
Adding tests
ahejlsberg Jan 25, 2016
da107fe
Accepting new baselines
ahejlsberg Jan 25, 2016
cbb195b
Renumbering NodeFlags to start at 1 << 0
ahejlsberg Jan 27, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
479 changes: 267 additions & 212 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

24 changes: 16 additions & 8 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@
"category": "Error",
"code": 1023
},
"'readonly' modifier can only appear on a property declaration or index signature.": {
"category": "Error",
"code": 1024
},
"Accessibility modifier already seen.": {
"category": "Error",
"code": 1028
Expand Down Expand Up @@ -203,6 +207,14 @@
"category": "Error",
"code": 1068
},
"'{0}' modifier cannot appear on a type member.": {
"category": "Error",
"code": 1070
},
"'{0}' modifier cannot appear on an index signature.": {
"category": "Error",
"code": 1071
},
"A '{0}' modifier cannot be used with an import declaration.": {
"category": "Error",
"code": 1079
Expand Down Expand Up @@ -423,10 +435,6 @@
"category": "Error",
"code": 1144
},
"Modifiers not permitted on index signature members.": {
"category": "Error",
"code": 1145
},
"Declaration expected.": {
"category": "Error",
"code": 1146
Expand Down Expand Up @@ -1379,11 +1387,11 @@
"category": "Error",
"code": 2448
},
"The operand of an increment or decrement operator cannot be a constant.": {
"The operand of an increment or decrement operator cannot be a constant or a read-only property.": {
"category": "Error",
"code": 2449
},
"Left-hand side of assignment expression cannot be a constant.": {
"Left-hand side of assignment expression cannot be a constant or a read-only property.": {
"category": "Error",
"code": 2450
},
Expand Down Expand Up @@ -1515,11 +1523,11 @@
"category": "Error",
"code": 2484
},
"The left-hand side of a 'for...of' statement cannot be a previously defined constant.": {
"The left-hand side of a 'for...of' statement cannot be a constant or a read-only property.": {
"category": "Error",
"code": 2485
},
"The left-hand side of a 'for...in' statement cannot be a previously defined constant.": {
"The left-hand side of a 'for...in' statement cannot be a constant or a read-only property.": {
"category": "Error",
"code": 2486
},
Expand Down
118 changes: 44 additions & 74 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1172,7 +1172,7 @@ namespace ts {
case ParsingContext.SwitchClauses:
return token === SyntaxKind.CaseKeyword || token === SyntaxKind.DefaultKeyword;
case ParsingContext.TypeMembers:
return isStartOfTypeMember();
return lookAhead(isTypeMemberStart);
case ParsingContext.ClassMembers:
// We allow semicolons as class elements (as specified by ES6) as long as we're
// not in error recovery. If we're in error recovery, we don't want an errant
Expand Down Expand Up @@ -2214,13 +2214,13 @@ namespace ts {
return finishNode(node);
}

function parsePropertyOrMethodSignature(): PropertySignature | MethodSignature {
const fullStart = scanner.getStartPos();
function parsePropertyOrMethodSignature(fullStart: number, modifiers: ModifiersArray): PropertySignature | MethodSignature {
const name = parsePropertyName();
const questionToken = parseOptionalToken(SyntaxKind.QuestionToken);

if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) {
const method = <MethodSignature>createNode(SyntaxKind.MethodSignature, fullStart);
setModifiers(method, modifiers);
method.name = name;
method.questionToken = questionToken;

Expand All @@ -2232,6 +2232,7 @@ namespace ts {
}
else {
const property = <PropertySignature>createNode(SyntaxKind.PropertySignature, fullStart);
setModifiers(property, modifiers);
property.name = name;
property.questionToken = questionToken;
property.type = parseTypeAnnotation();
Expand All @@ -2248,86 +2249,51 @@ namespace ts {
}
}

function isStartOfTypeMember(): boolean {
switch (token) {
case SyntaxKind.OpenParenToken:
case SyntaxKind.LessThanToken:
case SyntaxKind.OpenBracketToken: // Both for indexers and computed properties
return true;
default:
if (isModifierKind(token)) {
const result = lookAhead(isStartOfIndexSignatureDeclaration);
if (result) {
return result;
}
}

return isLiteralPropertyName() && lookAhead(isTypeMemberWithLiteralPropertyName);
function isTypeMemberStart(): boolean {
let idToken: SyntaxKind;
// Return true if we have the start of a signature member
if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) {
return true;
}
}

function isStartOfIndexSignatureDeclaration() {
// Eat up all modifiers, but hold on to the last one in case it is actually an identifier
while (isModifierKind(token)) {
idToken = token;
nextToken();
}

return isIndexSignature();
}

function isTypeMemberWithLiteralPropertyName() {
nextToken();
return token === SyntaxKind.OpenParenToken ||
token === SyntaxKind.LessThanToken ||
token === SyntaxKind.QuestionToken ||
token === SyntaxKind.ColonToken ||
canParseSemicolon();
// Index signatures and computed property names are type members
if (token === SyntaxKind.OpenBracketToken) {
return true;
}
// Try to get the first property-like token following all modifiers
if (isLiteralPropertyName()) {
idToken = token;
nextToken();
}
// If we were able to get any potential identifier, check that it is
// the start of a member declaration
if (idToken) {
return token === SyntaxKind.OpenParenToken ||
token === SyntaxKind.LessThanToken ||
token === SyntaxKind.QuestionToken ||
token === SyntaxKind.ColonToken ||
canParseSemicolon();
}
return false;
}

function parseTypeMember(): TypeElement {
switch (token) {
case SyntaxKind.OpenParenToken:
case SyntaxKind.LessThanToken:
return parseSignatureMember(SyntaxKind.CallSignature);
case SyntaxKind.OpenBracketToken:
// Indexer or computed property
return isIndexSignature()
? parseIndexSignatureDeclaration(scanner.getStartPos(), /*decorators*/ undefined, /*modifiers*/ undefined)
: parsePropertyOrMethodSignature();
case SyntaxKind.NewKeyword:
if (lookAhead(isStartOfConstructSignature)) {
return parseSignatureMember(SyntaxKind.ConstructSignature);
}
// fall through.
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
return parsePropertyOrMethodSignature();
default:
// Index declaration as allowed as a type member. But as per the grammar,
// they also allow modifiers. So we have to check for an index declaration
// that might be following modifiers. This ensures that things work properly
// when incrementally parsing as the parser will produce the Index declaration
// if it has the same text regardless of whether it is inside a class or an
// object type.
if (isModifierKind(token)) {
const result = tryParse(parseIndexSignatureWithModifiers);
if (result) {
return result;
}
}

if (tokenIsIdentifierOrKeyword(token)) {
return parsePropertyOrMethodSignature();
}
if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) {
return parseSignatureMember(SyntaxKind.CallSignature);
}
}

function parseIndexSignatureWithModifiers() {
const fullStart = scanner.getStartPos();
const decorators = parseDecorators();
if (token === SyntaxKind.NewKeyword && lookAhead(isStartOfConstructSignature)) {
return parseSignatureMember(SyntaxKind.ConstructSignature);
}
const fullStart = getNodePos();
const modifiers = parseModifiers();
return isIndexSignature()
? parseIndexSignatureDeclaration(fullStart, decorators, modifiers)
: undefined;
if (isIndexSignature()) {
return parseIndexSignatureDeclaration(fullStart, /*decorators*/ undefined, modifiers);
}
return parsePropertyOrMethodSignature(fullStart, modifiers);
}

function isStartOfConstructSignature() {
Expand Down Expand Up @@ -4404,6 +4370,7 @@ namespace ts {
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.PublicKeyword:
case SyntaxKind.ReadonlyKeyword:
nextToken();
// ASI takes effect for this modifier.
if (scanner.hasPrecedingLineBreak()) {
Expand Down Expand Up @@ -4486,6 +4453,7 @@ namespace ts {
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.StaticKeyword:
case SyntaxKind.ReadonlyKeyword:
// When these don't start a declaration, they may be the start of a class member if an identifier
// immediately follows. Otherwise they're an identifier in an expression statement.
return isStartOfDeclaration() || !lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine);
Expand Down Expand Up @@ -4567,6 +4535,7 @@ namespace ts {
case SyntaxKind.PublicKeyword:
case SyntaxKind.AbstractKeyword:
case SyntaxKind.StaticKeyword:
case SyntaxKind.ReadonlyKeyword:
case SyntaxKind.GlobalKeyword:
if (isStartOfDeclaration()) {
return parseDeclaration();
Expand Down Expand Up @@ -4855,6 +4824,7 @@ namespace ts {
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.StaticKeyword:
case SyntaxKind.ReadonlyKeyword:
return true;
default:
return false;
Expand Down
1 change: 1 addition & 0 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,7 @@ namespace ts {
case SyntaxKind.PublicKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.ReadonlyKeyword:
case SyntaxKind.DeclareKeyword:
diagnostics.push(createDiagnosticForNode(modifier, Diagnostics._0_can_only_be_used_in_a_ts_file, tokenToString(modifier.kind)));
return true;
Expand Down
1 change: 1 addition & 0 deletions src/compiler/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ namespace ts {
"private": SyntaxKind.PrivateKeyword,
"protected": SyntaxKind.ProtectedKeyword,
"public": SyntaxKind.PublicKeyword,
"readonly": SyntaxKind.ReadonlyKeyword,
"require": SyntaxKind.RequireKeyword,
"global": SyntaxKind.GlobalKeyword,
"return": SyntaxKind.ReturnKeyword,
Expand Down
69 changes: 39 additions & 30 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ namespace ts {
IsKeyword,
ModuleKeyword,
NamespaceKeyword,
ReadonlyKeyword,
RequireKeyword,
NumberKeyword,
SetKeyword,
Expand Down Expand Up @@ -369,32 +370,33 @@ namespace ts {
}

export const enum NodeFlags {
None = 0,
Export = 1 << 1, // Declarations
Ambient = 1 << 2, // Declarations
Public = 1 << 3, // Property/Method
Private = 1 << 4, // Property/Method
Protected = 1 << 5, // Property/Method
Static = 1 << 6, // Property/Method
Abstract = 1 << 7, // Class/Method/ConstructSignature
Async = 1 << 8, // Property/Method/Function
Default = 1 << 9, // Function/Class (export default declaration)
MultiLine = 1 << 10, // Multi-line array or object literal
Synthetic = 1 << 11, // Synthetic node (for full fidelity)
DeclarationFile = 1 << 12, // Node is a .d.ts file
Let = 1 << 13, // Variable declaration
Const = 1 << 14, // Variable declaration
OctalLiteral = 1 << 15, // Octal numeric literal
Namespace = 1 << 16, // Namespace declaration
ExportContext = 1 << 17, // Export context (initialized by binding)
ContainsThis = 1 << 18, // Interface contains references to "this"
HasImplicitReturn = 1 << 19, // If function implicitly returns on one of codepaths (initialized by binding)
HasExplicitReturn = 1 << 20, // If function has explicit reachable return on one of codepaths (initialized by binding)
GlobalAugmentation = 1 << 21, // Set if module declaration is an augmentation for the global scope
HasClassExtends = 1 << 22, // If the file has a non-ambient class with an extends clause in ES5 or lower (initialized by binding)
HasDecorators = 1 << 23, // If the file has decorators (initialized by binding)
HasParamDecorators = 1 << 24, // If the file has parameter decorators (initialized by binding)
HasAsyncFunctions = 1 << 25, // If the file has async functions (initialized by binding)
None = 0,
Export = 1 << 0, // Declarations
Ambient = 1 << 1, // Declarations
Public = 1 << 2, // Property/Method
Private = 1 << 3, // Property/Method
Protected = 1 << 4, // Property/Method
Static = 1 << 5, // Property/Method
Readonly = 1 << 6, // Property/Method
Abstract = 1 << 7, // Class/Method/ConstructSignature
Async = 1 << 8, // Property/Method/Function
Default = 1 << 9, // Function/Class (export default declaration)
MultiLine = 1 << 10, // Multi-line array or object literal
Synthetic = 1 << 11, // Synthetic node (for full fidelity)
DeclarationFile = 1 << 12, // Node is a .d.ts file
Let = 1 << 13, // Variable declaration
Const = 1 << 14, // Variable declaration
OctalLiteral = 1 << 15, // Octal numeric literal
Namespace = 1 << 16, // Namespace declaration
ExportContext = 1 << 17, // Export context (initialized by binding)
ContainsThis = 1 << 18, // Interface contains references to "this"
HasImplicitReturn = 1 << 19, // If function implicitly returns on one of codepaths (initialized by binding)
HasExplicitReturn = 1 << 20, // If function has explicit reachable return on one of codepaths (initialized by binding)
GlobalAugmentation = 1 << 21, // Set if module declaration is an augmentation for the global scope
HasClassExtends = 1 << 22, // If the file has a non-ambient class with an extends clause in ES5 or lower (initialized by binding)
HasDecorators = 1 << 23, // If the file has decorators (initialized by binding)
HasParamDecorators = 1 << 24, // If the file has parameter decorators (initialized by binding)
HasAsyncFunctions = 1 << 25, // If the file has async functions (initialized by binding)

Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async,
AccessibilityModifier = Public | Private | Protected,
Expand Down Expand Up @@ -2074,6 +2076,7 @@ namespace ts {
resolvedAwaitedType?: Type; // Cached awaited type of type node
resolvedSignature?: Signature; // Cached signature of signature node or call expression
resolvedSymbol?: Symbol; // Cached name resolution result
resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result
flags?: NodeCheckFlags; // Set of flags specific to Node
enumMemberValue?: number; // Constant value of enum member
isVisible?: boolean; // Is this node visible
Expand Down Expand Up @@ -2181,8 +2184,8 @@ namespace ts {
declaredProperties: Symbol[]; // Declared members
declaredCallSignatures: Signature[]; // Declared call signatures
declaredConstructSignatures: Signature[]; // Declared construct signatures
declaredStringIndexType: Type; // Declared string index type
declaredNumberIndexType: Type; // Declared numeric index type
declaredStringIndexInfo: IndexInfo; // Declared string indexing info
declaredNumberIndexInfo: IndexInfo; // Declared numeric indexing info
}

// Type references (TypeFlags.Reference). When a class or interface has type parameters or
Expand Down Expand Up @@ -2234,8 +2237,8 @@ namespace ts {
properties: Symbol[]; // Properties
callSignatures: Signature[]; // Call signatures of type
constructSignatures: Signature[]; // Construct signatures of type
stringIndexType?: Type; // String index type
numberIndexType?: Type; // Numeric index type
stringIndexInfo?: IndexInfo; // String indexing info
numberIndexInfo?: IndexInfo; // Numeric indexing info
}

/* @internal */
Expand Down Expand Up @@ -2298,6 +2301,12 @@ namespace ts {
Number,
}

export interface IndexInfo {
type: Type;
isReadonly: boolean;
declaration?: SignatureDeclaration;
}

/* @internal */
export interface TypeMapper {
(t: TypeParameter): Type;
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,7 @@ namespace ts {
case SyntaxKind.PublicKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.ReadonlyKeyword:
case SyntaxKind.StaticKeyword:
return true;
}
Expand Down Expand Up @@ -2313,6 +2314,7 @@ namespace ts {
case SyntaxKind.ConstKeyword: return NodeFlags.Const;
case SyntaxKind.DefaultKeyword: return NodeFlags.Default;
case SyntaxKind.AsyncKeyword: return NodeFlags.Async;
case SyntaxKind.ReadonlyKeyword: return NodeFlags.Readonly;
}
return 0;
}
Expand Down
Loading