Fix @implements emit for namespaced base types

Fixes #38640
This commit is contained in:
Nathan Shively-Sanders 2020-05-20 10:29:39 -07:00
parent 7ec467e270
commit c5f66716cf
9 changed files with 192 additions and 11 deletions

View File

@ -3855,17 +3855,17 @@ namespace ts {
return false;
}
function isTypeSymbolAccessible(typeSymbol: Symbol, enclosingDeclaration: Node | undefined): boolean {
const access = isSymbolAccessible(typeSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false);
function isTypeSymbolAccessible(typeSymbol: Symbol, enclosingDeclaration: Node | undefined, allowModules: boolean): boolean {
const access = isSymbolAccessibleWorker(typeSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false, allowModules);
return access.accessibility === SymbolAccessibility.Accessible;
}
function isValueSymbolAccessible(typeSymbol: Symbol, enclosingDeclaration: Node | undefined): boolean {
const access = isSymbolAccessible(typeSymbol, enclosingDeclaration, SymbolFlags.Value, /*shouldComputeAliasesToMakeVisible*/ false);
const access = isSymbolAccessibleWorker(typeSymbol, enclosingDeclaration, SymbolFlags.Value, /*shouldComputeAliasesToMakeVisible*/ false, /*allowModules*/ true);
return access.accessibility === SymbolAccessibility.Accessible;
}
function isAnySymbolAccessible(symbols: Symbol[] | undefined, enclosingDeclaration: Node | undefined, initialSymbol: Symbol, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean): SymbolAccessibilityResult | undefined {
function isAnySymbolAccessible(symbols: Symbol[] | undefined, enclosingDeclaration: Node | undefined, initialSymbol: Symbol, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean, allowModules: boolean): SymbolAccessibilityResult | undefined {
if (!length(symbols)) return;
let hadAccessibleChain: Symbol | undefined;
@ -3880,7 +3880,7 @@ namespace ts {
return hasAccessibleDeclarations;
}
}
else {
else if (allowModules) {
if (some(symbol.declarations, hasNonGlobalAugmentationExternalModuleSymbol)) {
if (shouldComputeAliasesToMakeVisible) {
earlyModuleBail = true;
@ -3920,7 +3920,7 @@ namespace ts {
containers = [getSymbolOfNode(firstDecl.parent)];
}
}
const parentResult = isAnySymbolAccessible(containers, enclosingDeclaration, initialSymbol, initialSymbol === symbol ? getQualifiedLeftMeaning(meaning) : meaning, shouldComputeAliasesToMakeVisible);
const parentResult = isAnySymbolAccessible(containers, enclosingDeclaration, initialSymbol, initialSymbol === symbol ? getQualifiedLeftMeaning(meaning) : meaning, shouldComputeAliasesToMakeVisible, allowModules);
if (parentResult) {
return parentResult;
}
@ -3950,8 +3950,12 @@ namespace ts {
* @param shouldComputeAliasToMakeVisible a boolean value to indicate whether to return aliases to be mark visible in case the symbol is accessible
*/
function isSymbolAccessible(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean): SymbolAccessibilityResult {
return isSymbolAccessibleWorker(symbol, enclosingDeclaration, meaning, shouldComputeAliasesToMakeVisible, true);
}
function isSymbolAccessibleWorker(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean, allowModules: boolean): SymbolAccessibilityResult {
if (symbol && enclosingDeclaration) {
const result = isAnySymbolAccessible([symbol], enclosingDeclaration, symbol, meaning, shouldComputeAliasesToMakeVisible);
const result = isAnySymbolAccessible([symbol], enclosingDeclaration, symbol, meaning, shouldComputeAliasesToMakeVisible, allowModules);
if (result) {
return result;
}
@ -4331,7 +4335,7 @@ namespace ts {
return createThis();
}
if (!inTypeAlias && type.aliasSymbol && (context.flags & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope || isTypeSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration))) {
if (!inTypeAlias && type.aliasSymbol && (context.flags & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope || isTypeSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration, /*allowModules*/ true))) {
const typeArgumentNodes = mapToTypeNodes(type.aliasTypeArguments, context);
if (isReservedMemberName(type.aliasSymbol.escapedName) && !(type.aliasSymbol.flags & SymbolFlags.Class)) return createTypeReferenceNode(createIdentifier(""), typeArgumentNodes);
return symbolToTypeNode(type.aliasSymbol, context, SymbolFlags.Type, typeArgumentNodes);
@ -4350,7 +4354,7 @@ namespace ts {
}
if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams &&
type.flags & TypeFlags.TypeParameter &&
!isTypeSymbolAccessible(type.symbol, context.enclosingDeclaration)) {
!isTypeSymbolAccessible(type.symbol, context.enclosingDeclaration, /*allowModules*/ true)) {
const name = typeParameterToName(type, context);
context.approximateLength += idText(name).length;
return createTypeReferenceNode(createIdentifier(idText(name)), /*typeArguments*/ undefined);
@ -6325,7 +6329,7 @@ namespace ts {
: anyType;
const heritageClauses = [
...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))]
...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeImplementedType(b)))]
];
const symbolProps = getNonInterhitedProperties(classType, baseTypes, getPropertiesOfType(classType));
const publicSymbolProps = filter(symbolProps, s => {
@ -6838,6 +6842,28 @@ namespace ts {
}
}
function serializeImplementedType(t: Type) {
let typeArgs: TypeNode[] | undefined;
let reference: Expression | undefined;
if ((t as TypeReference).target && isTypeSymbolAccessible((t as TypeReference).target.symbol, enclosingDeclaration, /*allowModules*/ false)) {
typeArgs = map(getTypeArguments(t as TypeReference), t => typeToTypeNodeHelper(t, context));
reference = symbolToExpression((t as TypeReference).target.symbol, context, SymbolFlags.Type);
}
else if (t.symbol && isTypeSymbolAccessible(t.symbol, enclosingDeclaration, /*allowModules*/ false)) {
reference = symbolToExpression(t.symbol, context, SymbolFlags.Type);
}
if (reference) {
return createExpressionWithTypeArguments(typeArgs, reference);
}
else if (context.tracker.reportInaccessibleBaseType) {
context.tracker.reportInaccessibleBaseType();
return createExpressionWithTypeArguments(/*typeArgs*/ undefined, symbolToExpression(t.symbol, context, SymbolFlags.Type));
}
else {
Debug.fail("context.tracker missing some error reporting methods");
}
}
function getUnusedName(input: string, symbol?: Symbol): string {
if (symbol) {
if (context.remappedSymbolNames!.has("" + getSymbolId(symbol))) {
@ -23925,7 +23951,7 @@ namespace ts {
// if jsx emit was not react as there wont be error being emitted
reactSym.isReferenced = SymbolFlags.All;
// If react symbol is alias, mark it as refereced
// If react symbol is alias, mark it as referenced
if (reactSym.flags & SymbolFlags.Alias && !getTypeOnlyAliasDeclaration(reactSym)) {
markAliasSymbolAsReferenced(reactSym);
}

View File

@ -2971,6 +2971,10 @@
"category": "Error",
"code": 2791
},
"The type '{0}' references an inaccessible base type.": {
"category": "Error",
"code": 2792
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",

View File

@ -70,6 +70,7 @@ namespace ts {
const host = context.getEmitHost();
const symbolTracker: SymbolTracker = {
trackSymbol,
reportInaccessibleBaseType,
reportInaccessibleThisError,
reportInaccessibleUniqueSymbolError,
reportPrivateInBaseOfClassExpression,
@ -175,6 +176,13 @@ namespace ts {
}
}
function reportInaccessibleBaseType() {
if (errorNameNode) {
context.addDiagnostic(createDiagnosticForNode(errorNameNode, Diagnostics.The_type_0_references_an_inaccessible_base_type,
declarationNameToString(errorNameNode)));
}
}
function reportInaccessibleThisError() {
if (errorNameNode) {
context.addDiagnostic(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary,

View File

@ -6465,6 +6465,7 @@ namespace ts {
// declaration emitter to help determine if it should patch up the final declaration file
// with import statements it previously saw (but chose not to emit).
trackSymbol?(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags): void;
reportInaccessibleBaseType?(): void;
reportInaccessibleThisError?(): void;
reportPrivateInBaseOfClassExpression?(propertyName: string): void;
reportInaccessibleUniqueSymbolError?(): void;

View File

@ -80,6 +80,7 @@ namespace ts {
decreaseIndent: noop,
clear: () => str = "",
trackSymbol: noop,
reportInaccessibleBaseType: noop,
reportInaccessibleThisError: noop,
reportInaccessibleUniqueSymbolError: noop,
reportPrivateInBaseOfClassExpression: noop,

View File

@ -0,0 +1,37 @@
//// [tests/cases/conformance/jsdoc/jsdocImplements_namespacedInterface.ts] ////
//// [defs.d.ts]
declare namespace N {
interface A {
mNumber(): number;
}
interface AT<T> {
gen(): T;
}
}
//// [a.js]
/** @implements N.A */
class B {
mNumber() {
return 0;
}
}
/** @implements {N.AT<string>} */
class BAT {
gen() {
return "";
}
}
//// [a.d.ts]
/** @implements N.A */
declare class B implements N.A {
mNumber(): number;
}
/** @implements {N.AT<string>} */
declare class BAT implements N.AT<string> {
gen(): string;
}

View File

@ -0,0 +1,41 @@
=== /defs.d.ts ===
declare namespace N {
>N : Symbol(N, Decl(defs.d.ts, 0, 0))
interface A {
>A : Symbol(A, Decl(defs.d.ts, 0, 21))
mNumber(): number;
>mNumber : Symbol(A.mNumber, Decl(defs.d.ts, 1, 17))
}
interface AT<T> {
>AT : Symbol(AT, Decl(defs.d.ts, 3, 5))
>T : Symbol(T, Decl(defs.d.ts, 4, 17))
gen(): T;
>gen : Symbol(AT.gen, Decl(defs.d.ts, 4, 21))
>T : Symbol(T, Decl(defs.d.ts, 4, 17))
}
}
=== /a.js ===
/** @implements N.A */
class B {
>B : Symbol(B, Decl(a.js, 0, 0))
mNumber() {
>mNumber : Symbol(B.mNumber, Decl(a.js, 1, 9))
return 0;
}
}
/** @implements {N.AT<string>} */
class BAT {
>BAT : Symbol(BAT, Decl(a.js, 5, 1))
gen() {
>gen : Symbol(BAT.gen, Decl(a.js, 7, 11))
return "";
}
}

View File

@ -0,0 +1,35 @@
=== /defs.d.ts ===
declare namespace N {
interface A {
mNumber(): number;
>mNumber : () => number
}
interface AT<T> {
gen(): T;
>gen : () => T
}
}
=== /a.js ===
/** @implements N.A */
class B {
>B : B
mNumber() {
>mNumber : () => number
return 0;
>0 : 0
}
}
/** @implements {N.AT<string>} */
class BAT {
>BAT : BAT
gen() {
>gen : () => string
return "";
>"" : ""
}
}

View File

@ -0,0 +1,28 @@
// @allowJs: true
// @checkJs: true
// @declaration: true
// @emitDeclarationOnly: true
// @outDir: ./out
// @Filename: /defs.d.ts
declare namespace N {
interface A {
mNumber(): number;
}
interface AT<T> {
gen(): T;
}
}
// @Filename: /a.js
/** @implements N.A */
class B {
mNumber() {
return 0;
}
}
/** @implements {N.AT<string>} */
class BAT {
gen() {
return "";
}
}