I improved the great code from Nessario a little bit to add more information to the output.
global $wpdb;
$category_slug=get_queried_object()->slug??'';
$attributes_query = $wpdb->prepare(
"SELECT t.name as taxonomy_name,tr.object_id, tt.taxonomy, tt.term_id, t.name, count(*) as count
FROM {$wpdb->prefix}term_relationships AS tr
JOIN {$wpdb->prefix}term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN {$wpdb->prefix}terms AS t ON tt.term_id = t.term_id
WHERE tr.object_id IN (
SELECT p.ID
FROM {$wpdb->prefix}posts AS p
JOIN {$wpdb->prefix}term_relationships AS tr ON p.ID = tr.object_id
JOIN {$wpdb->prefix}term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN {$wpdb->prefix}terms AS t ON tt.term_id = t.term_id
WHERE p.post_type = 'product'
AND p.post_status = 'publish'
AND tt.taxonomy = 'product_cat'
AND ('$category_slug'='' OR t.slug = '$category_slug')
)
AND tt.taxonomy LIKE %s
GROUP By tt.term_id",
'pa_%'
);
$terms=$wpdb->get_results($attributes_query);
$filters=array();
foreach($terms as $term){
if(!isset($filters[$term->taxonomy])){
$filters[$term->taxonomy]=array(
'name'=>wc_attribute_label($term->taxonomy),
'count'=>0,
'options'=>array()
);
}
$filters[$term->taxonomy]['count']+=$term->count;
$filters[$term->taxonomy]['options'][]=array(
'term_id'=>$term->term_id,
'name'=>$term->name,
'count'=>$term->count,
);
}
In the end you get an array with all used attributes, their used terms, and the product counts. This is ideal if you want to implement individual filters based on the current group.