shapesecurity / shift-scope-js

scope analyser for the Shift AST
http://shift-ast.org/scope.html
Apache License 2.0
11 stars 6 forks source link

How to get locations of a function "FunctionDeclaration" but not the "BindingIdentifier" ? #58

Open ash0080 opened 5 years ago

ash0080 commented 5 years ago
let sourceCode = `
function sum(a,b) {
  return a+b
}
sum(1,2)
`

const { tree, locations } = parseScriptWithLocation(sourceCode)
const globalScope = analyzeScope(tree)
const lookup = new ScopeLookup(globalScope)

walk(tree, {
  enter: function (node, parent) {
    if (node.type === 'CallExpression') {
      const scopeVariables = lookup.lookup(node.callee)
      if (scopeVariables.length === 1) {
        console.dir(scopeVariables, { depth: null })
        console.log(locations.get(scopeVariables[0].declarations[0].node))
      }
    }
  }
})

//  but this only get the `sum` location as [10, 13], what I wanted is get the whole function locations

Sure, I can traverse AST and check if a FunctionDeclaration 'contains' the BindingIdentifier but, is it the correct usage? Any tips here?

bakkot commented 5 years ago

The thing I usually do is write a reducer to give me a map from nodes to their parents:

const { reduce, adapt, MonoidalReducer } = require('shift-reducer');

const parentsMonoid = {
  empty() {
    return {
      children: [],
      pairs: [],
    };
  },
  concat(other) {
    return {
      children: this.children.concat(other.children),
      pairs: this.pairs.concat(other.pairs),
    };
  },
};

const parentsReducer = adapt(
  (d, n) => ({
    children: [n],
    pairs: d.children.map(c => [c, n]).concat(d.pairs),
  }),
  new MonoidalReducer(parentsMonoid)
);

const parentMap = tree => new Map(reduce(parentsReducer, tree).pairs);

which you can use for this:

const parents = parentMap(tree)
[...]
console.log(locations.get(parents.get(scopeVariables[0].declarations[0].node)))
ash0080 commented 5 years ago

Many thanks, using a reducer run much faster than before.