diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp index cdd911af9a733..a98dfa02fc717 100644 --- a/clang/lib/Driver/ToolChains/Gnu.cpp +++ b/clang/lib/Driver/ToolChains/Gnu.cpp @@ -2007,45 +2007,72 @@ Generic_GCC::GCCVersion Generic_GCC::GCCVersion::Parse(StringRef VersionText) { std::pair First = VersionText.split('.'); std::pair Second = First.second.split('.'); - GCCVersion GoodVersion = {VersionText.str(), -1, -1, -1, "", "", ""}; - if (First.first.getAsInteger(10, GoodVersion.Major) || GoodVersion.Major < 0) - return BadVersion; - GoodVersion.MajorStr = First.first.str(); - if (First.second.empty()) - return GoodVersion; + StringRef MajorStr = First.first; StringRef MinorStr = Second.first; - if (Second.second.empty()) { - if (size_t EndNumber = MinorStr.find_first_not_of("0123456789")) { - GoodVersion.PatchSuffix = std::string(MinorStr.substr(EndNumber)); - MinorStr = MinorStr.slice(0, EndNumber); - } - } - if (MinorStr.getAsInteger(10, GoodVersion.Minor) || GoodVersion.Minor < 0) - return BadVersion; - GoodVersion.MinorStr = MinorStr.str(); + StringRef PatchStr = Second.second; - // First look for a number prefix and parse that if present. Otherwise just - // stash the entire patch string in the suffix, and leave the number - // unspecified. This covers versions strings such as: - // 5 (handled above) + GCCVersion GoodVersion = {VersionText.str(), -1, -1, -1, "", "", ""}; + + // Parse version number strings such as: + // 5 // 4.4 // 4.4-patched // 4.4.0 // 4.4.x // 4.4.2-rc4 // 4.4.x-patched - // And retains any patch number it finds. - StringRef PatchText = Second.second; - if (!PatchText.empty()) { - if (size_t EndNumber = PatchText.find_first_not_of("0123456789")) { - // Try to parse the number and any suffix. - if (PatchText.slice(0, EndNumber).getAsInteger(10, GoodVersion.Patch) || - GoodVersion.Patch < 0) - return BadVersion; - GoodVersion.PatchSuffix = std::string(PatchText.substr(EndNumber)); + // 10-win32 + // Split on '.', handle 1, 2 or 3 such segments. Each segment must contain + // purely a number, except for the last one, where a non-number suffix + // is stored in PatchSuffix. The third segment is allowed to not contain + // a number at all. + + auto TryParseLastNumber = [&](StringRef Segment, int &Number, + std::string &OutStr) -> bool { + // Look for a number prefix and parse that, and split out any trailing + // string into GoodVersion.PatchSuffix. + + if (size_t EndNumber = Segment.find_first_not_of("0123456789")) { + StringRef NumberStr = Segment.slice(0, EndNumber); + if (NumberStr.getAsInteger(10, Number) || Number < 0) + return false; + OutStr = NumberStr; + GoodVersion.PatchSuffix = Segment.substr(EndNumber); + return true; } + return false; + }; + auto TryParseNumber = [](StringRef Segment, int &Number) -> bool { + if (Segment.getAsInteger(10, Number) || Number < 0) + return false; + return true; + }; + + if (MinorStr.empty()) { + // If no minor string, major is the last segment + if (!TryParseLastNumber(MajorStr, GoodVersion.Major, GoodVersion.MajorStr)) + return BadVersion; + return GoodVersion; } + if (!TryParseNumber(MajorStr, GoodVersion.Major)) + return BadVersion; + GoodVersion.MajorStr = MajorStr; + + if (PatchStr.empty()) { + // If no patch string, minor is the last segment + if (!TryParseLastNumber(MinorStr, GoodVersion.Minor, GoodVersion.MinorStr)) + return BadVersion; + return GoodVersion; + } + + if (!TryParseNumber(MinorStr, GoodVersion.Minor)) + return BadVersion; + GoodVersion.MinorStr = MinorStr; + + // For the last segment, tolerate a missing number. + std::string DummyStr; + TryParseLastNumber(PatchStr, GoodVersion.Patch, DummyStr); return GoodVersion; } diff --git a/clang/unittests/Driver/GCCVersionTest.cpp b/clang/unittests/Driver/GCCVersionTest.cpp index 88c26dfe814e3..3158911fe5db9 100644 --- a/clang/unittests/Driver/GCCVersionTest.cpp +++ b/clang/unittests/Driver/GCCVersionTest.cpp @@ -39,6 +39,7 @@ const VersionParseTest TestCases[] = { {"4.4.2-rc4", 4, 4, 2, "4", "4", "-rc4"}, {"4.4.x-patched", 4, 4, -1, "4", "4", ""}, {"not-a-version", -1, -1, -1, "", "", ""}, + {"10-win32", 10, -1, -1, "10", "", "-win32"}, }; TEST(GCCVersionTest, Parse) {