This is an extremely hard problem, both in practice and in theory. We're thinking hard about ways to either prevent or isolate side effects for precisely your scenarios -- memoization, automatic parallelization, and so on -- but it's difficult and we are still far from a workable solution for C#. So, no promises. (Consider switching to Haskell if you really want to eliminate side effects.)
Unfortunately, even if a miracle happened and you found a way to prevent memoization of methods with side effects, you've still got some big problems. Consider the following:
1) What if you memoize a function that is itself calling a memoized function? That's a good situation to be in, right? You want to be able to compose memoized functions. But memoization has a side effect: it adds data to a cache! So immediately you have a meta-problem: you want to tame side effects, but only "bad" side effects. The "good" ones you want to encourage, the bad ones you want to prevent, and it is hard to tell them apart.
2) What are you going to do about exceptions? Can you memoize a method which throws an exception? If so, does it always throw the same exception, or does it throw a new exception every time? If the former, how are you going to do it? If the latter, now you have a memoized function which has two different results on two different calls because two different exceptions are thrown. Exceptions can be seen as a side effect; it is hard to tame exceptions.
3) What are you going to do about methods which do not have a side effect but are nevertheless impure methods? Suppose you have a method GetCurrentTime(). That doesn't have a side effect; nothing is mutated by the call. But this is still not a candidate for memoization because any two calls are required to produce different results. You don't need a side-effects detector, you need a purity detector.
I think your best bet is to solve the human problem via education and code reviews, rather than trying to solve the hard technical problem.