Axis-aligned edges
Assuming each edge is parallel to either of x, y, z axes, it's trivial to look for U-patterns by taking an edge1 and calculating vector from one of its vertices to another, then taking a linked edge2 with a different vector, and then an edge3 linked to edge2 with a vector which is a reverse of edge1's vector. Then just pass the vertices of that pattern to a face constructor to ensure there's a face.
import bpy, bmesh
def main():
me = bpy.context.active_object.data
bm = bmesh.from_edit_mesh(me)
example = bm.faces[0] # copy settings from this face
for edge in bm.edges:
for verts in missing_faces(edge):
try:
bm.faces.new(verts, example)
except ValueError:
# you can drop try... if you decide to use `is_face_missing`
pass
bmesh.update_edit_mesh(me)
def missing_faces(start_edge):
for v2 in start_edge.verts:
v1 = start_edge.other_vert(v2)
vec1 = v2.co - v1.co
for neighbor_edge in (e for e in v2.link_edges if e is not start_edge):
v3 = neighbor_edge.other_vert(v2)
vec2 = v3.co - v2.co
if vec1 == vec2: # the edge continues away from v1
continue
for far_edge in (e for e in v3.link_edges if e is not neighbor_edge):
v4 = far_edge.other_vert(v3)
vec3 = v3.co - v4.co
if vec3 == vec1: # and is_face_missing(start_edge, far_edge):
# far_edge is parallel to start_edge
yield v1, v2, v3, v4
break
#def is_face_missing(edge1, edge2):
# for face in edge1.link_faces:
# if edge2 in face.edges:
# return False
# return True
main()

Generalized version
for each face A, take two opposite edges A1 and A2. Using loops instead of the edges directly (and link_loop_radial_next
), get connected faces to them. Iterate over A1 loop to find such face B, and over A2 loop to find such face C, that an edge B2 (opposite to A1=B1) and an edge C1 (opposite to A2=C2) have a connecting edge. If so, ensure there's a face by trying to create a new one.

import bpy, bmesh
def main():
me = bpy.context.active_object.data
bm = bmesh.from_edit_mesh(me)
for face in bm.faces:
for verts in traverse_face(face):
try:
bm.faces.new(verts)
except ValueError:
pass # face already exists here
bmesh.update_edit_mesh(me)
def traverse_face(face):
loop = face.loops[0]
for _ in range(2):
opposite_loop = loop.link_loop_next.link_loop_next
yield from traverse_loop_pair(loop, opposite_loop)
loop = loop.link_loop_next
def traverse_loop_pair(loop1, loop2):
cache = list(opposite_edges(loop2)) # cache repeatable inner loop
for edge1 in opposite_edges(loop1):
for edge2 in cache:
result = connection(edge1, edge2)
if result:
yield result
def opposite_edges(loop):
for _ in loop.edge.link_faces[1:]:
loop = loop.link_loop_radial_next
yield loop.link_loop_next.link_loop_next.edge
def connection(edge1, edge2):
e1_v1, e1_v2 = edge1.verts
for e2_v in edge2.verts:
for edge in e2_v.link_edges:
v = edge.other_vert(e2_v)
if v is e1_v1:
return e1_v2, v, e2_v, edge2.other_vert(e2_v)
if v is e1_v2:
return e1_v1, v, e2_v, edge2.other_vert(e2_v)
return None
main()