def select_nodes_recursively(self, spec: SelectionSpec) -> Tuple[Set[UniqueId], Set[UniqueId]]:
        """If the spec is a composite spec (a union, difference, or intersection),
        recurse into its selections and combine them. If the spec is a concrete
        selection criteria, resolve that using the given graph.
        """
        if isinstance(spec, SelectionCriteria):
            direct_nodes, indirect_nodes = self.get_nodes_from_criteria(spec)
        else:
            bundles = [self.select_nodes_recursively(component) for component in spec]

            direct_sets = []
            indirect_sets = []

            for direct, indirect in bundles:
                direct_sets.append(direct)
                indirect_sets.append(direct | indirect)

            initial_direct = spec.combined(direct_sets)
            indirect_nodes = spec.combined(indirect_sets)

            direct_nodes = self.incorporate_indirect_nodes(
                initial_direct, indirect_nodes, spec.indirect_selection
            )

            if spec.expect_exists and len(direct_nodes) == 0:
                warn_or_error(NoNodesForSelectionCriteria(spec_raw=str(spec.raw)))

        return direct_nodes, indirect_nodes
If the spec is a composite spec (a union, difference, or intersection),
        recurse into its selections and combine them. If the spec is a concrete
        selection criteria, resolve that using the given graph.
        
core/dbt/graph/selector.py