Simple case and scoping

This commit is contained in:
zhengbli 2016-03-28 12:37:37 -07:00 committed by Zhengbo Li
parent 89350b35f0
commit df91ef0b38
5 changed files with 130 additions and 27 deletions

View File

@ -404,6 +404,13 @@ namespace ts {
// This node will now be set as the parent of all of its children as we recurse into them.
parent = node;
// Binding of JsDocComment should be done before the current block scope container changes.
// because the scope of JsDocComment should not be affected by whether the current node is a
// container or not.
if (isInJavaScriptFile(node) && node.jsDocComment) {
bind(node.jsDocComment);
}
// Depending on what kind of node this is, we may have to adjust the current container
// and block-container. If the current node is a container, then it is automatically
// considered the current block-container as well. Also, for containers that we know
@ -468,10 +475,6 @@ namespace ts {
labelStack = labelIndexMap = implicitLabels = undefined;
}
if (isInJavaScriptFile(node) && node.jsDocComment) {
bind(node.jsDocComment);
}
bindReachableStatement(node);
if (currentReachabilityState === Reachability.Reachable && isFunctionLikeKind(kind) && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
@ -857,7 +860,7 @@ namespace ts {
// their container in the tree. To accomplish this, we simply add their declared
// symbol to the 'locals' of the container. These symbols can then be found as
// the type checker walks up the containers, checking them for matching names.
return declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes);
return declareSymbol(container.locals, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
}
}
@ -1343,6 +1346,7 @@ namespace ts {
return bindClassLikeDeclaration(<ClassLikeDeclaration>node);
case SyntaxKind.InterfaceDeclaration:
return bindBlockScopedDeclaration(<Declaration>node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes);
case SyntaxKind.JSDocTypedefTag:
case SyntaxKind.TypeAliasDeclaration:
return bindBlockScopedDeclaration(<Declaration>node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
case SyntaxKind.EnumDeclaration:

View File

@ -3403,7 +3403,7 @@ namespace ts {
return <InterfaceType>links.declaredType;
}
function getDeclaredTypeOfTypeAlias(symbol: Symbol): Type {
function getDeclaredTypeOfTypeAlias(symbol: Symbol, node?: Node): Type {
const links = getSymbolLinks(symbol);
if (!links.declaredType) {
// Note that we use the links object as the target here because the symbol object is used as the unique
@ -3411,8 +3411,18 @@ namespace ts {
if (!pushTypeResolution(symbol, TypeSystemPropertyName.DeclaredType)) {
return unknownType;
}
const declaration = <TypeAliasDeclaration>getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
let type = getTypeFromTypeNode(declaration.type);
let typeNode: TypeNode;
let name: Identifier;
if (node && (node.flags & NodeFlags.JavaScriptFile)) {
const declaration = <JSDocTypedefTag>getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag);
typeNode = declaration.typeExpression.type;
}
if (!typeNode) {
const declaration = <TypeAliasDeclaration>getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
typeNode = declaration.type;
}
let type = getTypeFromTypeNode(typeNode);
if (popTypeResolution()) {
links.typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
if (links.typeParameters) {
@ -3424,7 +3434,7 @@ namespace ts {
}
else {
type = unknownType;
error(declaration.name, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol));
error(name, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol));
}
links.declaredType = type;
}
@ -3462,13 +3472,13 @@ namespace ts {
return links.declaredType;
}
function getDeclaredTypeOfSymbol(symbol: Symbol): Type {
function getDeclaredTypeOfSymbol(symbol: Symbol, node?: Node): Type {
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0);
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
return getDeclaredTypeOfClassOrInterface(symbol);
}
if (symbol.flags & SymbolFlags.TypeAlias) {
return getDeclaredTypeOfTypeAlias(symbol);
return getDeclaredTypeOfTypeAlias(symbol, node);
}
if (symbol.flags & SymbolFlags.Enum) {
return getDeclaredTypeOfEnum(symbol);
@ -4564,7 +4574,7 @@ namespace ts {
// references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the
// declared type. Instantiations are cached using the type identities of the type arguments as the key.
function getTypeFromTypeAliasReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol): Type {
const type = getDeclaredTypeOfSymbol(symbol);
const type = getDeclaredTypeOfSymbol(symbol, node);
const links = getSymbolLinks(symbol);
const typeParameters = links.typeParameters;
if (typeParameters) {

View File

@ -811,6 +811,10 @@
"category": "Error",
"code": 1249
},
"'{0}' tag cannot be used independently as a top level JSDoc tag.": {
"category": "Error",
"code": 1250
},
"'with' statements are not allowed in an async function block.": {
"category": "Error",
"code": 1300

View File

@ -401,6 +401,8 @@ namespace ts {
return visitNode(cbNode, (<JSDocTypeTag>node).typeExpression);
case SyntaxKind.JSDocTemplateTag:
return visitNodes(cbNodes, (<JSDocTemplateTag>node).typeParameters);
case SyntaxKind.JSDocTypedefTag:
return visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression);
}
}
@ -886,7 +888,7 @@ namespace ts {
/** Invokes the provided callback then unconditionally restores the parser to the state it
* was in immediately prior to invoking the callback. The result of invoking the callback
* is returned from this function.
* is returned from this function.
*/
function lookAhead<T>(callback: () => T): T {
return speculationHelper(callback, /*isLookAhead*/ true);
@ -2068,11 +2070,11 @@ namespace ts {
}
function fillSignature(
returnToken: SyntaxKind,
yieldContext: boolean,
awaitContext: boolean,
requireCompleteParameterList: boolean,
signature: SignatureDeclaration): void {
returnToken: SyntaxKind,
yieldContext: boolean,
awaitContext: boolean,
requireCompleteParameterList: boolean,
signature: SignatureDeclaration): void {
const returnTokenRequired = returnToken === SyntaxKind.EqualsGreaterThanToken;
signature.typeParameters = parseTypeParameters();
@ -3341,8 +3343,8 @@ namespace ts {
if (sourceFile.languageVariant !== LanguageVariant.JSX) {
return false;
}
// We are in JSX context and the token is part of JSXElement.
// Fall through
// We are in JSX context and the token is part of JSXElement.
// Fall through
default:
return true;
}
@ -4044,9 +4046,9 @@ namespace ts {
const isAsync = !!(node.flags & NodeFlags.Async);
node.name =
isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) :
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
isAsync ? doInAwaitContext(parseOptionalIdentifier) :
parseOptionalIdentifier();
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
isAsync ? doInAwaitContext(parseOptionalIdentifier) :
parseOptionalIdentifier();
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false);
@ -4984,7 +4986,7 @@ namespace ts {
if (token === SyntaxKind.ConstKeyword && permitInvalidConstAsModifier) {
// We need to ensure that any subsequent modifiers appear on the same line
// so that when 'const' is a standalone declaration, we don't issue an error.
// so that when 'const' is a standalone declaration, we don't issue an error.
if (!tryParse(nextTokenIsOnSameLineAndCanFollowModifier)) {
break;
}
@ -5247,7 +5249,7 @@ namespace ts {
node.decorators = decorators;
setModifiers(node, modifiers);
if (token === SyntaxKind.GlobalKeyword) {
// parse 'global' as name of global scope augmentation
// parse 'global' as name of global scope augmentation
node.name = parseIdentifier();
node.flags |= NodeFlags.GlobalAugmentation;
}
@ -5829,7 +5831,7 @@ namespace ts {
}
function checkForEmptyTypeArgumentList(typeArguments: NodeArray<Node>) {
if (parseDiagnostics.length === 0 && typeArguments && typeArguments.length === 0) {
if (parseDiagnostics.length === 0 && typeArguments && typeArguments.length === 0) {
const start = typeArguments.pos - "<".length;
const end = skipTrivia(sourceText, typeArguments.end) + ">".length;
return parseErrorAtPosition(start, end - start, Diagnostics.Type_argument_list_cannot_be_empty);
@ -5990,6 +5992,7 @@ namespace ts {
Debug.assert(end <= content.length);
let tags: NodeArray<JSDocTag>;
let currentParentJSDocDeclaration: Declaration;
let result: JSDocComment;
@ -6097,6 +6100,11 @@ namespace ts {
return handleTemplateTag(atToken, tagName);
case "type":
return handleTypeTag(atToken, tagName);
case "typedef":
return handleTypedefTag(atToken, tagName);
case "property":
case "prop":
return handlePropertyTag(atToken, tagName);
}
}
@ -6204,6 +6212,56 @@ namespace ts {
return finishNode(result);
}
function handlePropertyTag(atToken: Node, tagName: Identifier): JSDocPropertyTag {
if (!currentParentJSDocDeclaration) {
parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_cannot_be_used_independently_as_a_top_level_JSDoc_tag, tagName.text);
return undefined;
}
const typeExpression = tryParseTypeExpression();
skipWhitespace();
const name = parseJSDocIdentifier();
if (!name) {
parseErrorAtPosition(scanner.getStartPos(), /*length*/ 0, Diagnostics.Identifier_expected);
return undefined;
}
const result = <JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
result.atToken = atToken;
result.tagName = tagName;
result.name = name;
result.typeExpression = typeExpression;
return finishNode(result);
}
function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag {
const typeExpression = tryParseTypeExpression();
skipWhitespace();
const name = parseJSDocIdentifier();
if (!name) {
parseErrorAtPosition(scanner.getStartPos(), 0, Diagnostics.Identifier_expected);
return undefined;
}
const result = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag, atToken.pos);
result.atToken = atToken;
result.tagName = tagName;
result.name = name;
result.typeExpression = typeExpression;
// if (typeExpression && typeExpression.type.kind === SyntaxKind.JSDocTypeReference) {
// const jsDocTypeReference = <JSDocTypeReference>typeExpression.type;
// if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) {
// const name = <Identifier>jsDocTypeReference.name;
// if (name.text === "Object") {
// currentParentJSDocDeclaration = declaration;
// }
// }
// }
return result;
}
function handleTemplateTag(atToken: Node, tagName: Identifier): JSDocTemplateTag {
if (forEach(tags, t => t.kind === SyntaxKind.JSDocTemplateTag)) {
parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_already_specified, tagName.text);

View File

@ -342,6 +342,10 @@ namespace ts {
JSDocReturnTag,
JSDocTypeTag,
JSDocTemplateTag,
JSDocTypedefTag,
JSDocPropertyTag,
JSDocTypedefDeclaration,
JSDocTypeLiteral,
// Synthesized list
SyntaxList,
@ -1510,6 +1514,29 @@ namespace ts {
typeExpression: JSDocTypeExpression;
}
// @kind(SyntaxKind.JSDocTypedefTag)
export interface JSDocTypedefTag extends JSDocTag, Declaration {
name: Identifier;
typeExpression: JSDocTypeExpression;
}
// @kind(SyntaxKind.JSDocPropertyTag)
export interface JSDocPropertyTag extends JSDocTag, TypeElement {
name: Identifier;
typeExpression: JSDocTypeExpression;
}
// @kind(SyntaxKind.JSDocTypedefDeclaration)
export interface JSDocTypedefDeclaration extends Declaration {
name: Identifier;
type: TypeNode;
}
// @kind(SyntaxKind.JSDocTypeLiteral)
export interface JSDocTypeLiteral extends TypeNode {
members: NodeArray<TypeElement>;
}
// @kind(SyntaxKind.JSDocParameterTag)
export interface JSDocParameterTag extends JSDocTag {
preParameterName?: Identifier;
@ -2094,7 +2121,7 @@ namespace ts {
jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with
resolvedJsxType?: Type; // resolved element attributes type of a JSX openinglike element
hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt.
superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing
superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing
}
export const enum TypeFlags {