diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b975b9848853d..06c8ddb3eda0a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1472,6 +1472,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { var argumentsSymbol = createSymbol(SymbolFlags.Property, "arguments" as __String); var requireSymbol = createSymbol(SymbolFlags.Property, "require" as __String); var isolatedModulesLikeFlagName = compilerOptions.verbatimModuleSyntax ? "verbatimModuleSyntax" : "isolatedModules"; + // It is an error to use `importsNotUsedAsValues` alongside `verbatimModuleSyntax`, but we still need to not crash. + // Given that, in such a scenario, `verbatimModuleSyntax` is basically disabled, as least as far as alias visibility tracking goes. + var canCollectSymbolAliasAccessabilityData = !compilerOptions.verbatimModuleSyntax || !!compilerOptions.importsNotUsedAsValues; /** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */ var apparentArgumentCount: number | undefined; @@ -4540,7 +4543,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function markExportAsReferenced(node: ImportEqualsDeclaration | ExportSpecifier) { - if (compilerOptions.verbatimModuleSyntax) { + if (!canCollectSymbolAliasAccessabilityData) { return; } const symbol = getSymbolOfDeclaration(node); @@ -4559,7 +4562,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // we reach a non-alias or an exported entity (which is always considered referenced). We do this by checking the target of // the alias as an expression (which recursively takes us back here if the target references another alias). function markAliasSymbolAsReferenced(symbol: Symbol) { - Debug.assert(!compilerOptions.verbatimModuleSyntax); + Debug.assert(canCollectSymbolAliasAccessabilityData); const links = getSymbolLinks(symbol); if (!links.referenced) { links.referenced = true; @@ -27631,7 +27634,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function markAliasReferenced(symbol: Symbol, location: Node) { - if (compilerOptions.verbatimModuleSyntax) { + if (!canCollectSymbolAliasAccessabilityData) { return; } if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && !getTypeOnlyAliasDeclaration(symbol, SymbolFlags.Value)) { @@ -30773,7 +30776,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { jsxFactorySym.isReferenced = SymbolFlags.All; // If react/jsxFactory symbol is alias, mark it as refereced - if (!compilerOptions.verbatimModuleSyntax && jsxFactorySym.flags & SymbolFlags.Alias && !getTypeOnlyAliasDeclaration(jsxFactorySym)) { + if (canCollectSymbolAliasAccessabilityData && jsxFactorySym.flags & SymbolFlags.Alias && !getTypeOnlyAliasDeclaration(jsxFactorySym)) { markAliasSymbolAsReferenced(jsxFactorySym); } } @@ -39710,7 +39713,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const meaning = (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias; const rootSymbol = resolveName(rootName, rootName.escapedText, meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isReference*/ true); if (rootSymbol && rootSymbol.flags & SymbolFlags.Alias) { - if (!compilerOptions.verbatimModuleSyntax + if (canCollectSymbolAliasAccessabilityData && symbolIsValue(rootSymbol) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol)) && !getTypeOnlyAliasDeclaration(rootSymbol)) { @@ -44227,6 +44230,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function checkImportsForTypeOnlyConversion(sourceFile: SourceFile) { + if (!canCollectSymbolAliasAccessabilityData) { + return; + } for (const statement of sourceFile.statements) { if (canConvertImportDeclarationToTypeOnly(statement) || canConvertImportEqualsDeclarationToTypeOnly(statement)) { error( @@ -45949,7 +45955,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function isValueAliasDeclaration(node: Node): boolean { - Debug.assert(!compilerOptions.verbatimModuleSyntax); + Debug.assert(canCollectSymbolAliasAccessabilityData); switch (node.kind) { case SyntaxKind.ImportEqualsDeclaration: return isAliasResolvedToValue(getSymbolOfDeclaration(node as ImportEqualsDeclaration)); @@ -46003,7 +46009,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function isReferencedAliasDeclaration(node: Node, checkChildren?: boolean): boolean { - Debug.assert(!compilerOptions.verbatimModuleSyntax); + Debug.assert(canCollectSymbolAliasAccessabilityData); if (isAliasSymbolDeclaration(node)) { const symbol = getSymbolOfDeclaration(node as Declaration); const links = symbol && getSymbolLinks(symbol); @@ -46377,13 +46383,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { isValueAliasDeclaration: nodeIn => { const node = getParseTreeNode(nodeIn); // Synthesized nodes are always treated like values. - return node ? isValueAliasDeclaration(node) : true; + return node && canCollectSymbolAliasAccessabilityData ? isValueAliasDeclaration(node) : true; }, hasGlobalName, isReferencedAliasDeclaration: (nodeIn, checkChildren?) => { const node = getParseTreeNode(nodeIn); // Synthesized nodes are always treated as referenced. - return node ? isReferencedAliasDeclaration(node, checkChildren) : true; + return node && canCollectSymbolAliasAccessabilityData ? isReferencedAliasDeclaration(node, checkChildren) : true; }, getNodeCheckFlags: nodeIn => { const node = getParseTreeNode(nodeIn); diff --git a/tests/baselines/reference/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.errors.txt b/tests/baselines/reference/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.errors.txt new file mode 100644 index 0000000000000..6a555d7fd01e6 --- /dev/null +++ b/tests/baselines/reference/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.errors.txt @@ -0,0 +1,19 @@ +error TS5104: Option 'importsNotUsedAsValues' is redundant and cannot be specified with option 'verbatimModuleSyntax'. +tests/cases/compiler/file.ts(1,1): error TS1287: A top-level 'export' modifier cannot be used on value declarations in a CommonJS module when 'verbatimModuleSyntax' is enabled. +tests/cases/compiler/index.ts(1,1): error TS1371: This import is never used as a value and must use 'import type' because 'importsNotUsedAsValues' is set to 'error'. +tests/cases/compiler/index.ts(1,9): error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled. + + +!!! error TS5104: Option 'importsNotUsedAsValues' is redundant and cannot be specified with option 'verbatimModuleSyntax'. +==== tests/cases/compiler/file.ts (1 errors) ==== + export class A {} + ~~~~~~ +!!! error TS1287: A top-level 'export' modifier cannot be used on value declarations in a CommonJS module when 'verbatimModuleSyntax' is enabled. +==== tests/cases/compiler/index.ts (2 errors) ==== + import {A} from "./file"; + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1371: This import is never used as a value and must use 'import type' because 'importsNotUsedAsValues' is set to 'error'. + ~ +!!! error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled. + + const a: A = null as any; \ No newline at end of file diff --git a/tests/baselines/reference/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.js b/tests/baselines/reference/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.js new file mode 100644 index 0000000000000..a0decaa9e268a --- /dev/null +++ b/tests/baselines/reference/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.js @@ -0,0 +1,24 @@ +//// [tests/cases/compiler/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.ts] //// + +//// [file.ts] +export class A {} +//// [index.ts] +import {A} from "./file"; + +const a: A = null as any; + +//// [file.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.A = void 0; +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +exports.A = A; +//// [index.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var file_1 = require("./file"); +var a = null; diff --git a/tests/baselines/reference/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.symbols b/tests/baselines/reference/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.symbols new file mode 100644 index 0000000000000..b61a513443929 --- /dev/null +++ b/tests/baselines/reference/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.symbols @@ -0,0 +1,12 @@ +=== tests/cases/compiler/file.ts === +export class A {} +>A : Symbol(A, Decl(file.ts, 0, 0)) + +=== tests/cases/compiler/index.ts === +import {A} from "./file"; +>A : Symbol(A, Decl(index.ts, 0, 8)) + +const a: A = null as any; +>a : Symbol(a, Decl(index.ts, 2, 5)) +>A : Symbol(A, Decl(index.ts, 0, 8)) + diff --git a/tests/baselines/reference/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.types b/tests/baselines/reference/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.types new file mode 100644 index 0000000000000..fbc296943b300 --- /dev/null +++ b/tests/baselines/reference/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.types @@ -0,0 +1,12 @@ +=== tests/cases/compiler/file.ts === +export class A {} +>A : A + +=== tests/cases/compiler/index.ts === +import {A} from "./file"; +>A : typeof A + +const a: A = null as any; +>a : A +>null as any : any + diff --git a/tests/cases/compiler/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.ts b/tests/cases/compiler/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.ts new file mode 100644 index 0000000000000..36624be56f595 --- /dev/null +++ b/tests/cases/compiler/noCrashWithVerbatimModuleSyntaxAndImportsNotUsedAsValues.ts @@ -0,0 +1,9 @@ +// @verbatimModuleSyntax: true +// @importsNotUsedAsValues: error +// @ignoreDeprecations: 5.0 +// @filename: file.ts +export class A {} +// @filename: index.ts +import {A} from "./file"; + +const a: A = null as any; \ No newline at end of file