def visit_FunctionDef(self, node: cst.FunctionDef) -> None: position = self.get_metadata(PositionProvider, node) self.context.scratch[node.name.value] = ( position.start.line, position.start.column, ) node.visit(TestingCollector(self.context))
def visit_FunctionDef_body(self, node: FunctionDef) -> None: class Visitor(m.MatcherDecoratableVisitor): def __init__(self): super().__init__() def visit_EmptyLine_comment(self, node: "EmptyLine") -> None: # FIXME too many matches on test_param_02 if not node.comment: return # TODO: use comment.value return None v = Visitor() node.visit(v) return None
def visit_FunctionDef(self, node: cst.FunctionDef) -> None: if not any( QualifiedNameProvider.has_name( self, decorator.decorator, QualifiedName(name="builtins.classmethod", source=QualifiedNameSource.BUILTIN), ) for decorator in node.decorators ): return # If it's not a @classmethod, we are not interested. if not node.params.params: # No params, but there must be the 'cls' param. # Note that pyre[47] already catches this, but we also generate # an autofix, so it still makes sense for us to report it here. new_params = node.params.with_changes(params=(cst.Param(name=cst.Name(value=CLS)),)) repl = node.with_changes(params=new_params) self.report(node, replacement=repl) return p0_name = node.params.params[0].name if p0_name.value == CLS: return # All good. # Rename all assignments and references of the first param within the # function scope, as long as they are done via a Name node. # We rely on the parser to correctly derive all # assigments and references within the FunctionScope. # The Param node's scope is our classmethod's FunctionScope. scope = self.get_metadata(ScopeProvider, p0_name, None) if not scope: # Cannot autofix without scope metadata. Only report in this case. # Not sure how to repro+cover this in a unit test... # If metadata creation fails, then the whole lint fails, and if it succeeds, # then there is valid metadata. But many other lint rule implementations contain # a defensive scope None check like this one, so I assume it is necessary. self.report(node) return if scope[CLS]: # The scope already has another assignment to "cls". # Trying to rename the first param to "cls" as well may produce broken code. # We should therefore refrain from suggesting an autofix in this case. self.report(node) return refs: List[Union[cst.Name, cst.Attribute]] = [] assignments = scope[p0_name.value] for a in assignments: if isinstance(a, Assignment): assign_node = a.node if isinstance(assign_node, cst.Name): refs.append(assign_node) elif isinstance(assign_node, cst.Param): refs.append(assign_node.name) # There are other types of possible assignment nodes: ClassDef, # FunctionDef, Import, etc. We deliberately do not handle those here. refs += [r.node for r in a.references] repl = node.visit(_RenameTransformer(refs, CLS)) self.report(node, replacement=repl)
def rewrite(self, original_tree: cst.FunctionDef, env: SymbolTable, metadata: tp.MutableMapping) -> PASS_ARGS_T: if not isinstance(original_tree, cst.FunctionDef): raise TypeError('ssa must be run on a FunctionDef') # resolve position information necessary for generating symbol table wrapper = _wrap(to_module(original_tree)) pos_info = wrapper.resolve(PositionProvider) # convert `elif cond:` to `else: if cond:` # (simplifies ssa logic) transformer = with_tracking(ElifToElse)() tree = original_tree.visit(transformer) # original node -> generated nodes node_tracking_table = transformer.node_tracking_table # node_tracking_table.i # generated node -> original nodes wrapper = _wrap(to_module(tree)) writter_attr_visitor = WrittenAttrs() wrapper.visit(writter_attr_visitor) replacer = with_tracking(AttrReplacer)() attr_format = gen_free_prefix(tree, env, '_attr') + '_{}_{}' init_reads = [] names_to_attr = {} seen = set() for written_attr in writter_attr_visitor.written_attrs: d_attr = DeepNode(written_attr) if d_attr in seen: continue if not isinstance(written_attr.value, cst.Name): raise NotImplementedError( 'writing non name nodes is not supported') seen.add(d_attr) attr_name = attr_format.format( written_attr.value.value, written_attr.attr.value, ) # using normal node instead of original node # is safe as parenthesis don't matter: # (name).attr == (name.attr) == name.attr norm = d_attr.normal_node names_to_attr[attr_name] = norm name = cst.Name(attr_name) replacer.add_replacement(written_attr, name) read = to_stmt(make_assign(name, norm)) init_reads.append(read) # Replace references to attr with the name generated above tree = tree.visit(replacer) node_tracking_table = replacer.trace_origins(node_tracking_table) # Rewrite conditions to be ssa cond_prefix = gen_free_prefix(tree, env, '_cond') wrapper = _wrap(tree) name_tests = NameTests(cond_prefix) tree = wrapper.visit(name_tests) node_tracking_table = name_tests.trace_origins(node_tracking_table) # Transform to single return format wrapper = _wrap(tree) single_return = SingleReturn(env, names_to_attr, self.strict) tree = wrapper.visit(single_return) node_tracking_table = single_return.trace_origins(node_tracking_table) # insert the initial reads / final writes / return body = tree.body body = body.with_changes(body=(*init_reads, *body.body, *single_return.tail)) tree = tree.with_changes(body=body) # perform ssa wrapper = _wrap(to_module(tree)) ctxs = wrapper.resolve(ExpressionContextProvider) # These names were constructed in such a way that they are # guaranteed to be ssa and shouldn't be touched by the # transformer final_names = single_return.added_names | name_tests.added_names ssa_transformer = SSATransformer(env, ctxs, final_names, single_return.returning_blocks, strict=self.strict) tree = tree.visit(ssa_transformer) node_tracking_table = ssa_transformer.trace_origins( node_tracking_table) tree.validate_types_deep() # generate symbol table start_ln = pos_info[original_tree].start.line end_ln = pos_info[original_tree].end.line visitor = GenerateSymbolTable( node_tracking_table, ssa_transformer.original_names, pos_info, start_ln, end_ln, ) tree.visit(visitor) metadata.setdefault('SYMBOL-TABLE', list()).append( (type(self), visitor.symbol_table)) return tree, env, metadata