Problem
AndroidBuildToolsVersion.TryParse wraps its parse logic in a try/catch (Exception) whose body is empty and undocumented. This bare catch silently swallows every exception type — including genuinely unexpected ones (e.g. a NullReferenceException introduced by a future bug in the parse path) — making real failures invisible and indistinguishable from an expected "bad version string" result. The catch block has no comment, so it also reads like an accidental swallow.
The expected-failure exception set here is small and knowable, so this is the textbook case for narrowing to the specific expected types (per the error-handling guidance).
Location
- File(s):
src/Xamarin.AndroidTools/AndroidBuildToolsVersion.cs
- Line(s): 47–51 (the
try / catch (Exception) in TryParse)
Current Code
public static bool TryParse (string input, out AndroidBuildToolsVersion result)
{
result = null;
if (String.IsNullOrEmpty (input))
return false;
try {
result = ParseInternal (input);
return true;
} catch (Exception) {
}
return false;
}
Suggested Fix
Narrow the catch (Exception) to the specific exception types the parse path can actually throw, each with a short comment. The return false behavior on parse failure is preserved exactly:
public static bool TryParse (string input, out AndroidBuildToolsVersion result)
{
result = null;
if (String.IsNullOrEmpty (input))
return false;
try {
result = ParseInternal (input);
return true;
} catch (FormatException) {
// Non-numeric version component
} catch (OverflowException) {
// Version component outside the range of Int32
} catch (ArgumentException) {
// Negative or otherwise invalid version component (Version constructor)
}
return false;
}
Why these three types are exhaustive
ParseInternal (input) (same file) only calls input.Trim (), string.IndexOf (' '), guarded Substring calls (guarded by index > 0, so they cannot throw), and MonoDroidSdk.ParseVersion (versionText). MonoDroidSdk.ParseVersion (src/Xamarin.AndroidTools/MonoDroidSdk.cs:318) does:
var split = versionString.Trim ().Split ('.');
int major = int.Parse (split [0]);
int minor = split.Length > 1 ? int.Parse (split [1]) : 0;
int build = split.Length > 2 ? int.Parse (split [2]) : 0;
return new Version (major, minor, build);
int.Parse (string) throws FormatException (non-numeric) or OverflowException (out of Int32 range). split [0] is never null (string.Split never yields null elements), so ArgumentNullException is not reachable.
new Version (major, minor, build) throws ArgumentOutOfRangeException (a subclass of ArgumentException) when a component is negative (e.g. "-1.0").
Catching FormatException, OverflowException, and ArgumentException therefore covers every failure the current implementation can produce for any input, so the change is behavior-preserving for all real version strings. Catching ArgumentException (the base type) rather than only ArgumentOutOfRangeException is intentional defensiveness.
Guidelines
- Follow Mono formatting style: tabs, one space before
( and [.
- The owning project
src/Xamarin.AndroidTools/Xamarin.AndroidTools.csproj targets netstandard2.0 (LangVersion 12). FormatException, OverflowException, ArgumentException, and multiple catch clauses are all available there — do not introduce newer APIs.
- Do not change the method signature or the
return false semantics: TryParse must still return false (never throw) for malformed version strings.
- Reference: [
try-catch (C#)]((learn.microsoft.com/redacted)
Acceptance Criteria
Fix-finder metadata
- Script:
07-error-handling
- Score:
27/30 (actionability: 10, safety: 7, scope: 10)
Generated by Nightly Fix Finder · 1.2K AIC · ⌖ 71.4 AIC · ⊞ 40.4K · ◷
Problem
AndroidBuildToolsVersion.TryParsewraps its parse logic in atry/catch (Exception)whose body is empty and undocumented. This bare catch silently swallows every exception type — including genuinely unexpected ones (e.g. aNullReferenceExceptionintroduced by a future bug in the parse path) — making real failures invisible and indistinguishable from an expected "bad version string" result. Thecatchblock has no comment, so it also reads like an accidental swallow.The expected-failure exception set here is small and knowable, so this is the textbook case for narrowing to the specific expected types (per the error-handling guidance).
Location
src/Xamarin.AndroidTools/AndroidBuildToolsVersion.cstry/catch (Exception)inTryParse)Current Code
Suggested Fix
Narrow the
catch (Exception)to the specific exception types the parse path can actually throw, each with a short comment. Thereturn falsebehavior on parse failure is preserved exactly:Why these three types are exhaustive
ParseInternal (input)(same file) only callsinput.Trim (),string.IndexOf (' '), guardedSubstringcalls (guarded byindex > 0, so they cannot throw), andMonoDroidSdk.ParseVersion (versionText).MonoDroidSdk.ParseVersion(src/Xamarin.AndroidTools/MonoDroidSdk.cs:318) does:int.Parse (string)throwsFormatException(non-numeric) orOverflowException(out ofInt32range).split [0]is nevernull(string.Splitnever yields null elements), soArgumentNullExceptionis not reachable.new Version (major, minor, build)throwsArgumentOutOfRangeException(a subclass ofArgumentException) when a component is negative (e.g."-1.0").Catching
FormatException,OverflowException, andArgumentExceptiontherefore covers every failure the current implementation can produce for any input, so the change is behavior-preserving for all real version strings. CatchingArgumentException(the base type) rather than onlyArgumentOutOfRangeExceptionis intentional defensiveness.Guidelines
(and[.src/Xamarin.AndroidTools/Xamarin.AndroidTools.csprojtargetsnetstandard2.0(LangVersion 12).FormatException,OverflowException,ArgumentException, and multiplecatchclauses are all available there — do not introduce newer APIs.return falsesemantics:TryParsemust still returnfalse(never throw) for malformed version strings.try-catch(C#)]((learn.microsoft.com/redacted)Acceptance Criteria
catch (Exception)inTryParseis replaced withcatch (FormatException),catch (OverflowException), andcatch (ArgumentException)clauses, each with an explanatory commentTryParsestill returnsfalse(does not throw) for all malformed version strings such as"abc","1.x","-1.0", and"99999999999999999999"Fix-finder metadata
07-error-handling27/30(actionability:10, safety:7, scope:10)