diff options
author | chriseth <c@ethdev.com> | 2016-08-20 01:57:21 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2016-09-01 06:02:51 +0800 |
commit | 3c412ed2f63a58b27eeb00fe584b9378311b099f (patch) | |
tree | cba706f91c05658a8b1f8794ad21745ea5619e39 /libsolidity/analysis | |
parent | 52d9f897126394dcc7388277d4fbd3ef7b4df38a (diff) | |
download | dexon-solidity-3c412ed2f63a58b27eeb00fe584b9378311b099f.tar.gz dexon-solidity-3c412ed2f63a58b27eeb00fe584b9378311b099f.tar.zst dexon-solidity-3c412ed2f63a58b27eeb00fe584b9378311b099f.zip |
Version pragma.
Diffstat (limited to 'libsolidity/analysis')
-rw-r--r-- | libsolidity/analysis/SemVerHandler.cpp | 290 | ||||
-rw-r--r-- | libsolidity/analysis/SemVerHandler.h | 102 | ||||
-rw-r--r-- | libsolidity/analysis/SyntaxChecker.cpp | 53 | ||||
-rw-r--r-- | libsolidity/analysis/SyntaxChecker.h | 7 |
4 files changed, 449 insertions, 3 deletions
diff --git a/libsolidity/analysis/SemVerHandler.cpp b/libsolidity/analysis/SemVerHandler.cpp new file mode 100644 index 00000000..c7b212b2 --- /dev/null +++ b/libsolidity/analysis/SemVerHandler.cpp @@ -0,0 +1,290 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <chris@ethereum.org> + * @date 2016 + * Utilities to handle semantic versioning. + */ + +#include <libsolidity/analysis/SemVerHandler.h> +#include <functional> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +SemVerVersion::SemVerVersion(string const& _versionString) +{ + auto i = _versionString.begin(); + auto end = _versionString.end(); + + for (unsigned level = 0; level < 3; ++level) + { + unsigned v = 0; + for (; i != end && '0' <= *i && *i <= '9'; ++i) + v = v * 10 + (*i - '0'); + numbers[level] = v; + if (level < 2) + { + if (i == end || *i != '.') + throw SemVerError(); + else + ++i; + } + } + if (i != end && *i == '-') + { + auto prereleaseStart = ++i; + while (i != end && *i != '+') ++i; + prerelease = string(prereleaseStart, i); + } + if (i != end && *i == '+') + { + auto buildStart = ++i; + while (i != end) ++i; + build = string(buildStart, i); + } + if (i != end) + throw SemVerError(); +} + +bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _version) const +{ + if (prefix == Token::BitNot) + { + MatchComponent comp = *this; + + comp.prefix = Token::GreaterThanOrEqual; + if (!comp.matches(_version)) + return false; + + if (levelsPresent >= 2) + comp.levelsPresent = 2; + else + comp.levelsPresent = 1; + comp.prefix = Token::LessThanOrEqual; + return comp.matches(_version); + } + else if (prefix == Token::BitXor) + { + MatchComponent comp = *this; + + comp.prefix = Token::GreaterThanOrEqual; + if (!comp.matches(_version)) + return false; + + if (comp.version.numbers[0] == 0) + comp.levelsPresent = 2; + else + comp.levelsPresent = 1; + comp.prefix = Token::LessThanOrEqual; + return comp.matches(_version); + } + else + { + int cmp = 0; + bool didCompare = false; + for (unsigned i = 0; i < levelsPresent && cmp == 0; i++) + if (version.numbers[i] != unsigned(-1)) + { + didCompare = true; + cmp = _version.numbers[i] - version.numbers[i]; + } + if (cmp == 0 && !_version.prerelease.empty() && didCompare) + cmp = -1; + if (prefix == Token::Assign) + return cmp == 0; + else if (prefix == Token::LessThan) + return cmp < 0; + else if (prefix == Token::LessThanOrEqual) + return cmp <= 0; + else if (prefix == Token::GreaterThan) + return cmp > 0; + else if (prefix == Token::GreaterThanOrEqual) + return cmp >= 0; + else + solAssert(false, "Invalid SemVer expression"); + return false; + } +} + +bool SemVerMatchExpression::Conjunction::matches(SemVerVersion const& _version) const +{ + for (auto const& component: components) + if (!component.matches(_version)) + return false; + return true; +} + +bool SemVerMatchExpression::matches(SemVerVersion const& _version) const +{ + if (!isValid()) + return false; + for (auto const& range: m_disjunction) + if (range.matches(_version)) + return true; + return false; +} + +SemVerMatchExpression SemVerMatchExpressionParser::parse() +{ + reset(); + + try + { + while (true) + { + parseMatchExpression(); + if (m_pos >= m_tokens.size()) + break; + if (currentToken() != Token::Or) + throw SemVerError(); + nextToken(); + } + } + catch (SemVerError const&) + { + reset(); + } + + return m_expression; +} + + +void SemVerMatchExpressionParser::reset() +{ + m_expression = SemVerMatchExpression(); + m_pos = 0; + m_posInside = 0; +} + +void SemVerMatchExpressionParser::parseMatchExpression() +{ + // component - component (range) + // or component component* (conjunction) + + SemVerMatchExpression::Conjunction range; + range.components.push_back(parseMatchComponent()); + if (currentToken() == Token::Sub) + { + range.components[0].prefix = Token::GreaterThanOrEqual; + nextToken(); + range.components.push_back(parseMatchComponent()); + range.components[1].prefix = Token::LessThanOrEqual; + } + else + while (currentToken() != Token::Or && currentToken() != Token::Illegal) + range.components.push_back(parseMatchComponent()); + m_expression.m_disjunction.push_back(range); +} + +SemVerMatchExpression::MatchComponent SemVerMatchExpressionParser::parseMatchComponent() +{ + SemVerMatchExpression::MatchComponent component; + Token::Value token = currentToken(); + if ( + token == Token::BitXor || + token == Token::BitNot || + token == Token::LessThan || + token == Token::LessThanOrEqual|| + token == Token::GreaterThan || + token == Token::GreaterThanOrEqual || + token == Token::Assign + ) + { + component.prefix = token; + nextToken(); + } + else + component.prefix = Token::Assign; + + component.levelsPresent = 0; + while (component.levelsPresent < 3) + { + component.version.numbers[component.levelsPresent] = parseVersionPart(); + component.levelsPresent++; + if (currentChar() == '.') + nextChar(); + else + break; + } + // TODO we do not support pre and build version qualifiers for now in match expressions + // (but we do support them in the actual versions) + return component; +} + +unsigned SemVerMatchExpressionParser::parseVersionPart() +{ + auto startPos = m_pos; + char c = currentChar(); + nextChar(); + if (c == 'x' || c == 'X' || c == '*') + return unsigned(-1); + else if (c == '0') + return 0; + else if ('1' <= c && c <= '9') + { + unsigned v = c - '0'; + // If we skip to the next token, the current number is terminated. + while (m_pos == startPos && '0' <= currentChar() && currentChar() <= '9') + { + c = currentChar(); + if (v * 10 < v || v * 10 + (c - '0') < v * 10) + throw SemVerError(); + v = v * 10 + c - '0'; + nextChar(); + } + return v; + } + else + throw SemVerError(); +} + +char SemVerMatchExpressionParser::currentChar() const +{ + if (m_pos >= m_literals.size()) + return char(-1); + if (m_posInside >= m_literals[m_pos].size()) + return char(-1); + return m_literals[m_pos][m_posInside]; +} + +char SemVerMatchExpressionParser::nextChar() +{ + if (m_pos < m_literals.size()) + { + if (m_posInside + 1 >= m_literals[m_pos].size()) + nextToken(); + else + ++m_posInside; + } + return currentChar(); +} + +Token::Value SemVerMatchExpressionParser::currentToken() const +{ + if (m_pos < m_tokens.size()) + return m_tokens[m_pos]; + else + return Token::Illegal; +} + +void SemVerMatchExpressionParser::nextToken() +{ + ++m_pos; + m_posInside = 0; +} diff --git a/libsolidity/analysis/SemVerHandler.h b/libsolidity/analysis/SemVerHandler.h new file mode 100644 index 00000000..3c110b19 --- /dev/null +++ b/libsolidity/analysis/SemVerHandler.h @@ -0,0 +1,102 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <chris@ethereum.org> + * @date 2016 + * Utilities to handle semantic versioning. + */ + +#pragma once + +#include <vector> +#include <libsolidity/parsing/Token.h> + +namespace dev +{ +namespace solidity +{ + +class SemVerError: dev::Exception +{ +}; + +struct SemVerVersion +{ + unsigned numbers[3]; + std::string prerelease; + std::string build; + + explicit SemVerVersion(std::string const& _versionString = "0.0.0"); +}; + +struct SemVerMatchExpression +{ + bool matches(SemVerVersion const& _version) const; + + bool isValid() const { return !m_disjunction.empty(); } + + struct MatchComponent + { + /// Prefix from < > <= >= ~ ^ + Token::Value prefix = Token::Illegal; + /// Version, where unsigned(-1) in major, minor or patch denotes '*', 'x' or 'X' + SemVerVersion version; + /// Whether we have 1, 1.2 or 1.2.4 + unsigned levelsPresent = 1; + bool matches(SemVerVersion const& _version) const; + }; + + struct Conjunction + { + std::vector<MatchComponent> components; + bool matches(SemVerVersion const& _version) const; + }; + + std::vector<Conjunction> m_disjunction; +}; + +class SemVerMatchExpressionParser +{ +public: + SemVerMatchExpressionParser(std::vector<Token::Value> const& _tokens, std::vector<std::string> const& _literals): + m_tokens(_tokens), m_literals(_literals) + {} + SemVerMatchExpression parse(); + +private: + void reset(); + + void parseMatchExpression(); + SemVerMatchExpression::MatchComponent parseMatchComponent(); + unsigned parseVersionPart(); + + char currentChar() const; + char nextChar(); + Token::Value currentToken() const; + void nextToken(); + + std::vector<Token::Value> m_tokens; + std::vector<std::string> m_literals; + + unsigned m_pos = 0; + unsigned m_posInside = 0; + + SemVerMatchExpression m_expression; +}; + +} +} diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 593f2f69..d44fceb4 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -15,9 +15,11 @@ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. */ +#include <libsolidity/analysis/SyntaxChecker.h> #include <memory> #include <libsolidity/ast/AST.h> -#include <libsolidity/analysis/SyntaxChecker.h> +#include <libsolidity/analysis/SemVerHandler.h> +#include <libsolidity/interface/Version.h> using namespace std; using namespace dev; @@ -27,7 +29,7 @@ using namespace dev::solidity; bool SyntaxChecker::checkSyntax(SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); - return m_errors.empty(); + return Error::containsOnlyWarnings(m_errors); } void SyntaxChecker::syntaxError(SourceLocation const& _location, std::string const& _description) @@ -40,6 +42,51 @@ void SyntaxChecker::syntaxError(SourceLocation const& _location, std::string con m_errors.push_back(err); } +bool SyntaxChecker::visit(SourceUnit const&) +{ + m_versionPragmaFound = false; + return true; +} + +void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) +{ + if (!m_versionPragmaFound) + { + auto err = make_shared<Error>(Error::Type::Warning); + *err << + errinfo_sourceLocation(_sourceUnit.location()) << + errinfo_comment( + string("Source file does not specify required compiler version! ") + + string("Consider adding \"pragma solidity ^") + VersionNumber + string(";\".") + ); + m_errors.push_back(err); + } +} + +bool SyntaxChecker::visit(PragmaDirective const& _pragma) +{ + solAssert(!_pragma.tokens().empty(), ""); + solAssert(_pragma.tokens().size() == _pragma.literals().size(), ""); + if (_pragma.tokens()[0] != Token::Identifier && _pragma.literals()[0] != "solidity") + syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); + else + { + vector<Token::Value> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end()); + vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end()); + SemVerMatchExpressionParser parser(tokens, literals); + auto matchExpression = parser.parse(); + SemVerVersion currentVersion{string(VersionNumber)}; + if (!matchExpression.matches(currentVersion)) + syntaxError( + _pragma.location(), + "Source file requires different compiler version (current compiler is " + + string(VersionNumber) + ")." + ); + m_versionPragmaFound = true; + } + return true; +} + bool SyntaxChecker::visit(ModifierDefinition const&) { m_placeholderFound = false; @@ -91,7 +138,7 @@ bool SyntaxChecker::visit(Break const& _breakStatement) return true; } -bool SyntaxChecker::visit(const PlaceholderStatement&) +bool SyntaxChecker::visit(PlaceholderStatement const&) { m_placeholderFound = true; return true; diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index 3198ffd0..ac8ed872 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -45,6 +45,10 @@ private: /// Adds a new error to the list of errors. void syntaxError(SourceLocation const& _location, std::string const& _description); + virtual bool visit(SourceUnit const& _sourceUnit) override; + virtual void endVisit(SourceUnit const& _sourceUnit) override; + virtual bool visit(PragmaDirective const& _pragma) override; + virtual bool visit(ModifierDefinition const& _modifier) override; virtual void endVisit(ModifierDefinition const& _modifier) override; @@ -63,6 +67,9 @@ private: /// Flag that indicates whether a function modifier actually contains '_'. bool m_placeholderFound = false; + /// Flag that indicates whether some version pragma was present. + bool m_versionPragmaFound = false; + int m_inLoopDepth = 0; }; |