Skip to content

Commit

Permalink
Safely check all array accesses (#749)
Browse files Browse the repository at this point in the history
  • Loading branch information
danez authored Jan 31, 2023
1 parent c6eb278 commit 0a44fca
Show file tree
Hide file tree
Showing 18 changed files with 57 additions and 38 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ module.exports = {
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/no-duplicate-imports': 'error',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/sort-type-union-intersection-members': 'error',
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default async function loadReactDocgenPlugin<T>(
builtins?: Record<string, T>,
): Promise<T> {
if (builtins?.[input]) {
return builtins[input];
return builtins[input]!;
}

const path = resolve(process.cwd(), input);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,5 @@ export default async function loadResolvers(
});
}

return loadResolver(input[0]);
return loadResolver(input[0]!);
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const explodedImperativeHandleVisitors =
// useImperativeHandle(ref, () => ({ name: () => {}, ...}))
const arg = path.get('arguments')[1];

if (arg && !arg.isFunction()) {
if (!arg || !arg.isFunction()) {
return path.skip();
}

Expand Down
12 changes: 8 additions & 4 deletions packages/react-docgen/src/resolver/FindAllDefinitionsResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,24 @@ const explodedVisitors = visitors.explode<TraverseState>({
ClassDeclaration: { enter: classVisitor },
CallExpression: {
enter: function (path, state): void {
const argument = path.get('arguments')[0];

if (!argument) {
return;
}

if (isReactForwardRefCall(path)) {
// If the the inner function was previously identified as a component
// replace it with the parent node
const inner = resolveToValue(
path.get('arguments')[0],
) as ComponentNodePath;
const inner = resolveToValue(argument) as ComponentNodePath;

state.foundDefinitions.delete(inner);
state.foundDefinitions.add(path);

// Do not traverse into arguments
return path.skip();
} else if (isReactCreateClassCall(path)) {
const resolvedPath = resolveToValue(path.get('arguments')[0]);
const resolvedPath = resolveToValue(argument);

if (resolvedPath.isObjectExpression()) {
state.foundDefinitions.add(resolvedPath);
Expand Down
4 changes: 2 additions & 2 deletions packages/react-docgen/src/utils/docblock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function getDocblock(path: NodePath, trailing = false): string | null {
}

if (comments.length > 0) {
return parseDocblock(comments[comments.length - 1].value);
return parseDocblock(comments[comments.length - 1]!.value);
}

return null;
Expand All @@ -50,7 +50,7 @@ export function getDoclets(str: string): Record<string, string> {
let match: RegExpExecArray | null;

while ((match = DOCLET_PATTERN.exec(str))) {
doclets[match[1]] = match[2] || true;
doclets[match[1]!] = match[2] || true;
}

return doclets;
Expand Down
2 changes: 1 addition & 1 deletion packages/react-docgen/src/utils/getFlowType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ function handleGenericTypeAnnotation(
if (
typeParams &&
typeParams[type.name] &&
typeParams[type.name].isGenericTypeAnnotation()
typeParams[type.name]!.isGenericTypeAnnotation()
) {
return type;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ function resolveName(path: NodePath): string | undefined {
' declarations.',
);
}
const id = declarations[0].get('id');
// VariableDeclarator always has at least one declaration, hence the non-null-assertion
const id = declarations[0]!.get('id');

if (id.isIdentifier()) {
return id.node.name;
Expand Down
22 changes: 13 additions & 9 deletions packages/react-docgen/src/utils/getTSType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,15 +207,19 @@ function handleTSTypeLiteral(
} else if (param.isTSCallSignatureDeclaration()) {
type.signature.constructor = handleTSFunctionType(param, typeParams);
} else if (param.isTSIndexSignature() && typeAnnotation.hasNode()) {
const idTypeAnnotation = param
.get('parameters')[0]
.get('typeAnnotation') as NodePath<TSTypeAnnotation | null | undefined>;

if (idTypeAnnotation.hasNode()) {
type.signature.properties.push({
key: getTSTypeWithResolvedTypes(idTypeAnnotation, typeParams),
value: getTSTypeWithRequirements(typeAnnotation, typeParams),
});
const parameters = param.get('parameters');

if (parameters[0]) {
const idTypeAnnotation = parameters[0].get(
'typeAnnotation',
) as NodePath<TSTypeAnnotation | null | undefined>;

if (idTypeAnnotation.hasNode()) {
type.signature.properties.push({
key: getTSTypeWithResolvedTypes(idTypeAnnotation, typeParams),
value: getTSTypeWithRequirements(typeAnnotation, typeParams),
});
}
}
}
});
Expand Down
4 changes: 3 additions & 1 deletion packages/react-docgen/src/utils/getTypeFromReactComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ export default (path: NodePath): NodePath | null => {
if (superTypes.hasNode()) {
const params = superTypes.get('params');

typePath = params[params.length === 3 ? 1 : 0];
if (params.length >= 1) {
typePath = params[params.length === 3 ? 1 : 0]!;
}
} else {
const propsMemberPath = getMemberValuePath(path, 'props');

Expand Down
2 changes: 1 addition & 1 deletion packages/react-docgen/src/utils/getTypeParameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default function getTypeParameters(
typeName.isIdentifier() &&
inputParams[typeName.node.name]
) {
resolvedTypePath = inputParams[typeName.node.name];
resolvedTypePath = inputParams[typeName.node.name]!;
}

params[key] = resolvedTypePath;
Expand Down
6 changes: 2 additions & 4 deletions packages/react-docgen/src/utils/postProcessDocumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ export default function (
const props = documentation.props;

if (props) {
// props with default values should not be required
Object.keys(props).forEach((prop) => {
const propInfo = props[prop];

Object.values(props).forEach((propInfo) => {
// props with default values should not be required
if (propInfo.defaultValue) {
propInfo.required = false;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/react-docgen/src/utils/resolveHOC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function resolveHOC(path: NodePath): NodePath {

if (argumentLength && argumentLength > 0) {
const args = path.get('arguments');
const firstArg = args[0];
const firstArg = args[0]!;

// If the first argument is one of these types then the component might be the last argument
// If there are all identifiers then we cannot figure out exactly and have to assume it is the first
Expand All @@ -31,7 +31,7 @@ export default function resolveHOC(path: NodePath): NodePath {
firstArg.isArrayExpression() ||
firstArg.isSpreadElement())
) {
return resolveHOC(resolveToValue(args[argumentLength - 1]));
return resolveHOC(resolveToValue(args[argumentLength - 1]!));
}

return resolveHOC(resolveToValue(firstArg));
Expand Down
6 changes: 5 additions & 1 deletion packages/react-docgen/src/utils/resolveObjectKeysToArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,11 @@ export default function resolveObjectKeysToArray(
path: NodePath,
): string[] | null {
if (isObjectKeysCall(path)) {
const objectExpression = resolveToValue(path.get('arguments')[0]);
const argument = path.get('arguments')[0];
const objectExpression = resolveToValue(
// isObjectKeysCall already asserts that there is at least one argument, hence the non-null-assertion
argument!,
);
const values = resolveObjectToNameArray(objectExpression);

if (values) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,11 @@ export default function resolveObjectValuesToArray(
path: NodePath,
): string[] | null {
if (isObjectValuesCall(path)) {
const objectExpression = resolveToValue(path.get('arguments')[0]);
const argument = path.get('arguments')[0];
const objectExpression = resolveToValue(
// isObjectValuesCall already asserts that there is at least one argument, hence the non-null-assertion
argument!,
);
const values = resolveObjectToPropMap(objectExpression);

if (values) {
Expand Down
8 changes: 3 additions & 5 deletions packages/react-docgen/src/utils/resolveToValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import initialize from './ts-types/index.js';
import getNameOrValue from './getNameOrValue.js';

function findScopePath(
bindingIdentifiers: Array<NodePath<Identifier>>,
bindingIdentifiers: Array<NodePath<Identifier>> | undefined,
): NodePath | null {
if (bindingIdentifiers && bindingIdentifiers.length >= 1) {
if (bindingIdentifiers && bindingIdentifiers[0]) {
const resolvedParentPath = bindingIdentifiers[0].parentPath;

if (
Expand Down Expand Up @@ -136,9 +136,7 @@ export default function resolveToValue(path: NodePath): NodePath {
// block where it is defined (i.e. we are not traversing into statements)
resolvedPath = findLastAssignedValue(binding.scope.path, path);
if (!resolvedPath) {
const bindingMap = binding.path.getOuterBindingIdentifierPaths(
true,
) as Record<string, Array<NodePath<Identifier>>>;
const bindingMap = binding.path.getOuterBindingIdentifierPaths(true);

resolvedPath = findScopePath(bindingMap[path.node.name]);
}
Expand Down
7 changes: 5 additions & 2 deletions packages/react-docgen/src/utils/ts-types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ declare module '@babel/traverse' {
export interface Scope {
typeBindings: Record<string, TypeBinding>;
getTypeBinding(name: string): TypeBinding | undefined;
getOwnTypeBinding(name: string): TypeBinding;
getOwnTypeBinding(name: string): TypeBinding | undefined;
registerTypeBinding(
this: BaseScope,
typeKind: TypeKind,
Expand Down Expand Up @@ -128,7 +128,10 @@ function getTypeBinding(
return undefined;
}

function getOwnTypeBinding(this: BaseScope, name: string): TypeBinding {
function getOwnTypeBinding(
this: BaseScope,
name: string,
): TypeBinding | undefined {
return this.typeBindings[name];
}

Expand Down
2 changes: 1 addition & 1 deletion tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"strict": true,
"noImplicitAny": false,
"noImplicitReturns": true,
"noUncheckedIndexedAccess": false,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"moduleResolution": "node16",
Expand Down

0 comments on commit 0a44fca

Please sign in to comment.