def leave_SubscriptElement(self, original_node: cst.SubscriptElement, updated_node: cst.SubscriptElement): if self.type_annot_visited and self.parametric_type_annot_visited: if match.matches( original_node, match.SubscriptElement(slice=match.Index( value=match.Subscript()))): q_name, _ = self.__get_qualified_name( original_node.slice.value.value) if q_name is not None: return updated_node.with_changes(slice=cst.Index( value=cst.Subscript( value=self.__name2annotation(q_name).annotation, slice=updated_node.slice.value.slice))) elif match.matches( original_node, match.SubscriptElement(slice=match.Index( value=match.Ellipsis()))): # TODO: Should the original node be returned?! return updated_node.with_changes(slice=cst.Index( value=cst.Ellipsis())) elif match.matches( original_node, match.SubscriptElement(slice=match.Index( value=match.SimpleString(value=match.DoNotCare())))): return updated_node.with_changes(slice=cst.Index( value=updated_node.slice.value)) elif match.matches( original_node, match.SubscriptElement(slice=match.Index(value=match.Name( value='None')))): return original_node elif match.matches( original_node, match.SubscriptElement(slice=match.Index( value=match.List()))): return updated_node.with_changes(slice=cst.Index( value=updated_node.slice.value)) else: q_name, _ = self.__get_qualified_name( original_node.slice.value) if q_name is not None: return updated_node.with_changes(slice=cst.Index( value=self.__name2annotation(q_name).annotation)) return original_node
def contains_union_with_none(self, node: cst.Annotation) -> bool: return m.matches( node, m.Annotation( m.Subscript( value=m.Name("Union"), slice=m.OneOf( [ m.SubscriptElement(m.Index()), m.SubscriptElement(m.Index(m.Name("None"))), ], [ m.SubscriptElement(m.Index(m.Name("None"))), m.SubscriptElement(m.Index()), ], ), )), )
def leave_Annotation(self, original_node: cst.Annotation) -> None: if self.contains_union_with_none(original_node): scope = self.get_metadata(cst.metadata.ScopeProvider, original_node, None) nones = 0 indexes = [] replacement = None if scope is not None and "Optional" in scope: for s in cst.ensure_type(original_node.annotation, cst.Subscript).slice: if m.matches(s, m.SubscriptElement(m.Index(m.Name("None")))): nones += 1 else: indexes.append(s.slice) if not (nones > 1) and len(indexes) == 1: replacement = original_node.with_changes( annotation=cst.Subscript( value=cst.Name("Optional"), slice=(cst.SubscriptElement(indexes[0]),), ) ) # TODO(T57106602) refactor lint replacement once extract exists self.report(original_node, replacement=replacement)
def _is_awaitable_callable(annotation: str) -> bool: if not (annotation.startswith("typing.Callable") or annotation.startswith("typing.ClassMethod") or annotation.startswith("StaticMethod")): # Exit early if this is not even a `typing.Callable` annotation. return False try: # Wrap this in a try-except since the type annotation may not be parse-able as a module. # If it is not parse-able, we know it's not what we are looking for anyway, so return `False`. parsed_ann = cst.parse_module(annotation) except Exception: return False # If passed annotation does not match the expected annotation structure for a `typing.Callable` with # typing.Coroutine as the return type, matched_callable_ann will simply be `None`. # The expected structure of an awaitable callable annotation from Pyre is: typing.Callable()[[...], typing.Coroutine[...]] matched_callable_ann: Optional[Dict[str, Union[ Sequence[cst.CSTNode], cst.CSTNode]]] = m.extract( parsed_ann, m.Module(body=[ m.SimpleStatementLine(body=[ m.Expr(value=m.Subscript(slice=[ m.SubscriptElement(), m.SubscriptElement(slice=m.Index(value=m.Subscript( value=m.SaveMatchedNode( m.Attribute(), "base_return_type", )))), ], )) ]), ]), ) if (matched_callable_ann is not None and "base_return_type" in matched_callable_ann): base_return_type = get_full_name_for_node( cst.ensure_type(matched_callable_ann["base_return_type"], cst.CSTNode)) return (base_return_type is not None and base_return_type == "typing.Coroutine") return False
def obf_slice(self, sub_slice: Union[cst.Index, cst.Slice, List[cst.SubscriptElement]]): if m.matches(sub_slice, m.Index()): sub_slice = self.obf_universal(sub_slice.value) elif m.matches(sub_slice, m.Slice()): sub_slice = cst.ensure_type(sub_slice, cst.Slice) sub_slice = sub_slice.with_changes( lower=self.obf_universal(sub_slice.lower), upper=self.obf_universal(sub_slice.upper), step=self.obf_universal(sub_slice.step)) elif m.matches(sub_slice, m.SubscriptElement()): sub_slice = cst.ensure_type(sub_slice, cst.SubscriptElement) sub_slice = sub_slice.with_changes( slice=self.obf_universal(sub_slice.slice)) else: pass # print(f"NOT IMPLEMENTED obf_slice") return sub_slice
def _deprecated_visitor(self, node: cst.ExtSlice) -> None: if m.matches(node, m.ExtSlice(m.Index(m.Integer("2")))): self.calls.add("called")