@typedef: Improve error spans from declaration emit (#42501)

* @typedef: Improve error spans from declaration emit

This is a proof-of-concept fix. I think it could be expanded for all of
jsdoc, but I only set it up for jsdoc type aliases. It could use a lot
of polish too.

* track error node in isSymbolAccessible instead

* Switch to using enclosingDeclaration

Remove trueErrorNode

* add test of @callback and @enum

* Better error + fix @enum error

Since enums don't have a name property, you *have* to call
`getNameOfDeclaration` to go looking through the AST for one.
This commit is contained in:
Nathan Shively-Sanders
2021-01-28 08:35:05 -08:00
committed by GitHub
parent 517f32940a
commit d2443a5df1
12 changed files with 88 additions and 26 deletions

View File

@@ -4183,7 +4183,8 @@ namespace ts {
return {
accessibility: SymbolAccessibility.CannotBeNamed,
errorSymbolName: symbolToString(symbol, enclosingDeclaration, meaning),
errorModuleName: symbolToString(symbolExternalModule)
errorModuleName: symbolToString(symbolExternalModule),
errorNode: isInJSFile(enclosingDeclaration) ? enclosingDeclaration : undefined,
};
}
}
@@ -4699,7 +4700,7 @@ namespace ts {
function createMappedTypeNodeFromType(type: MappedType) {
Debug.assert(!!(type.flags & TypeFlags.Object));
const readonlyToken = type.declaration.readonlyToken ? <ReadonlyToken | PlusToken | MinusToken>factory.createToken(type.declaration.readonlyToken.kind) : undefined;
const readonlyToken = type.declaration.readonlyToken ? <ReadonlyKeyword | PlusToken | MinusToken>factory.createToken(type.declaration.readonlyToken.kind) : undefined;
const questionToken = type.declaration.questionToken ? <QuestionToken | PlusToken | MinusToken>factory.createToken(type.declaration.questionToken.kind) : undefined;
let appropriateConstraintTypeNode: TypeNode;
if (isMappedTypeWithKeyofConstraintDeclaration(type)) {
@@ -6183,7 +6184,7 @@ namespace ts {
tracker: {
...oldcontext.tracker,
trackSymbol: (sym, decl, meaning) => {
const accessibleResult = isSymbolAccessible(sym, decl, meaning, /*computeALiases*/ false);
const accessibleResult = isSymbolAccessible(sym, decl, meaning, /*computeAliases*/ false);
if (accessibleResult.accessibility === SymbolAccessibility.Accessible) {
// Lookup the root symbol of the chain of refs we'll use to access it and serialize it
const chain = lookupSymbolChainWorker(sym, context, meaning);
@@ -6625,16 +6626,17 @@ namespace ts {
function addResult(node: Statement, additionalModifierFlags: ModifierFlags) {
if (canHaveModifiers(node)) {
let newModifierFlags: ModifierFlags = ModifierFlags.None;
const enclosingDeclaration = context.enclosingDeclaration &&
(isJSDocTypeAlias(context.enclosingDeclaration) ? getSourceFileOfNode(context.enclosingDeclaration) : context.enclosingDeclaration);
if (additionalModifierFlags & ModifierFlags.Export &&
context.enclosingDeclaration &&
(isExportingScope(context.enclosingDeclaration) || isModuleDeclaration(context.enclosingDeclaration)) &&
enclosingDeclaration && (isExportingScope(enclosingDeclaration) || isModuleDeclaration(enclosingDeclaration)) &&
canHaveExportModifier(node)
) {
// Classes, namespaces, variables, functions, interfaces, and types should all be `export`ed in a module context if not private
newModifierFlags |= ModifierFlags.Export;
}
if (addingDeclare && !(newModifierFlags & ModifierFlags.Export) &&
(!context.enclosingDeclaration || !(context.enclosingDeclaration.flags & NodeFlags.Ambient)) &&
(!enclosingDeclaration || !(enclosingDeclaration.flags & NodeFlags.Ambient)) &&
(isEnumDeclaration(node) || isVariableStatement(node) || isFunctionDeclaration(node) || isClassDeclaration(node) || isModuleDeclaration(node))) {
// Classes, namespaces, variables, enums, and functions all need `declare` modifiers to be valid in a declaration file top-level scope
newModifierFlags |= ModifierFlags.Ambient;
@@ -6657,6 +6659,8 @@ namespace ts {
const commentText = jsdocAliasDecl ? jsdocAliasDecl.comment || jsdocAliasDecl.parent.comment : undefined;
const oldFlags = context.flags;
context.flags |= NodeBuilderFlags.InTypeAlias;
const oldEnclosingDecl = context.enclosingDeclaration;
context.enclosingDeclaration = jsdocAliasDecl;
const typeNode = jsdocAliasDecl && jsdocAliasDecl.typeExpression
&& isJSDocTypeExpression(jsdocAliasDecl.typeExpression)
&& serializeExistingTypeNode(context, jsdocAliasDecl.typeExpression.type, includePrivateSymbol, bundled)
@@ -6666,6 +6670,7 @@ namespace ts {
!commentText ? [] : [{ kind: SyntaxKind.MultiLineCommentTrivia, text: "*\n * " + commentText.replace(/\n/g, "\n * ") + "\n ", pos: -1, end: -1, hasTrailingNewLine: true }]
), modifierFlags);
context.flags = oldFlags;
context.enclosingDeclaration = oldEnclosingDecl;
}
function serializeInterface(symbol: Symbol, symbolName: string, modifierFlags: ModifierFlags) {

View File

@@ -3537,6 +3537,10 @@
"category": "Error",
"code": 4083
},
"Exported type alias '{0}' has or is using private name '{1}' from module {2}.": {
"category": "Error",
"code": 4084
},
"Conflicting definitions for '{0}' found at '{1}' and '{2}'. Consider installing a specific version of this library to resolve the conflict.": {
"category": "Error",
"code": 4090

View File

@@ -217,12 +217,12 @@ namespace ts {
function transformDeclarationsForJS(sourceFile: SourceFile, bundled?: boolean) {
const oldDiag = getSymbolAccessibilityDiagnostic;
getSymbolAccessibilityDiagnostic = (s) => ({
getSymbolAccessibilityDiagnostic = (s) => (s.errorNode && canProduceDiagnostics(s.errorNode) ? createGetSymbolAccessibilityDiagnosticForNode(s.errorNode)(s) : ({
diagnosticMessage: s.errorModuleName
? Diagnostics.Declaration_emit_for_this_file_requires_using_private_name_0_from_module_1_An_explicit_type_annotation_may_unblock_declaration_emit
: Diagnostics.Declaration_emit_for_this_file_requires_using_private_name_0_An_explicit_type_annotation_may_unblock_declaration_emit,
errorNode: s.errorNode || sourceFile
});
}));
const result = resolver.getDeclarationStatementsForSourceFile(sourceFile, declarationEmitNodeBuilderFlags, symbolTracker, bundled);
getSymbolAccessibilityDiagnostic = oldDiag;
return result;

View File

@@ -27,7 +27,10 @@ namespace ts {
| TypeAliasDeclaration
| ConstructorDeclaration
| IndexSignatureDeclaration
| PropertyAccessExpression;
| PropertyAccessExpression
| JSDocTypedefTag
| JSDocCallbackTag
| JSDocEnumTag;
export function canProduceDiagnostics(node: Node): node is DeclarationDiagnosticProducing {
return isVariableDeclaration(node) ||
@@ -48,7 +51,8 @@ namespace ts {
isTypeAliasDeclaration(node) ||
isConstructorDeclaration(node) ||
isIndexSignatureDeclaration(node) ||
isPropertyAccessExpression(node);
isPropertyAccessExpression(node) ||
isJSDocTypeAlias(node);
}
export function createGetSymbolAccessibilityDiagnosticForNodeName(node: DeclarationDiagnosticProducing) {
@@ -124,7 +128,7 @@ namespace ts {
}
}
export function createGetSymbolAccessibilityDiagnosticForNode(node: DeclarationDiagnosticProducing): (symbolAccessibilityResult: SymbolAccessibilityResult) => SymbolAccessibilityDiagnostic | undefined {
export function createGetSymbolAccessibilityDiagnosticForNode(node: DeclarationDiagnosticProducing): GetSymbolAccessibilityDiagnostic {
if (isVariableDeclaration(node) || isPropertyDeclaration(node) || isPropertySignature(node) || isPropertyAccessExpression(node) || isBindingElement(node) || isConstructorDeclaration(node)) {
return getVariableDeclarationTypeVisibilityError;
}
@@ -149,7 +153,7 @@ namespace ts {
else if (isImportEqualsDeclaration(node)) {
return getImportEntityNameVisibilityError;
}
else if (isTypeAliasDeclaration(node)) {
else if (isTypeAliasDeclaration(node) || isJSDocTypeAlias(node)) {
return getTypeAliasDeclarationVisibilityError;
}
else {
@@ -474,11 +478,13 @@ namespace ts {
};
}
function getTypeAliasDeclarationVisibilityError(): SymbolAccessibilityDiagnostic {
function getTypeAliasDeclarationVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
return {
diagnosticMessage: Diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1,
errorNode: (node as TypeAliasDeclaration).type,
typeName: (node as TypeAliasDeclaration).name
diagnosticMessage: symbolAccessibilityResult.errorModuleName
? Diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1_from_module_2
: Diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1,
errorNode: isJSDocTypeAlias(node) ? Debug.checkDefined(node.typeExpression) : (node as TypeAliasDeclaration).type,
typeName: isJSDocTypeAlias(node) ? getNameOfDeclaration(node) : (node as TypeAliasDeclaration).name,
};
}
}