Skip to content

Commit

Permalink
fix: Some issues with inheritence
Browse files Browse the repository at this point in the history
  • Loading branch information
Gerrit0 committed Nov 25, 2020
1 parent 3e50e0c commit 033b1ae
Show file tree
Hide file tree
Showing 14 changed files with 1,260 additions and 968 deletions.
59 changes: 59 additions & 0 deletions src/lib/converter/plugins/ImplementsPlugin.ts
Expand Up @@ -9,6 +9,7 @@ import { Component, ConverterComponent } from "../components";
import { Converter } from "../converter";
import { Context } from "../context";
import { Comment } from "../../models/comments/comment";
import { zip } from "../../utils/array";

/**
* A plugin that detects interface implementations of functions and
Expand Down Expand Up @@ -158,6 +159,54 @@ export class ImplementsPlugin extends ConverterComponent {
}
}

private analyzeInheritance(
context: Context,
reflection: DeclarationReflection
) {
const extendedTypes = (reflection.extendedTypes?.filter((type) => {
return (
type instanceof ReferenceType &&
type.reflection instanceof DeclarationReflection
);
}) ?? []) as Array<
ReferenceType & { reflection: DeclarationReflection }
>;

for (const parent of extendedTypes) {
for (const parentMember of parent.reflection.children ?? []) {
const child = reflection.children?.find(
(child) =>
child.name == parentMember.name &&
child.flags.isStatic === parentMember.flags.isStatic
);

if (child) {
const key = child.getOverwrites()
? "overwrites"
: "inheritedFrom";

for (const [childSig, parentSig] of zip(
child.signatures ?? [],
parentMember.signatures ?? []
)) {
childSig[key] = new ReferenceType(
`${parent.name}.${parentMember.name}`,
parentSig,
context.project
);
}

child[key] = new ReferenceType(
`${parent.name}.${parentMember.name}`,
parentMember,
context.project
);
this.copyComment(child, parentMember);
}
}
}
}

/**
* Triggered when the converter resolves a reflection.
*
Expand Down Expand Up @@ -186,5 +235,15 @@ export class ImplementsPlugin extends ConverterComponent {
}
});
}

if (
reflection.kindOf([
ReflectionKind.Class,
ReflectionKind.Interface,
]) &&
reflection.extendedTypes
) {
this.analyzeInheritance(context, reflection);
}
}
}
91 changes: 81 additions & 10 deletions src/lib/converter/symbols.ts
Expand Up @@ -241,15 +241,29 @@ function convertFunctionOrMethod(
symbol.flags &
(ts.SymbolFlags.Property | ts.SymbolFlags.Method)
);

const allDeclarations =
symbol.getDeclarations()?.filter(ts.isFunctionLike) ?? [];

// Don't do anything if we inherited this method and it is private.
if (
isMethod &&
isInherited(context, symbol) &&
allDeclarations.length > 0 &&
hasFlag(
ts.getCombinedModifierFlags(allDeclarations[0]),
ts.ModifierFlags.Private
)
) {
return;
}

const reflection = context.createDeclarationReflection(
isMethod ? ReflectionKind.Method : ReflectionKind.Function,
symbol,
nameOverride
);

const allDeclarations =
symbol.getDeclarations()?.filter(ts.isFunctionLike) ?? [];

// Note: We have to filter unique declarations here because TS might give us
// the same declaration twice in mixins. This happens to method1 in the mixin
// test when converting SomeClassWithMixin
Expand All @@ -258,6 +272,21 @@ function convertFunctionOrMethod(
if (declarations.length && isMethod) {
// All method signatures must have the same modifier flags.
setModifiers(declarations[0], reflection);

const parentSymbol = context.project.getSymbolFromReflection(
context.scope
);
assert(
parentSymbol,
"Tried to convert an arrow method without a parent."
);
if (
parentSymbol
.getDeclarations()
?.some((d) => d === declarations[0].parent)
) {
reflection.setOverwrites();
}
}

const signatures = filterMap(declarations, (decl) => {
Expand All @@ -275,14 +304,13 @@ function convertFunctionOrMethod(
const scope = context.withScope(reflection);
reflection.signatures ??= [];
for (const [signature, declaration] of zip(signatures, declarations)) {
reflection.signatures.push(
createSignature(
scope,
ReflectionKind.CallSignature,
signature,
declaration
)
const converted = createSignature(
scope,
ReflectionKind.CallSignature,
signature,
declaration
);
reflection.signatures.push(converted);
}
}

Expand Down Expand Up @@ -440,6 +468,20 @@ function convertProperty(
nameOverride?: string
) {
const declarations = symbol.getDeclarations() ?? [];
const parentSymbol = context.project.getSymbolFromReflection(context.scope);
assert(parentSymbol);

// Don't do anything if we inherited this property and it is private.
if (
isInherited(context, symbol) &&
declarations.length > 0 &&
hasFlag(
ts.getCombinedModifierFlags(declarations[0]),
ts.ModifierFlags.Private
)
) {
return;
}

// Special case: We pretend properties are methods if they look like methods.
// This happens with mixins / weird inheritance.
Expand Down Expand Up @@ -486,6 +528,17 @@ function convertProperty(
) {
parameterType = declaration.type;
setModifiers(declaration, reflection);
const parentSymbol = context.project.getSymbolFromReflection(
context.scope
);
assert(parentSymbol, "Tried to convert a property without a parent.");
if (
parentSymbol
.getDeclarations()
?.some((d) => d === declaration.parent)
) {
reflection.setOverwrites();
}
if (ts.isPrivateIdentifier(declaration.name)) {
reflection.setFlag(ReflectionFlag.Private);
}
Expand Down Expand Up @@ -520,6 +573,12 @@ function convertArrowAsMethod(
const signature = context.checker.getSignatureFromDeclaration(arrow);
assert(signature);

const parentSymbol = context.project.getSymbolFromReflection(context.scope);
assert(parentSymbol, "Tried to convert an arrow method without a parent.");
if (parentSymbol.getDeclarations()?.some((d) => d === arrow.parent)) {
reflection.setOverwrites();
}

reflection.signatures = [
createSignature(rc, ReflectionKind.CallSignature, signature, arrow),
];
Expand Down Expand Up @@ -766,6 +825,18 @@ function convertAccessor(
}
}

function isInherited(context: Context, symbol: ts.Symbol) {
const parentSymbol = context.project.getSymbolFromReflection(context.scope);
assert(parentSymbol);
return (
parentSymbol
.getDeclarations()
?.some((d) =>
symbol.getDeclarations()?.some((d2) => d2.parent === d)
) === false
);
}

function setModifiers(declaration: ts.Declaration, reflection: Reflection) {
const modifiers = ts.getCombinedModifierFlags(declaration);
// Note: We only set this flag if the modifier is present because we allow
Expand Down
16 changes: 16 additions & 0 deletions src/lib/models/reflections/declaration.ts
Expand Up @@ -89,6 +89,12 @@ export class DeclarationReflection
*/
overwrites?: Type;

/**
* Flag to determine if this reflection ought to be documented as overwriting another reflection
* or inheriting from it.
*/
private _overwrites = false;

/**
* A type that points to the reflection this reflection has been inherited from.
*
Expand Down Expand Up @@ -236,4 +242,14 @@ export class DeclarationReflection

return result;
}

/** @internal */
setOverwrites() {
this._overwrites = true;
}

/** @internal */
getOverwrites() {
return this._overwrites;
}
}
5 changes: 5 additions & 0 deletions src/lib/models/reflections/project.ts
Expand Up @@ -241,6 +241,11 @@ export class ProjectReflection extends ContainerReflection {
}
}

/** @internal */
getSymbolFromReflection(reflection: Reflection) {
return this.reflectionIdToSymbolMap.get(reflection.id);
}

private getReferenceGraph(): Map<number, number[]> {
if (!this.referenceGraph) {
this.referenceGraph = new Map();
Expand Down

0 comments on commit 033b1ae

Please sign in to comment.