Skip to content

Commit

Permalink
fix(50415): Language server debug failure - Did not expect GetAccesso…
Browse files Browse the repository at this point in the history
…r to have an Identifier in its trivia (#50470)

* fix(50415): clone props for get/set accessors

* add additional tests

* create helpers to create name, body, modifiers, typeName

* cleanup
  • Loading branch information
a-tarasyuk committed Aug 26, 2022
1 parent 3557092 commit bb3a7ae
Show file tree
Hide file tree
Showing 6 changed files with 490 additions and 16 deletions.
2 changes: 2 additions & 0 deletions src/compiler/factory/nodeFactory.ts
Expand Up @@ -6823,6 +6823,7 @@ namespace ts {
constantValue,
helpers,
startsOnNewLine,
snippetElement,
} = sourceEmitNode;
if (!destEmitNode) destEmitNode = {} as EmitNode;
// We are using `.slice()` here in case `destEmitNode.leadingComments` is pushed to later.
Expand All @@ -6839,6 +6840,7 @@ namespace ts {
}
}
if (startsOnNewLine !== undefined) destEmitNode.startsOnNewLine = startsOnNewLine;
if (snippetElement !== undefined) destEmitNode.snippetElement = snippetElement;
return destEmitNode;
}

Expand Down
48 changes: 32 additions & 16 deletions src/services/codefixes/helpers.ts
Expand Up @@ -79,9 +79,8 @@ namespace ts.codefix {
* In such cases, we assume the declaration to be a `PropertySignature`.
*/
const kind = declaration?.kind ?? SyntaxKind.PropertySignature;
const name = getSynthesizedDeepClone(getNameOfDeclaration(declaration), /*includeTrivia*/ false) as PropertyName;
const declarationName = getNameOfDeclaration(declaration) as PropertyName;
const visibilityModifier = createVisibilityModifier(declaration ? getEffectiveModifierFlags(declaration) : ModifierFlags.None);
const modifiers = visibilityModifier ? factory.createNodeArray([visibilityModifier]) : undefined;
const type = checker.getWidenedType(checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration));
const optional = !!(symbol.flags & SymbolFlags.Optional);
const ambient = !!(enclosingDeclaration.flags & NodeFlags.Ambient) || isAmbient;
Expand All @@ -100,8 +99,8 @@ namespace ts.codefix {
}
}
addClassElement(factory.createPropertyDeclaration(
modifiers,
declaration ? name : symbol.getName(),
createModifiers(visibilityModifier),
declaration ? createName(declarationName) : symbol.getName(),
optional && (preserveOptional & PreserveOptionalFlags.Property) ? factory.createToken(SyntaxKind.QuestionToken) : undefined,
typeNode,
/*initializer*/ undefined));
Expand All @@ -124,21 +123,21 @@ namespace ts.codefix {
for (const accessor of orderedAccessors) {
if (isGetAccessorDeclaration(accessor)) {
addClassElement(factory.createGetAccessorDeclaration(
modifiers,
name,
createModifiers(visibilityModifier),
createName(declarationName),
emptyArray,
typeNode,
ambient ? undefined : body || createStubbedMethodBody(quotePreference)));
createTypeNode(typeNode),
createBody(body, quotePreference, ambient)));
}
else {
Debug.assertNode(accessor, isSetAccessorDeclaration, "The counterpart to a getter should be a setter");
const parameter = getSetAccessorValueParameter(accessor);
const parameterName = parameter && isIdentifier(parameter.name) ? idText(parameter.name) : undefined;
addClassElement(factory.createSetAccessorDeclaration(
modifiers,
name,
createDummyParameters(1, [parameterName], [typeNode], 1, /*inJs*/ false),
ambient ? undefined : body || createStubbedMethodBody(quotePreference)));
createModifiers(visibilityModifier),
createName(declarationName),
createDummyParameters(1, [parameterName], [createTypeNode(typeNode)], 1, /*inJs*/ false),
createBody(body, quotePreference, ambient)));
}
}
break;
Expand All @@ -161,23 +160,23 @@ namespace ts.codefix {
if (declarations.length === 1) {
Debug.assert(signatures.length === 1, "One declaration implies one signature");
const signature = signatures[0];
outputMethod(quotePreference, signature, modifiers, name, ambient ? undefined : body || createStubbedMethodBody(quotePreference));
outputMethod(quotePreference, signature, createModifiers(visibilityModifier), createName(declarationName), createBody(body, quotePreference, ambient));
break;
}

for (const signature of signatures) {
// Ensure nodes are fresh so they can have different positions when going through formatting.
outputMethod(quotePreference, signature, getSynthesizedDeepClones(modifiers, /*includeTrivia*/ false), getSynthesizedDeepClone(name, /*includeTrivia*/ false));
outputMethod(quotePreference, signature, createModifiers(visibilityModifier), createName(declarationName));
}

if (!ambient) {
if (declarations.length > signatures.length) {
const signature = checker.getSignatureFromDeclaration(declarations[declarations.length - 1] as SignatureDeclaration)!;
outputMethod(quotePreference, signature, modifiers, name, body || createStubbedMethodBody(quotePreference));
outputMethod(quotePreference, signature, createModifiers(visibilityModifier), createName(declarationName), createBody(body, quotePreference));
}
else {
Debug.assert(declarations.length === signatures.length, "Declarations and signatures should match count");
addClassElement(createMethodImplementingSignatures(checker, context, enclosingDeclaration, signatures, name, optional && !!(preserveOptional & PreserveOptionalFlags.Method), modifiers, quotePreference, body));
addClassElement(createMethodImplementingSignatures(checker, context, enclosingDeclaration, signatures, createName(declarationName), optional && !!(preserveOptional & PreserveOptionalFlags.Method), createModifiers(visibilityModifier), quotePreference, body));
}
}
break;
Expand All @@ -187,6 +186,23 @@ namespace ts.codefix {
const method = createSignatureDeclarationFromSignature(SyntaxKind.MethodDeclaration, context, quotePreference, signature, body, name, modifiers, optional && !!(preserveOptional & PreserveOptionalFlags.Method), enclosingDeclaration, importAdder) as MethodDeclaration;
if (method) addClassElement(method);
}

function createName(node: PropertyName) {
return getSynthesizedDeepClone(node, /*includeTrivia*/ false);
}

function createModifiers(modifier: Modifier | undefined) {
return modifier ? factory.createNodeArray([modifier]) : undefined;
}

function createBody(block: Block | undefined, quotePreference: QuotePreference, ambient?: boolean) {
return ambient ? undefined :
getSynthesizedDeepClone(block, /*includeTrivia*/ false) || createStubbedMethodBody(quotePreference);
}

function createTypeNode(typeNode: TypeNode | undefined) {
return getSynthesizedDeepClone(typeNode, /*includeTrivia*/ false);
}
}

