diff --git a/source/dub/dependency.d b/source/dub/dependency.d index 301e339..e894e4e 100644 --- a/source/dub/dependency.d +++ b/source/dub/dependency.d @@ -316,7 +316,7 @@ These methods are suitable for equality comparisons, as well as for using `Dependency` as a key in hash or tree maps. */ - bool opEquals(const Dependency o) + bool opEquals(scope const Dependency o) const { // TODO(mdondorff): Check if not comparing the path is correct for all clients. return this.m_range == o.m_range @@ -324,7 +324,7 @@ } /// ditto - int opCmp(const Dependency o) + int opCmp(scope const Dependency o) const { if (auto result = this.m_range.opCmp(o.m_range)) return result; @@ -683,7 +683,6 @@ Semantic Versioning Specification v2.0.0 at http://semver.org/). */ struct Version { -@safe: private { static immutable MAX_VERS = "99999.0.0"; static immutable UNKNOWN_VERS = "unknown"; @@ -699,7 +698,7 @@ /** Constructs a new `Version` from its string representation. */ - this(string vers) + this(string vers) @safe pure { enforce(vers.length > 1, "Version strings must not be empty."); if (vers[0] != branchPrefix && !vers.isGitHash && vers.ptr !is UNKNOWN_VERS.ptr) @@ -712,35 +711,52 @@ This method is equivalent to calling the constructor and is used as an endpoint for the serialization framework. */ - static Version fromString(string vers) { return Version(vers); } + static Version fromString(string vers) @safe pure { return Version(vers); } - bool opEquals(const Version oth) const { return opCmp(oth) == 0; } + bool opEquals(scope const Version oth) const scope @safe pure + { + return opCmp(oth) == 0; + } /// Tests if this represents a hash instead of a version. - @property bool isSCM() const { return m_version.isGitHash; } + @property bool isSCM() const scope @safe pure nothrow @nogc + { + return m_version.isGitHash; + } /// Tests if this represents a branch instead of a version. - @property bool isBranch() const { return m_version.length > 0 && m_version[0] == branchPrefix; } + @property bool isBranch() const scope @safe pure nothrow @nogc + { + return m_version.length > 0 && m_version[0] == branchPrefix; + } /// Tests if this represents the master branch "~master". - @property bool isMaster() const { return m_version == masterString; } + @property bool isMaster() const scope @safe pure nothrow @nogc + { + return m_version == masterString; + } /** Tests if this represents a pre-release version. Note that branches are always considered pre-release versions. */ - @property bool isPreRelease() const { + @property bool isPreRelease() const scope @safe pure nothrow @nogc + { if (isBranch || isSCM) return true; return isPreReleaseVersion(m_version); } /// Tests if this represents the special unknown version constant. - @property bool isUnknown() const { return m_version == UNKNOWN_VERS; } + @property bool isUnknown() const scope @safe pure nothrow @nogc + { + return m_version == UNKNOWN_VERS; + } /** Tests two versions for equality, according to the selected match mode. */ bool matches(Version other, VersionMatchMode mode = VersionMatchMode.standard) - const { + const scope @safe pure + { if (this != other) return false; @@ -757,8 +773,8 @@ compared using SemVer semantics, while branches are compared lexicographically. */ - int opCmp(ref const Version other) - const { + int opCmp(scope ref const Version other) const scope @safe pure + { if (isUnknown || other.isUnknown) { throw new Exception("Can't compare unknown versions! (this: %s, other: %s)".format(this, other)); } @@ -781,10 +797,16 @@ return compareVersions(m_version, other.m_version); } /// ditto - int opCmp(const Version other) const { return opCmp(other); } + int opCmp(scope const Version other) const scope @safe pure + { + return this.opCmp(other); + } /// Returns the string representation of the version/branch. - string toString() const { return m_version; } + string toString() const return scope @safe pure nothrow @nogc + { + return m_version; + } } /// A range of versions that are acceptable @@ -796,7 +818,7 @@ bool m_inclusiveB = true; // B comparison < (true) or <= (false) /// - public int opCmp (const VersionRange o) const @safe + public int opCmp (scope const VersionRange o) const scope @safe { if (m_inclusiveA != o.m_inclusiveA) return m_inclusiveA < o.m_inclusiveA ? -1 : 1; if (m_inclusiveB != o.m_inclusiveB) return m_inclusiveB < o.m_inclusiveB ? -1 : 1; diff --git a/source/dub/semver.d b/source/dub/semver.d index c04a9c5..674b324 100644 --- a/source/dub/semver.d +++ b/source/dub/semver.d @@ -24,7 +24,7 @@ /** Validates a version string according to the SemVer specification. */ -bool isValidVersion(string ver) +bool isValidVersion(scope string ver) pure @nogc { // NOTE: this is not by spec, but to ensure sane input if (ver.length > 256) return false; @@ -101,7 +101,7 @@ /** Determines if a given valid SemVer version has a pre-release suffix. */ -bool isPreReleaseVersion(string ver) pure @nogc +bool isPreReleaseVersion(scope string ver) pure @nogc nothrow in { assert(isValidVersion(ver)); } do { foreach (i; 0 .. 2) { @@ -134,16 +134,30 @@ Returns a negative number if `a` is a lower version than `b`, `0` if they are equal, and a positive number otherwise. */ -int compareVersions(string a, string b) +int compareVersions(scope string a, scope string b) pure @nogc { + // This needs to be a nested function as we can't pass local scope + // variables by `ref` + int compareNumber() @safe pure @nogc { + int res = 0; + while (true) { + if (a[0] != b[0] && res == 0) res = a[0] - b[0]; + a = a[1 .. $]; b = b[1 .. $]; + auto aempty = !a.length || (a[0] < '0' || a[0] > '9'); + auto bempty = !b.length || (b[0] < '0' || b[0] > '9'); + if (aempty != bempty) return bempty - aempty; + if (aempty) return res; + } + } + // compare a.b.c numerically - if (auto ret = compareNumber(a, b)) return ret; + if (auto ret = compareNumber()) return ret; assert(a[0] == '.' && b[0] == '.'); a = a[1 .. $]; b = b[1 .. $]; - if (auto ret = compareNumber(a, b)) return ret; + if (auto ret = compareNumber()) return ret; assert(a[0] == '.' && b[0] == '.'); a = a[1 .. $]; b = b[1 .. $]; - if (auto ret = compareNumber(a, b)) return ret; + if (auto ret = compareNumber()) return ret; // give precedence to non-prerelease versions bool apre = a.length > 0 && a[0] == '-'; @@ -314,7 +328,7 @@ assert("1.0.0-pre.release+meta" == expandVersion("1-pre.release+meta")); } -private int compareIdentifier(ref string a, ref string b) +private int compareIdentifier(scope ref string a, scope ref string b) pure @nogc { bool anumber = true; bool bnumber = true; @@ -344,20 +358,7 @@ } } -private int compareNumber(ref string a, ref string b) -pure @nogc { - int res = 0; - while (true) { - if (a[0] != b[0] && res == 0) res = a[0] - b[0]; - a = a[1 .. $]; b = b[1 .. $]; - auto aempty = !a.length || (a[0] < '0' || a[0] > '9'); - auto bempty = !b.length || (b[0] < '0' || b[0] > '9'); - if (aempty != bempty) return bempty - aempty; - if (aempty) return res; - } -} - -private bool isValidIdentifierChain(string str, bool allow_leading_zeros = false) +private bool isValidIdentifierChain(scope string str, bool allow_leading_zeros = false) pure @nogc { if (str.length == 0) return false; while (str.length) { @@ -370,7 +371,7 @@ return true; } -private bool isValidIdentifier(string str, bool allow_leading_zeros = false) +private bool isValidIdentifier(scope string str, bool allow_leading_zeros = false) pure @nogc { if (str.length < 1) return false; @@ -394,7 +395,7 @@ } private bool isValidNumber(string str) -pure @nogc { +pure @nogc nothrow { if (str.length < 1) return false; foreach (ch; str) if (ch < '0' || ch > '9') @@ -406,7 +407,7 @@ return true; } -private ptrdiff_t indexOfAny(string str, in char[] chars) +private ptrdiff_t indexOfAny(scope string str, in char[] chars) pure @nogc { ptrdiff_t ret = -1; foreach (ch; chars) {