Another complementary strategy is to avoid Turing-complete constructs as much as possible, i.e. use DSLs with restricted semantics. This way, advanced semantic properties such as termination are provable.
[1] Program Analysis, An Appetizer. https://arxiv.org/pdf/2012.10086.pdf
There are many examples of programs whose halting behavior is not known (collatz conjecture for example) but many others where program analysis works just fine.
You can make the analyzer happy by adding a max iteration limit or a recursion depth limit or something to make sure it fails out rather than looping forever.
Which is probably a good idea anyway, if you’re running code that you can’t mathematically prove will always complete.
Usually, when people say “static analysis“ they accept unsoundness and use of heuristics. Otherwise, they call the tool a type checker or a verifier. Such tools may run into the theoretical issues you mentioned. For them, the solution is to change the program until it compiles in a reasonable amount of time.
I guess something related to this one way or another.
If the judge program should say terminates yes/no and the program given is `while True: continue`, I guess the argument is that in the finite case, you could in principle just enumerate all programs that don't terminate and identify them as such?
Then you treeshake the unreachable parts of that directed graph from the start state, and look for closed loops in what remains.