def _get_async_expr_replacement( self, node: cst.CSTNode) -> Optional[cst.CSTNode]: if m.matches(node, m.Call()): node = cast(cst.Call, node) return self._get_async_call_replacement(node) elif m.matches(node, m.Attribute()): node = cast(cst.Attribute, node) return self._get_async_attr_replacement(node) elif m.matches(node, m.UnaryOperation(operator=m.Not())): node = cast(cst.UnaryOperation, node) replacement_expression = self._get_async_expr_replacement( node.expression) if replacement_expression is not None: return node.with_changes(expression=replacement_expression) elif m.matches(node, m.BooleanOperation()): node = cast(cst.BooleanOperation, node) maybe_left = self._get_async_expr_replacement(node.left) maybe_right = self._get_async_expr_replacement(node.right) if maybe_left is not None or maybe_right is not None: left_replacement = maybe_left if maybe_left is not None else node.left right_replacement = (maybe_right if maybe_right is not None else node.right) return node.with_changes(left=left_replacement, right=right_replacement) return None
def basic_parenthesize( node: libcst.CSTNode, whitespace: Optional[libcst.BaseParenthesizableWhitespace] = None, ) -> libcst.CSTNode: if not hasattr(node, "lpar"): return node if whitespace: return node.with_changes( lpar=[libcst.LeftParen(whitespace_after=whitespace)], rpar=[libcst.RightParen()], ) return node.with_changes(lpar=[libcst.LeftParen()], rpar=[libcst.RightParen()])
def _maybe_autofix_node(self, node: cst.CSTNode, attribute_name: str) -> None: replacement_value = self._get_async_expr_replacement( getattr(node, attribute_name)) if replacement_value is not None: replacement = node.with_changes( **{attribute_name: replacement_value}) self.report(node, replacement=replacement)
def _leave(self, original: cst.CSTNode, updated: cst.CSTNode) -> cst.CSTNode: # TODO: if global scope query create a module tree from scratch? # NOTE: in the future can cache lib cst node comparisons for speed match = notFound if first_ref_index is not None: match = tryFind(lambda m: original.deep_equals( m.path[first_ref_index].node), matches) if match is not notFound: from_assert = first(astNodeFromAssertion(transform, match)) return from_assert elif original in transform.references: # TODO: replace references to anything destroyed by the transform pass elif find_attempt is notFound and isinstance(updated, cst.Module): module_match = Match( [CaptureExpr().contextualize(node=updated)]) return updated.with_changes(body=(*updated.body, *astNodeFromAssertion(transform, module_match))) else: return updated
def render_node(node: cst.CSTNode, module: Optional[cst.Module] = None) -> str: if module is None: module = cst.Module(body=[]) code = module.code_for_node(node.with_changes(leading_lines=())) return code
def renamed(self, old_node: cst.CSTNode): return old_node.with_changes(name=self.get_new_cst_name(old_node.name))
def obf_universal(self, node: cst.CSTNode, *types): if m.matches(node, m.Name()): types = ('a', 'ca', 'v', 'cv') if not types else types node = cst.ensure_type(node, cst.Name) if self.can_rename(node.value, *types): node = self.get_new_cst_name(node) elif m.matches(node, m.NameItem()): node = cst.ensure_type(node, cst.NameItem) node = node.with_changes(name=self.obf_universal(node.name)) elif m.matches(node, m.Call()): node = cst.ensure_type(node, cst.Call) if self.change_methods or self.change_functions: node = self.new_obf_function_name(node) if self.change_arguments or self.change_method_arguments: node = self.obf_function_args(node) elif m.matches(node, m.Attribute()): node = cst.ensure_type(node, cst.Attribute) value = node.value attr = node.attr self.obf_universal(value) self.obf_universal(attr) elif m.matches(node, m.AssignTarget()): node = cst.ensure_type(node, cst.AssignTarget) node = node.with_changes(target=self.obf_universal(node.target)) elif m.matches(node, m.List() | m.Tuple()): node = cst.ensure_type(node, cst.List) if m.matches( node, m.List()) else cst.ensure_type(node, cst.Tuple) new_elements = [] for el in node.elements: new_elements.append(self.obf_universal(el)) node = node.with_changes(elements=new_elements) elif m.matches(node, m.Subscript()): node = cst.ensure_type(node, cst.Subscript) new_slice = [] for el in node.slice: new_slice.append( el.with_changes(slice=self.obf_slice(el.slice))) node = node.with_changes(slice=new_slice) node = node.with_changes(value=self.obf_universal(node.value)) elif m.matches(node, m.Element()): node = cst.ensure_type(node, cst.Element) node = node.with_changes(value=self.obf_universal(node.value)) elif m.matches(node, m.Dict()): node = cst.ensure_type(node, cst.Dict) new_elements = [] for el in node.elements: new_elements.append(self.obf_universal(el)) node = node.with_changes(elements=new_elements) elif m.matches(node, m.DictElement()): node = cst.ensure_type(node, cst.DictElement) new_key = self.obf_universal(node.key) new_val = self.obf_universal(node.value) node = node.with_changes(key=new_key, value=new_val) elif m.matches(node, m.StarredDictElement()): node = cst.ensure_type(node, cst.StarredDictElement) node = node.with_changes(value=self.obf_universal(node.value)) elif m.matches(node, m.If() | m.While()): node = cst.ensure_type(node, cst.IfExp) if m.matches( node, cst.If | cst.IfExp) else cst.ensure_type(node, cst.While) node = node.with_changes(test=self.obf_universal(node.test)) elif m.matches(node, m.IfExp()): node = cst.ensure_type(node, cst.IfExp) node = node.with_changes(body=self.obf_universal(node.body)) node = node.with_changes(test=self.obf_universal(node.test)) node = node.with_changes(orelse=self.obf_universal(node.orelse)) elif m.matches(node, m.Comparison()): node = cst.ensure_type(node, cst.Comparison) new_compars = [] for target in node.comparisons: new_compars.append(self.obf_universal(target)) node = node.with_changes(left=self.obf_universal(node.left)) node = node.with_changes(comparisons=new_compars) elif m.matches(node, m.ComparisonTarget()): node = cst.ensure_type(node, cst.ComparisonTarget) node = node.with_changes( comparator=self.obf_universal(node.comparator)) elif m.matches(node, m.FormattedString()): node = cst.ensure_type(node, cst.FormattedString) new_parts = [] for part in node.parts: new_parts.append(self.obf_universal(part)) node = node.with_changes(parts=new_parts) elif m.matches(node, m.FormattedStringExpression()): node = cst.ensure_type(node, cst.FormattedStringExpression) node = node.with_changes( expression=self.obf_universal(node.expression)) elif m.matches(node, m.BinaryOperation() | m.BooleanOperation()): node = cst.ensure_type(node, cst.BinaryOperation) if m.matches( node, m.BinaryOperation()) else cst.ensure_type( node, cst.BooleanOperation) node = node.with_changes(left=self.obf_universal(node.left), right=self.obf_universal(node.right)) elif m.matches(node, m.UnaryOperation()): node = cst.ensure_type(node, cst.UnaryOperation) node = node.with_changes( expression=self.obf_universal(node.expression)) elif m.matches(node, m.ListComp()): node = cst.ensure_type(node, cst.ListComp) node = node.with_changes(elt=self.obf_universal(node.elt)) node = node.with_changes(for_in=self.obf_universal(node.for_in)) elif m.matches(node, m.DictComp()): node = cst.ensure_type(node, cst.DictComp) node = node.with_changes(key=self.obf_universal(node.key)) node = node.with_changes(value=self.obf_universal(node.value)) node = node.with_changes(for_in=self.obf_universal(node.for_in)) elif m.matches(node, m.CompFor()): node = cst.ensure_type(node, cst.CompFor) new_ifs = [] node = node.with_changes(target=self.obf_universal(node.target)) node = node.with_changes(iter=self.obf_universal(node.iter)) for el in node.ifs: new_ifs.append(self.obf_universal(el)) node = node.with_changes(ifs=new_ifs) elif m.matches(node, m.CompIf()): node = cst.ensure_type(node, cst.CompIf) node = node.with_changes(test=self.obf_universal(node.test)) elif m.matches(node, m.Integer() | m.Float() | m.SimpleString()): pass else: pass # print(node) return node
def _handle_suppression_comment( self, parent_node: cst.CSTNode, parent_attribute_name: str, index: int, local_supp_comment: SuppressionComment, comment_physical_line: int, ) -> None: ignored_rules = local_supp_comment.ignored_rules # Just a check for the type-checker - it should never actually be AllRulesType # because we don't support that in lint-fixme/lint-ignore comments. # TODO: We can remove the AllRulesType check once we deprecate noqa. if isinstance(ignored_rules, AllRulesType): return # First find the suppressed rules that were included in the lint run. If a rule was not included # in this run, we CANNOT know for sure that this lint suppression is unused. ignored_rules_that_ran = {ig for ig in ignored_rules if ig in self.rule_names} if not ignored_rules_that_ran: return lines_span = len(local_supp_comment.tokens) unneeded_codes = _get_unused_codes_in_comment( local_supp_comment, ignored_rules_that_ran ) if unneeded_codes: new_comment_lines = _compose_new_comment( local_supp_comment, unneeded_codes, comment_physical_line ) if not new_comment_lines: # If we're here, all of the codes in this suppression refer to rules that ran, and # none of comment's codes suppress anything, so we report this comment and offer to remove it. new_parent_attribute_value = _modify_parent_attribute( parent_node, parent_attribute_name, index, index + lines_span, [] ) self.report( parent_node, message=UNUSED_SUPPRESSION_COMMENT_MESSAGE, replacement=parent_node.with_changes( **{parent_attribute_name: new_parent_attribute_value} ), ) else: node_to_replace = getattr(parent_node, parent_attribute_name)[index] replacement_emptyline_nodes = [ cst.EmptyLine( indent=node_to_replace.indent, whitespace=node_to_replace.whitespace, comment=cst.Comment(line), ) for line in new_comment_lines ] new_parent_attribute_value: List[ cst.EmptyLine ] = _modify_parent_attribute( parent_node, parent_attribute_name, index, index + lines_span, replacement_emptyline_nodes, ) self.report( parent_node, message=UNUSED_SUPPRESSION_CODES_IN_COMMENT_MESSAGE.format( lint_codes="`, `".join(uc for uc in unneeded_codes) ), replacement=parent_node.with_changes( **{parent_attribute_name: new_parent_attribute_value} ), )