TreeUtils { @CheckForNull public static Tree firstAncestorOfKind(Tree tree, Kind... kinds) { return firstAncestor(tree, t -> t.is(kinds)); } private  TreeUtils(); @CheckForNull static Tree firstAncestor(Tree tree, Predicate<Tree> predicate); @CheckForNull static Tree firstAncestorOfKind(Tree tree, Kind... kinds); static List<Token> tokens(Tree tree); static List<Token> nonWhitespaceTokens(Tree tree); static boolean hasDescendant(Tree tree, Predicate<Tree> predicate); static Stream<Expression> flattenTuples(Expression expression); static Optional<Symbol> getSymbolFromTree(@Nullable Tree tree); @CheckForNull static ClassSymbol getClassSymbolFromDef(@Nullable ClassDef classDef); @CheckForNull static FunctionSymbol getFunctionSymbolFromDef(@Nullable FunctionDef functionDef); static List<Parameter> nonTupleParameters(FunctionDef functionDef); static List<Parameter> positionalParameters(FunctionDef functionDef); static List<FunctionDef> topLevelFunctionDefs(ClassDef classDef); @CheckForNull static RegularArgument argumentByKeyword(String keyword, List<Argument> arguments); @CheckForNull static RegularArgument nthArgumentOrKeyword(int argPosition, String keyword, List<Argument> arguments); static boolean isBooleanLiteral(Tree tree); static String nameFromExpression(Expression expression); @CheckForNull static LocationInFile locationInFile(Tree tree, @Nullable String fileId);  }
@Test public void first_ancestor_of_kind() { String code = "" + "class A:\n" + " def foo(): pass"; FileInput root = parse(code); assertThat(TreeUtils.firstAncestorOfKind(root, Kind.CLASSDEF)).isNull(); ClassDef classDef = (ClassDef) root.statements().statements().get(0); assertThat(TreeUtils.firstAncestorOfKind(classDef, Kind.FILE_INPUT, Kind.CLASSDEF)).isEqualTo(root); FunctionDef funcDef = (FunctionDef) classDef.body().statements().get(0); assertThat(TreeUtils.firstAncestorOfKind(funcDef, Kind.FILE_INPUT)).isEqualTo(root); assertThat(TreeUtils.firstAncestorOfKind(funcDef, Kind.CLASSDEF)).isEqualTo(classDef); code = "" + "while True:\n" + " while True:\n" + " pass"; WhileStatement outerWhile = (WhileStatement) parse(code).statements().statements().get(0); WhileStatement innerWhile = (WhileStatement) outerWhile.body().statements().get(0); PassStatement passStatement = (PassStatement) innerWhile.body().statements().get(0); assertThat(TreeUtils.firstAncestorOfKind(passStatement, Kind.WHILE_STMT)).isEqualTo(innerWhile); }