Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.24.0 Regression Test #164

Merged
merged 7 commits into from
Sep 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
54 changes: 34 additions & 20 deletions source/lib/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,47 @@ export const extractAssertions = (program: Program): Map<Assertion, Set<CallExpr
const checker = program.getTypeChecker();

/**
* Recursively loop over all the nodes and extract all the assertions out of the source files.
* Checks if the given node is semantically valid and is an assertion.
*/
function walkNodes(node: Node) {
if (isCallExpression(node)) {
const expression = isPropertyAccessExpression(node.expression) ?
node.expression.name :
node.expression;
function handleNode(node: CallExpression) {
const expression = isPropertyAccessExpression(node.expression) ?
node.expression.name :
node.expression;

const maybeSymbol = checker.getSymbolAtLocation(expression);

if (!maybeSymbol) {
// Bail out if a Symbol doesn't exist for this Node
// This either means a symbol could not be resolved
// for an identifier, or that the expression is
// syntactically valid, but not semantically valid.
return;
}

const symbol = maybeSymbol.flags & SymbolFlags.Alias ?
checker.getAliasedSymbol(maybeSymbol) :
maybeSymbol;

const maybeAlias = checker.getSymbolAtLocation(expression);
if (maybeAlias) {
const symbol = maybeAlias.flags & SymbolFlags.Alias ?
checker.getAliasedSymbol(maybeAlias) :
maybeAlias;
const identifier = symbol.getName();

const identifier = symbol.getName();
// Check if the call type is a valid assertion
if (assertionFnNames.has(identifier)) {
const assertion = identifier as Assertion;

// Check if the call type is a valid assertion
if (assertionFnNames.has(identifier)) {
const assertion = identifier as Assertion;
const nodes = assertions.get(assertion) ?? new Set<CallExpression>();

const nodes = assertions.get(assertion) ?? new Set<CallExpression>();
nodes.add(node);

nodes.add(node);
assertions.set(assertion, nodes);
}
}

assertions.set(assertion, nodes);
}
}
/**
* Recursively loop over all the nodes and extract all the assertions out of the source files.
*/
function walkNodes(node: Node) {
if (isCallExpression(node)) {
handleNode(node);
}

forEachChild(node, walkNodes);
Expand Down
1 change: 1 addition & 0 deletions source/test/fixtures/undefined-symbol/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {}
7 changes: 7 additions & 0 deletions source/test/fixtures/undefined-symbol/index.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {expectType} from '../../../..';

// Identifier `bar` has no Symbol
const anyCall = (foo: any) => foo.bar();

// Fails with `Cannot read properties of undefined (reading 'flags')` in 0.24.0
expectType<any>(anyCall('foo'));
3 changes: 3 additions & 0 deletions source/test/fixtures/undefined-symbol/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "foo"
}
6 changes: 6 additions & 0 deletions source/test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,3 +466,9 @@ test('assertions should be identified if aliased', async t => {

verify(t, diagnostics, []);
});

test('parsing undefined symbol should not fail', async t => {
const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/undefined-symbol')});

verify(t, diagnostics, []);
});