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

Add section - Find identifiers with same symbol #3

Merged
merged 4 commits into from
Jan 3, 2020
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions example-transformers/match-identifier-by-symbol/source.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
let foo = 1;
foo = 2;
{
const foo = 'abcd';
}
foo = 3;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
let foo = 1;
foo = 2;
{
const foo = 'abcd';
}
foo = 3;
45 changes: 45 additions & 0 deletions example-transformers/match-identifier-by-symbol/transformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as ts from 'typescript';

const transformerProgram = (program: ts.Program) => {
const typeChecker = program.getTypeChecker();

// Create array of found symbols
const foundSymbols = new Array<ts.Symbol>();

const transformerFactory: ts.TransformerFactory<ts.SourceFile> = context => {
return sourceFile => {
const visitor = (node: ts.Node): ts.Node => {
if (ts.isIdentifier(node)) {
const relatedSymbol = typeChecker.getSymbolAtLocation(node);

// Check if array already contains same symbol - check by reference
if (foundSymbols.includes(relatedSymbol)) {
const foundIndex = foundSymbols.indexOf(relatedSymbol);
console.log(
`Found existing symbol at position = ${foundIndex} and name = "${relatedSymbol.name}"`
);
} else {
// If not found, Add it to array
foundSymbols.push(relatedSymbol);

console.log(
`Found new symbol with name = "${
relatedSymbol.name
}". Added at positon = ${foundSymbols.length - 1}`
);
}

return node;
}

return ts.visitEachChild(node, visitor, context);
};

return ts.visitNode(sourceFile, visitor);
};
};

return transformerFactory;
};

export default transformerProgram;
8 changes: 8 additions & 0 deletions example-transformers/match-identifier-by-symbol/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "transformed",
"plugins": [{ "transform": "./transformer.ts" }]
},
"files": ["source.ts"]
}
74 changes: 71 additions & 3 deletions translations/en/transformer-handbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ This document covers how to write a [Typescript](https://typescriptlang.org/) [T
- [Transformation operations](#transformation-operations)
- [Visiting](#visiting-1)
- [Checking a node is a certain type](#checking-a-node-is-a-certain-type)
- [Check if an identifier is referenced](#check-if-an-identifier-is-referenced)
- [Check if two identifiers refer to the same symbol](#check-if-two-identifiers-refer-to-the-same-symbol)
- [Find a specific parent](#find-a-specific-parent)
- [Stopping traversal](#stopping-traversal)
- [Manipulation](#manipulation)
Expand Down Expand Up @@ -879,9 +879,77 @@ const visitor = (node: ts.Node): ts.Node => {
};
```

#### Check if an identifier is referenced
#### Check if two identifiers refer to the same symbol

> **TODO** - Is this possible?
Identifiers are created by the parser and are always unique.
Say, if you create a variable `foo` and use it in another line, it will create 2 separate identifiers with the same text `foo`.

Then, the linker runs through these identifiers and connects the identifiers referring to the same variable with a common symbol (while considering scope and shadowing). Think of symbols as what we intuitively think as variables.

So, to check if two identifiers refer to the same symbol - just get the symbols related to the identifier and check if they are the same (by reference).
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if an identifier is not referenced typeChecker.getSymbolAtLocation(node) would return undefined yeah? should we mention that? 😄


**Short example** -

```ts
const symbol1 = typeChecker.getSymbolAtLocation(node1);
const symbol2 = typeChecker.getSymbolAtLocation(node2);

symbol1 === symbol2 // check by reference
```

**Full example** -

This will log all repeating symbols.

```ts
import * as ts from 'typescript';

const transformerProgram = (program: ts.Program) => {
const typeChecker = program.getTypeChecker();

// Create array of found symbols
const foundSymbols = new Array<ts.Symbol>();

const transformerFactory: ts.TransformerFactory<ts.SourceFile> = context => {
return sourceFile => {
const visitor = (node: ts.Node): ts.Node => {
if (ts.isIdentifier(node)) {
const relatedSymbol = typeChecker.getSymbolAtLocation(node);

// Check if array already contains same symbol - check by reference
if (foundSymbols.includes(relatedSymbol)) {
const foundIndex = foundSymbols.indexOf(relatedSymbol);
console.log(
`Found existing symbol at position = ${foundIndex} and name = "${relatedSymbol.name}"`
);
} else {
// If not found, Add it to array
foundSymbols.push(relatedSymbol);

console.log(
`Found new symbol with name = "${
relatedSymbol.name
}". Added at positon = ${foundSymbols.length - 1}`
);
}

return node;
}

return ts.visitEachChild(node, visitor, context);
};

return ts.visitNode(sourceFile, visitor);
};
};

return transformerFactory;
};

export default transformerProgram;
```

> **Tip** - You can see the source for this at [/example-transformers/match-identifier-by-symbol](/example-transformers/match-identifier-by-symbol)

#### Find a specific parent

Expand Down