If you consider any form of "confusing" the compiler to be an invalid solution, you're probably going to have to live with the warning. (In my book, if you asking how to get rid of a warning, it's unwise to look a gift horse in the mouth and say something is invalid just because it doesn't look like you'd expect.)
The answers that work at runtime involve masking the operation that's happening with dynamic dispatch so the compiler doesn't complain about the deprecated call. If you don't like that approach, you can turn off "Warn About Deprecated Functions" in your Xcode project or target settings, but that's generally a bad idea. You want to know about deprecated APIs, but in this case you want to use it without warning. There are easy and hard ways to do this, and odds are you'd consider all of them "invalid" in some form, but that doesn't prevent them from being effective, even correct. ;-)
One possible way to avoid the warnings yet still select at runtime is to use objc_msgSend() directly:
objc_msgSend(fileManager, @selector(removeFileAtPath:error:), downloadDir, nil];
Code should say what it means. If it doesn't, then the API, the language, or the programmer is broken. If the compiler is warning when the programmer is confident that his code is correct, then either the compiler needs to learn about that situation and ignore it automatically, or it needs to support a syntax that lets the it ignore that particular warning on that line (like the syntax I posted that doesn't work in the Clang that ships with Xcode). Anything else is a hack.
I applaud your idealism, but sometimes you have to compromise. The code I've posted, and that others have as well, does say what it means. I believe you mean that the compiler should be able to divine your intent based purely on code, without jumping through hoops. That's nice and all, but how do you propose the compiler should know that you'll only call a given method on runtime where it's not deprecated? Without additional semantic information, it really can't. Syntax that can communicate such meaning would be fantastic, but until it exists, calling everything else a hack is unproductive.
Sometimes writing code that isn't crystal clear to the casual observer is unavoidable. This is a perfect time to add a clarifying comment if necessary. Using either performSelector:withObject:withObject: or objc_msgSend() are minimally disruptive approaches that still communicate your meaning and avoid the warning. Java has annotations to suppress warnings, but C does not. Bummer, but life goes on. I'm not defending the current state of things as how it ought to be, just suggesting that if you're going to actually use the language, it's more effective to embrace its quirks than be elitist.
To answer your question: the compiler already has a list of deprecated methods, why not give it a list of alternative methods as well? Then, if it sees a deprecated call, it could look to see if it is inside the else branch of a conditional that tests [<OBJECT> respondsToSelector:@selector(<ALTERNATIVE>))].
I understand your viewpoint, yet I think these answers are about as good as you can expect for now. The deprecated/alternative method approach is an interesting idea, but one that would require fundamental changes to the syntax and semantics of marking code as deprecated. I prefer the safety of compiler checking whenever possible, and avoid casting as id just to avoid warnings. However, spurious warnings tend to distract me more than an occasional hack, particularly when I have unit tests in place to verify that the code work correctly. :-) FWIW, I agree about per-line warning suppression.