mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-20 19:45:07 -06:00
Bind a jsdoc enum as SymbolFlags.TypeAlias and not SymbolFlags.Enum (#32520)
* Bind a jsdoc enum as SymbolFlags.TypeAlias and not SymbolFlags.Enum * Actually include an @enum tag as a declaration * Add enum tag refs into a couple more syntax kind lists * accept symbol baseline update
This commit is contained in:
parent
2fe3c1b3b7
commit
2a4930f4ec
@ -120,7 +120,7 @@ namespace ts {
|
||||
let thisParentContainer: Node; // Container one level up
|
||||
let blockScopeContainer: Node;
|
||||
let lastContainer: Node;
|
||||
let delayedTypeAliases: (JSDocTypedefTag | JSDocCallbackTag)[];
|
||||
let delayedTypeAliases: (JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag)[];
|
||||
let seenThisKeyword: boolean;
|
||||
|
||||
// state used by control flow analysis
|
||||
@ -732,7 +732,8 @@ namespace ts {
|
||||
break;
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
bindJSDocTypeAlias(node as JSDocTypedefTag | JSDocCallbackTag);
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
bindJSDocTypeAlias(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag);
|
||||
break;
|
||||
// In source files and blocks, bind functions first to match hoisting that occurs at runtime
|
||||
case SyntaxKind.SourceFile: {
|
||||
@ -1436,9 +1437,9 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag) {
|
||||
function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag) {
|
||||
node.tagName.parent = node;
|
||||
if (node.fullName) {
|
||||
if (node.kind !== SyntaxKind.JSDocEnumTag && node.fullName) {
|
||||
setParentPointers(node, node.fullName);
|
||||
}
|
||||
}
|
||||
@ -1805,7 +1806,7 @@ namespace ts {
|
||||
currentFlow = { flags: FlowFlags.Start };
|
||||
parent = typeAlias;
|
||||
bind(typeAlias.typeExpression);
|
||||
if (!typeAlias.fullName || typeAlias.fullName.kind === SyntaxKind.Identifier) {
|
||||
if (isJSDocEnumTag(typeAlias) || !typeAlias.fullName || typeAlias.fullName.kind === SyntaxKind.Identifier) {
|
||||
parent = typeAlias.parent;
|
||||
bindBlockScopedDeclaration(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
|
||||
}
|
||||
@ -2319,7 +2320,8 @@ namespace ts {
|
||||
return declareSymbolAndAddToSymbolTable(propTag, flags, SymbolFlags.PropertyExcludes);
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
return (delayedTypeAliases || (delayedTypeAliases = [])).push(node as JSDocTypedefTag | JSDocCallbackTag);
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
return (delayedTypeAliases || (delayedTypeAliases = [])).push(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2766,11 +2768,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (!isBindingPattern(node.name)) {
|
||||
const isEnum = isInJSFile(node) && !!getJSDocEnumTag(node);
|
||||
const enumFlags = (isEnum ? SymbolFlags.RegularEnum : SymbolFlags.None);
|
||||
const enumExcludes = (isEnum ? SymbolFlags.RegularEnumExcludes : SymbolFlags.None);
|
||||
if (isBlockOrCatchScoped(node)) {
|
||||
bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable | enumFlags, SymbolFlags.BlockScopedVariableExcludes | enumExcludes);
|
||||
bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes);
|
||||
}
|
||||
else if (isParameterDeclaration(node)) {
|
||||
// It is safe to walk up parent chain to find whether the node is a destructuring parameter declaration
|
||||
@ -2785,7 +2784,7 @@ namespace ts {
|
||||
declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes);
|
||||
}
|
||||
else {
|
||||
declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable | enumFlags, SymbolFlags.FunctionScopedVariableExcludes | enumExcludes);
|
||||
declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1680,6 +1680,7 @@ namespace ts {
|
||||
break;
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
// js type aliases do not resolve names from their host, so skip past it
|
||||
location = getJSDocHost(location);
|
||||
break;
|
||||
@ -2025,7 +2026,7 @@ namespace ts {
|
||||
// Block-scoped variables cannot be used before their definition
|
||||
const declaration = find(
|
||||
result.declarations,
|
||||
d => isBlockOrCatchScoped(d) || isClassLike(d) || (d.kind === SyntaxKind.EnumDeclaration) || isInJSFile(d) && !!getJSDocEnumTag(d));
|
||||
d => isBlockOrCatchScoped(d) || isClassLike(d) || (d.kind === SyntaxKind.EnumDeclaration));
|
||||
|
||||
if (declaration === undefined) return Debug.fail("Declaration to checkResolvedBlockScopedVariable is undefined");
|
||||
|
||||
@ -4851,6 +4852,7 @@ namespace ts {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
// Top-level jsdoc type aliases are considered exported
|
||||
// First parent is comment node, second is hosting declaration or token; we only care about those tokens or declarations whose parent is a source file
|
||||
return !!(node.parent && node.parent.parent && node.parent.parent.parent && isSourceFile(node.parent.parent.parent));
|
||||
@ -6156,6 +6158,7 @@ namespace ts {
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
case SyntaxKind.MappedType:
|
||||
case SyntaxKind.ConditionalType:
|
||||
@ -6489,8 +6492,10 @@ namespace ts {
|
||||
return errorType;
|
||||
}
|
||||
|
||||
const declaration = <JSDocTypedefTag | JSDocCallbackTag | TypeAliasDeclaration>find(symbol.declarations, d =>
|
||||
isJSDocTypeAlias(d) || d.kind === SyntaxKind.TypeAliasDeclaration);
|
||||
const declaration = find(symbol.declarations, isTypeAlias);
|
||||
if (!declaration) {
|
||||
return Debug.fail("Type alias symbol with no valid declaration found");
|
||||
}
|
||||
const typeNode = isJSDocTypeAlias(declaration) ? declaration.typeExpression : declaration.type;
|
||||
// If typeNode is missing, we will error in checkJSDocTypedefTag.
|
||||
let type = typeNode ? getTypeFromTypeNode(typeNode) : errorType;
|
||||
@ -6507,7 +6512,7 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
type = errorType;
|
||||
error(declaration.name, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol));
|
||||
error(isJSDocEnumTag(declaration) ? declaration : declaration.name || declaration, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol));
|
||||
}
|
||||
links.declaredType = type;
|
||||
}
|
||||
@ -9159,21 +9164,6 @@ namespace ts {
|
||||
return type;
|
||||
}
|
||||
|
||||
// JS enums are 'string' or 'number', not an enum type.
|
||||
const enumTag = isInJSFile(node) && symbol.valueDeclaration && getJSDocEnumTag(symbol.valueDeclaration);
|
||||
if (enumTag) {
|
||||
const links = getNodeLinks(enumTag);
|
||||
if (!pushTypeResolution(enumTag, TypeSystemPropertyName.EnumTagType)) {
|
||||
return errorType;
|
||||
}
|
||||
let type = enumTag.typeExpression ? getTypeFromTypeNode(enumTag.typeExpression) : errorType;
|
||||
if (!popTypeResolution()) {
|
||||
type = errorType;
|
||||
error(node, Diagnostics.Enum_type_0_circularly_references_itself, symbolToString(symbol));
|
||||
}
|
||||
return (links.resolvedEnumType = type);
|
||||
}
|
||||
|
||||
// Get type from reference to named type that cannot be generic (enum or type parameter)
|
||||
const res = tryGetDeclaredTypeOfSymbol(symbol);
|
||||
if (res) {
|
||||
@ -26409,6 +26399,7 @@ namespace ts {
|
||||
// A jsdoc typedef and callback are, by definition, type aliases
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
return DeclarationSpaces.ExportType;
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
return isAmbientModule(d as ModuleDeclaration) || getModuleInstanceState(d as ModuleDeclaration) !== ModuleInstanceState.NonInstantiated
|
||||
@ -30325,6 +30316,7 @@ namespace ts {
|
||||
return checkJSDocAugmentsTag(node as JSDocAugmentsTag);
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
return checkJSDocTypeAliasTag(node as JSDocTypedefTag);
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
return checkJSDocTemplateTag(node as JSDocTemplateTag);
|
||||
|
||||
@ -2466,7 +2466,8 @@ namespace ts {
|
||||
kind: SyntaxKind.JSDocClassTag;
|
||||
}
|
||||
|
||||
export interface JSDocEnumTag extends JSDocTag {
|
||||
export interface JSDocEnumTag extends JSDocTag, Declaration {
|
||||
parent: JSDoc;
|
||||
kind: SyntaxKind.JSDocEnumTag;
|
||||
typeExpression?: JSDocTypeExpression;
|
||||
}
|
||||
|
||||
@ -2147,11 +2147,11 @@ namespace ts {
|
||||
return !!name && name.escapedText === "new";
|
||||
}
|
||||
|
||||
export function isJSDocTypeAlias(node: Node): node is JSDocTypedefTag | JSDocCallbackTag {
|
||||
return node.kind === SyntaxKind.JSDocTypedefTag || node.kind === SyntaxKind.JSDocCallbackTag;
|
||||
export function isJSDocTypeAlias(node: Node): node is JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag {
|
||||
return node.kind === SyntaxKind.JSDocTypedefTag || node.kind === SyntaxKind.JSDocCallbackTag || node.kind === SyntaxKind.JSDocEnumTag;
|
||||
}
|
||||
|
||||
export function isTypeAlias(node: Node): node is JSDocTypedefTag | JSDocCallbackTag | TypeAliasDeclaration {
|
||||
export function isTypeAlias(node: Node): node is JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag | TypeAliasDeclaration {
|
||||
return isJSDocTypeAlias(node) || isTypeAliasDeclaration(node);
|
||||
}
|
||||
|
||||
@ -5091,7 +5091,7 @@ namespace ts {
|
||||
* attempt to draw the name from the node the declaration is on (as that declaration is what its' symbol
|
||||
* will be merged with)
|
||||
*/
|
||||
function nameForNamelessJSDocTypedef(declaration: JSDocTypedefTag): Identifier | undefined {
|
||||
function nameForNamelessJSDocTypedef(declaration: JSDocTypedefTag | JSDocEnumTag): Identifier | undefined {
|
||||
const hostNode = declaration.parent.parent;
|
||||
if (!hostNode) {
|
||||
return undefined;
|
||||
@ -5177,6 +5177,8 @@ namespace ts {
|
||||
}
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
return getNameOfJSDocTypedef(declaration as JSDocTypedefTag);
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
return nameForNamelessJSDocTypedef(declaration as JSDocEnumTag);
|
||||
case SyntaxKind.ExportAssignment: {
|
||||
const { expression } = declaration as ExportAssignment;
|
||||
return isIdentifier(expression) ? expression : undefined;
|
||||
|
||||
@ -97,7 +97,7 @@ class TypeWriterWalker {
|
||||
if (!isSymbolWalk) {
|
||||
// Don't try to get the type of something that's already a type.
|
||||
// Exception for `T` in `type T = something` because that may evaluate to some interesting type.
|
||||
if (ts.isPartOfTypeNode(node) || ts.isIdentifier(node) && !(ts.getMeaningFromDeclaration(node.parent) & ts.SemanticMeaning.Value) && !(ts.isTypeAlias(node.parent) && node.parent.name === node)) {
|
||||
if (ts.isPartOfTypeNode(node) || ts.isIdentifier(node) && !(ts.getMeaningFromDeclaration(node.parent) & ts.SemanticMeaning.Value) && !(ts.isTypeAliasDeclaration(node.parent) && node.parent.name === node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@ -1589,7 +1589,8 @@ declare namespace ts {
|
||||
interface JSDocClassTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocClassTag;
|
||||
}
|
||||
interface JSDocEnumTag extends JSDocTag {
|
||||
interface JSDocEnumTag extends JSDocTag, Declaration {
|
||||
parent: JSDoc;
|
||||
kind: SyntaxKind.JSDocEnumTag;
|
||||
typeExpression?: JSDocTypeExpression;
|
||||
}
|
||||
|
||||
@ -1589,7 +1589,8 @@ declare namespace ts {
|
||||
interface JSDocClassTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocClassTag;
|
||||
}
|
||||
interface JSDocEnumTag extends JSDocTag {
|
||||
interface JSDocEnumTag extends JSDocTag, Declaration {
|
||||
parent: JSDoc;
|
||||
kind: SyntaxKind.JSDocEnumTag;
|
||||
typeExpression?: JSDocTypeExpression;
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
=== tests/cases/conformance/jsdoc/a.js ===
|
||||
/** @enum {string} */
|
||||
const Target = {
|
||||
>Target : Symbol(Target, Decl(a.js, 1, 5))
|
||||
>Target : Symbol(Target, Decl(a.js, 1, 5), Decl(a.js, 0, 4))
|
||||
|
||||
START: "start",
|
||||
>START : Symbol(START, Decl(a.js, 1, 16))
|
||||
@ -21,7 +21,7 @@ const Target = {
|
||||
}
|
||||
/** @enum number */
|
||||
const Second = {
|
||||
>Second : Symbol(Second, Decl(a.js, 10, 5))
|
||||
>Second : Symbol(Second, Decl(a.js, 10, 5), Decl(a.js, 9, 4))
|
||||
|
||||
MISTAKE: "end",
|
||||
>MISTAKE : Symbol(MISTAKE, Decl(a.js, 10, 16))
|
||||
@ -35,7 +35,7 @@ const Second = {
|
||||
}
|
||||
/** @enum {function(number): number} */
|
||||
const Fs = {
|
||||
>Fs : Symbol(Fs, Decl(a.js, 17, 5))
|
||||
>Fs : Symbol(Fs, Decl(a.js, 17, 5), Decl(a.js, 16, 4))
|
||||
|
||||
ADD1: n => n + 1,
|
||||
>ADD1 : Symbol(ADD1, Decl(a.js, 17, 12))
|
||||
@ -82,17 +82,17 @@ function consume(t,s,f) {
|
||||
var v = Target.START
|
||||
>v : Symbol(v, Decl(a.js, 35, 7))
|
||||
>Target.START : Symbol(START, Decl(a.js, 1, 16))
|
||||
>Target : Symbol(Target, Decl(a.js, 1, 5))
|
||||
>Target : Symbol(Target, Decl(a.js, 1, 5), Decl(a.js, 0, 4))
|
||||
>START : Symbol(START, Decl(a.js, 1, 16))
|
||||
|
||||
v = Target.UNKNOWN // error, can't find 'UNKNOWN'
|
||||
>v : Symbol(v, Decl(a.js, 35, 7))
|
||||
>Target : Symbol(Target, Decl(a.js, 1, 5))
|
||||
>Target : Symbol(Target, Decl(a.js, 1, 5), Decl(a.js, 0, 4))
|
||||
|
||||
v = Second.MISTAKE // meh..ok, I guess?
|
||||
>v : Symbol(v, Decl(a.js, 35, 7))
|
||||
>Second.MISTAKE : Symbol(MISTAKE, Decl(a.js, 10, 16))
|
||||
>Second : Symbol(Second, Decl(a.js, 10, 5))
|
||||
>Second : Symbol(Second, Decl(a.js, 10, 5), Decl(a.js, 9, 4))
|
||||
>MISTAKE : Symbol(MISTAKE, Decl(a.js, 10, 16))
|
||||
|
||||
v = 'something else' // allowed, like Typescript's classic enums and unlike its string enums
|
||||
@ -105,14 +105,14 @@ function ff(s) {
|
||||
|
||||
// element access with arbitrary string is an error only with noImplicitAny
|
||||
if (!Target[s]) {
|
||||
>Target : Symbol(Target, Decl(a.js, 1, 5))
|
||||
>Target : Symbol(Target, Decl(a.js, 1, 5), Decl(a.js, 0, 4))
|
||||
>s : Symbol(s, Decl(a.js, 41, 12))
|
||||
|
||||
return null
|
||||
}
|
||||
else {
|
||||
return Target[s]
|
||||
>Target : Symbol(Target, Decl(a.js, 1, 5))
|
||||
>Target : Symbol(Target, Decl(a.js, 1, 5), Decl(a.js, 0, 4))
|
||||
>s : Symbol(s, Decl(a.js, 41, 12))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
tests/cases/conformance/jsdoc/bug27142.js(1,12): error TS2586: Enum type 'E' circularly references itself.
|
||||
tests/cases/conformance/jsdoc/bug27142.js(1,5): error TS2456: Type alias 'E' circularly references itself.
|
||||
|
||||
|
||||
==== tests/cases/conformance/jsdoc/bug27142.js (1 errors) ====
|
||||
/** @enum {E} */
|
||||
~
|
||||
!!! error TS2586: Enum type 'E' circularly references itself.
|
||||
~~~~~~~~~
|
||||
!!! error TS2456: Type alias 'E' circularly references itself.
|
||||
const E = { x: 0 };
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
=== tests/cases/conformance/jsdoc/bug27142.js ===
|
||||
/** @enum {E} */
|
||||
const E = { x: 0 };
|
||||
>E : Symbol(E, Decl(bug27142.js, 1, 5))
|
||||
>E : Symbol(E, Decl(bug27142.js, 1, 5), Decl(bug27142.js, 0, 4))
|
||||
>x : Symbol(x, Decl(bug27142.js, 1, 11))
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ const tist = TestEnum.ADD
|
||||
=== tests/cases/conformance/jsdoc/mod1.js ===
|
||||
/** @enum {string} */
|
||||
export const TestEnum = {
|
||||
>TestEnum : Symbol(TestEnum, Decl(mod1.js, 1, 12))
|
||||
>TestEnum : Symbol(TestEnum, Decl(mod1.js, 1, 12), Decl(mod1.js, 0, 4))
|
||||
|
||||
ADD: 'add',
|
||||
>ADD : Symbol(ADD, Decl(mod1.js, 1, 25))
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
* @enum {number}
|
||||
*/
|
||||
var foo = { };
|
||||
>foo : Symbol(foo, Decl(bug27134.js, 3, 3))
|
||||
>foo : Symbol(foo, Decl(bug27134.js, 3, 3), Decl(bug27134.js, 1, 3))
|
||||
|
||||
/**
|
||||
* @type {foo}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
* @enum {number}
|
||||
*/
|
||||
var foo = { };
|
||||
>foo : typeof foo
|
||||
>foo : {}
|
||||
>{ } : {}
|
||||
|
||||
/**
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
////const e = [|E|].A;
|
||||
|
||||
verify.singleReferenceGroup(
|
||||
`enum E
|
||||
`type E = string
|
||||
const E: {
|
||||
A: string;
|
||||
}`, "E");
|
||||
|
||||
@ -18,11 +18,10 @@
|
||||
verify.noErrors();
|
||||
|
||||
verify.quickInfoAt("type",
|
||||
`enum E`,
|
||||
`type E = number`,
|
||||
"Doc");
|
||||
verify.quickInfoAt("value",
|
||||
`enum E
|
||||
const E: {
|
||||
`const E: {
|
||||
A: number;
|
||||
}`,
|
||||
"Doc");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user