Compare commits
	
		
			5 Commits
		
	
	
		
			kurt-2833
			...
			max-unused
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cfde4e99f9 | |||
| 1bcaaec807 | |||
| 67e90f580d | |||
| 78046eceb6 | |||
| 502cb08a10 | 
| @ -1,6 +1,7 @@ | ||||
| import { parse, recast, initPromise } from './wasm' | ||||
| import { | ||||
|   findAllPreviousVariables, | ||||
|   findUnusedVariables, | ||||
|   isNodeSafeToReplace, | ||||
|   isTypeInValue, | ||||
|   getNodePathFromSourceRange, | ||||
| @ -60,6 +61,91 @@ const variableBelowShouldNotBeIncluded = 3 | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| describe('Test findUnusedVariables', () => { | ||||
|   it('should find unused variable in common kcl code', () => { | ||||
|     // example code | ||||
|     const code = ` | ||||
|       const xRel001 = -20 | ||||
|       const xRel002 = -50 | ||||
|       const part001 = startSketchOn('-XZ') | ||||
|         |> startProfileAt([175.73, 109.38], %) | ||||
|         |> line([xRel001, 178.25], %) | ||||
|         |> line([-265.39, -87.86], %) | ||||
|         |> tangentialArcTo([543.32, -355.04], %) | ||||
|     ` | ||||
|     // parse into ast | ||||
|     const ast = parse(code) | ||||
|     // find unused variables | ||||
|     const unusedVariables = findUnusedVariables(ast) | ||||
|     // check wether unused variables match the expected result | ||||
|     expect( | ||||
|       unusedVariables | ||||
|         .map((node) => node.declarations.map((decl) => decl.id.name)) | ||||
|         .flat() | ||||
|     ).toEqual(['xRel002']) | ||||
|   }) | ||||
|   it("should not find used variable, even if it's heavy nested", () => { | ||||
|     // example code | ||||
|     const code = ` | ||||
|   const deepWithin = 1 | ||||
|   const veryNested = [ | ||||
|     { key: [{ key2: max(5, deepWithin) }] } | ||||
|   ] | ||||
|     ` | ||||
|     // parse into ast | ||||
|     const ast = parse(code) | ||||
|     // find unused variables | ||||
|     const unusedVariables = findUnusedVariables(ast) | ||||
|     // check wether unused variables match the expected result | ||||
|     expect( | ||||
|       unusedVariables.find((node) => | ||||
|         node.declarations.find((decl) => decl.id.name === 'deepWithin') | ||||
|       ) | ||||
|     ).toBeFalsy() | ||||
|   }) | ||||
|   it('should not find used variable, even if used in a closure', () => { | ||||
|     // example code | ||||
|     const code = `const usedInClosure = 1 | ||||
|   fn myFunction = () => { | ||||
|     return usedInClosure | ||||
|   }     | ||||
|     ` | ||||
|     // parse into ast | ||||
|     const ast = parse(code) | ||||
|     // find unused variables | ||||
|     const unusedVariables = findUnusedVariables(ast) | ||||
|     // check wether unused variables match the expected result | ||||
|     expect( | ||||
|       unusedVariables.find((node) => | ||||
|         node.declarations.find((decl) => decl.id.name === 'usedInClosure') | ||||
|       ) | ||||
|     ).toBeFalsy() | ||||
|   }) | ||||
|   // TODO: The commented code in the below does not even parse due to a KCL bug | ||||
|   // When it does parse correctly we'de expect 'a' to be defined but unused | ||||
|   // as it's shadowed by the 'a' in the inner scope | ||||
|   // it('should find unused variable when the same identifier is used in deeper scope', () => { | ||||
|   //   const code = `const a = 1 | ||||
|   // const b = 2 | ||||
|   // fn (a) => { | ||||
|   //   return a + 1 | ||||
|   // } | ||||
|   // const myVar = b + 5` | ||||
|   //   // parse into ast | ||||
|   //   const ast = parse(code) | ||||
|   //   console.log('ast', ast) | ||||
|   //   // find unused variables | ||||
|   //   const unusedVariables = findUnusedVariables(ast) | ||||
|   //   console.log('unusedVariables', unusedVariables) | ||||
|   //   // check wether unused variables match the expected result | ||||
|   //   expect( | ||||
|   //     unusedVariables | ||||
|   //       .map((node) => node.declarations.map((decl) => decl.id.name)) | ||||
|   //       .flat() | ||||
|   //   ).toEqual(['a']) | ||||
|   // }) | ||||
| }) | ||||
|  | ||||
| describe('testing argIsNotIdentifier', () => { | ||||
|   const code = `const part001 = startSketchOn('XY') | ||||
| |> startProfileAt([-1.2, 4.83], %) | ||||
|  | ||||
| @ -392,6 +392,68 @@ export function findAllPreviousVariables( | ||||
|   } | ||||
| } | ||||
|  | ||||
| export function findUnusedVariables(ast: Program): Array<VariableDeclaration> { | ||||
|   const declaredVariables = new Map<string, VariableDeclarator>() // Map to store declared variables | ||||
|   const usedVariables = new Set<string>() // Set to track used variables | ||||
|  | ||||
|   // 1. Traverse and populate | ||||
|   ast.body.forEach((node) => { | ||||
|     traverse(node, { | ||||
|       enter(node) { | ||||
|         if (node.type === 'VariableDeclarator') { | ||||
|           // if node is a VariableDeclarator, | ||||
|           // add it to declaredVariables | ||||
|           declaredVariables.set(node.id.name, node) | ||||
|         } else if (node.type === 'Identifier') { | ||||
|           // if the node is Identifier, (use of a variable) | ||||
|           // check if it is a declared value, | ||||
|           // just in case... | ||||
|           // to be sure it's a part of the declared variables | ||||
|           if (declaredVariables.has(node.name)) { | ||||
|             // if yes - mark it as used | ||||
|             usedVariables.add(node.name) | ||||
|           } | ||||
|         } else if (node.type === 'VariableDeclaration') { | ||||
|           // check if the declaration is model-defining (contains PipeExpression) | ||||
|           const isModelDefining = node.declarations.some( | ||||
|             (decl) => decl.init?.type === 'PipeExpression' | ||||
|           ) | ||||
|           if (isModelDefining) { | ||||
|             // If it is, mark all contained variables as used | ||||
|             node.declarations.forEach((decl) => { | ||||
|               usedVariables.add(decl.id.name) | ||||
|             }) | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|     }) | ||||
|   }) | ||||
|  | ||||
|   // 2. Remove used variables from declaredVariables | ||||
|   usedVariables.forEach((name) => { | ||||
|     declaredVariables.delete(name) | ||||
|   }) | ||||
|  | ||||
|   // 3. collect unused VariableDeclarations | ||||
|   const unusedVariableDeclarations: Array<VariableDeclaration> = [] | ||||
|   ast.body.forEach((node) => { | ||||
|     if (node.type === 'VariableDeclaration') { | ||||
|       const unusedDeclarators = node.declarations.filter((declarator) => | ||||
|         declaredVariables.has(declarator.id.name) | ||||
|       ) | ||||
|       if (unusedDeclarators.length > 0) { | ||||
|         unusedVariableDeclarations.push({ | ||||
|           ...node, | ||||
|           declarations: unusedDeclarators, | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   }) | ||||
|  | ||||
|   // 4. Return the unused variables | ||||
|   return unusedVariableDeclarations | ||||
| } | ||||
|  | ||||
| type ReplacerFn = (_ast: Program, varName: string) => { modifiedAst: Program } | ||||
|  | ||||
| export function isNodeSafeToReplace( | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	