export function createSignatureDeclarationFromSignature(
Expand Down
211 changes: 211 additions & 0 deletions tests/baselines/reference/completionsOverridingMethod15.baseline
@@ -0,0 +1,211 @@
[
{
"marker": {
"fileName": "/tests/cases/fourslash/completionsOverridingMethod15.ts",
"position": 89,
"name": ""
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": true,
"entries": [
{
"name": "abstract",
"kind": "keyword",
"kindModifiers": "",
"sortText": "15",
"displayParts": [
{
"text": "abstract",
"kind": "keyword"
}
]
},
{
"name": "async",
"kind": "keyword",
"kindModifiers": "",
"sortText": "15",
"displayParts": [
{
"text": "async",
"kind": "keyword"
}
]
},
{
"name": "constructor",
"kind": "keyword",
"kindModifiers": "",
"sortText": "15",
"displayParts": [
{
"text": "constructor",
"kind": "keyword"
}
]
},
{
"name": "declare",
"kind": "keyword",
"kindModifiers": "",
"sortText": "15",
"displayParts": [
{
"text": "declare",
"kind": "keyword"
}
]
},
{
"name": "get",
"kind": "keyword",
"kindModifiers": "",
"sortText": "15",
"displayParts": [
{
"text": "get",
"kind": "keyword"
}
]
},
{
"name": "override",
"kind": "keyword",
"kindModifiers": "",
"sortText": "15",
"displayParts": [
{
"text": "override",
"kind": "keyword"
}
]
},
{
"name": "private",
"kind": "keyword",
"kindModifiers": "",
"sortText": "15",
"displayParts": [
{
"text": "private",
"kind": "keyword"
}
]
},
{
"name": "protected",
"kind": "keyword",
"kindModifiers": "",
"sortText": "15",
"displayParts": [
{
"text": "protected",
"kind": "keyword"
}
]
},
{
"name": "public",
"kind": "keyword",
"kindModifiers": "",
"sortText": "15",
"displayParts": [
{
"text": "public",
"kind": "keyword"
}
]
},
{
"name": "readonly",
"kind": "keyword",
"kindModifiers": "",
"sortText": "15",
"displayParts": [
{
"text": "readonly",
"kind": "keyword"
}
]
},
{
"name": "set",
"kind": "keyword",
"kindModifiers": "",
"sortText": "15",
"displayParts": [
{
"text": "set",
"kind": "keyword"
}
]
},
{
"name": "static",
"kind": "keyword",
"kindModifiers": "",
"sortText": "15",
"displayParts": [
{
"text": "static",
"kind": "keyword"
}
]
},
{
"name": "foo",
"kind": "property",
"kindModifiers": "declare",
"sortText": "17",
"insertText": "get foo(): any {\n}\nset foo(value: any) {\n}",
"displayParts": [
{
"text": "(",
"kind": "punctuation"
},
{
"text": "property",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "B",
"kind": "className"
},
{
"text": ".",
"kind": "punctuation"
},
{
"text": "foo",
"kind": "propertyName"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "any",
"kind": "keyword"
}
],
"documentation": []
}
]
}
}
]

0 comments on commit bb3a7ae

Please sign in to comment.