mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add support for SPDX license identifiers.
This commit is contained in:
@@ -156,19 +156,26 @@ std::vector<T const*> ASTNode::filteredNodes(std::vector<ASTPointer<ASTNode>> co
|
||||
class SourceUnit: public ASTNode
|
||||
{
|
||||
public:
|
||||
SourceUnit(int64_t _id, SourceLocation const& _location, std::vector<ASTPointer<ASTNode>> _nodes):
|
||||
ASTNode(_id, _location), m_nodes(std::move(_nodes)) {}
|
||||
SourceUnit(
|
||||
int64_t _id,
|
||||
SourceLocation const& _location,
|
||||
std::optional<std::string> _licenseString,
|
||||
std::vector<ASTPointer<ASTNode>> _nodes
|
||||
):
|
||||
ASTNode(_id, _location), m_licenseString(std::move(_licenseString)), m_nodes(std::move(_nodes)) {}
|
||||
|
||||
void accept(ASTVisitor& _visitor) override;
|
||||
void accept(ASTConstVisitor& _visitor) const override;
|
||||
SourceUnitAnnotation& annotation() const override;
|
||||
|
||||
std::optional<std::string> const& licenseString() const { return m_licenseString; }
|
||||
std::vector<ASTPointer<ASTNode>> nodes() const { return m_nodes; }
|
||||
|
||||
/// @returns a set of referenced SourceUnits. Recursively if @a _recurse is true.
|
||||
std::set<SourceUnit const*> referencedSourceUnits(bool _recurse = false, std::set<SourceUnit const*> _skipList = std::set<SourceUnit const*>()) const;
|
||||
|
||||
private:
|
||||
std::optional<std::string> m_licenseString;
|
||||
std::vector<ASTPointer<ASTNode>> m_nodes;
|
||||
};
|
||||
|
||||
|
||||
@@ -225,6 +225,7 @@ bool ASTJsonConverter::visit(SourceUnit const& _node)
|
||||
{
|
||||
make_pair("absolutePath", _node.annotation().path),
|
||||
make_pair("exportedSymbols", move(exportedSymbols)),
|
||||
make_pair("license", _node.licenseString() ? Json::Value(*_node.licenseString()) : Json::nullValue),
|
||||
make_pair("nodes", toJson(_node.nodes()))
|
||||
}
|
||||
);
|
||||
|
||||
@@ -218,10 +218,15 @@ ASTPointer<ASTNode> ASTJsonImporter::convertJsonToASTNode(Json::Value const& _js
|
||||
|
||||
ASTPointer<SourceUnit> ASTJsonImporter::createSourceUnit(Json::Value const& _node, string const& _srcName)
|
||||
{
|
||||
optional<string> license;
|
||||
if (_node.isMember("license") && !_node["license"].isNull())
|
||||
license = _node["license"].asString();
|
||||
|
||||
vector<ASTPointer<ASTNode>> nodes;
|
||||
for (auto& child: member(_node, "nodes"))
|
||||
nodes.emplace_back(convertJsonToASTNode(child));
|
||||
ASTPointer<SourceUnit> tmp = createASTNode<SourceUnit>(_node, nodes);
|
||||
|
||||
ASTPointer<SourceUnit> tmp = createASTNode<SourceUnit>(_node, license, nodes);
|
||||
tmp->annotation().path = _srcName;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
@@ -1236,6 +1236,8 @@ string CompilerStack::createMetadata(Contract const& _contract) const
|
||||
|
||||
solAssert(s.second.scanner, "Scanner not available");
|
||||
meta["sources"][s.first]["keccak256"] = "0x" + toHex(s.second.keccak256().asBytes());
|
||||
if (optional<string> licenseString = s.second.ast->licenseString())
|
||||
meta["sources"][s.first]["license"] = *licenseString;
|
||||
if (m_metadataLiteralSources)
|
||||
meta["sources"][s.first]["content"] = s.second.scanner->source();
|
||||
else
|
||||
|
||||
@@ -30,8 +30,11 @@
|
||||
#include <liblangutil/SemVerHandler.h>
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <cctype>
|
||||
#include <vector>
|
||||
#include <regex>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::langutil;
|
||||
@@ -79,6 +82,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
|
||||
m_recursionDepth = 0;
|
||||
m_scanner = _scanner;
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
|
||||
vector<ASTPointer<ASTNode>> nodes;
|
||||
while (m_scanner->currentToken() != Token::EOS)
|
||||
{
|
||||
@@ -107,7 +111,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
|
||||
}
|
||||
}
|
||||
solAssert(m_recursionDepth == 0, "");
|
||||
return nodeFactory.createNode<SourceUnit>(nodes);
|
||||
return nodeFactory.createNode<SourceUnit>(findLicenseString(nodes), nodes);
|
||||
}
|
||||
catch (FatalError const&)
|
||||
{
|
||||
@@ -1981,6 +1985,60 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
|
||||
return ret;
|
||||
}
|
||||
|
||||
optional<string> Parser::findLicenseString(std::vector<ASTPointer<ASTNode>> const& _nodes)
|
||||
{
|
||||
// We circumvent the scanner here, because it skips non-docstring comments.
|
||||
static regex const licenseRegex("SPDX-License-Identifier:\\s*([a-zA-Z0-9 ()+.-]+)");
|
||||
|
||||
// Search inside all parts of the source not covered by parsed nodes.
|
||||
// This will leave e.g. "global comments".
|
||||
string const& source = m_scanner->source();
|
||||
using iter = decltype(source.begin());
|
||||
vector<pair<iter, iter>> sequencesToSearch;
|
||||
sequencesToSearch.emplace_back(source.begin(), source.end());
|
||||
for (ASTPointer<ASTNode> const& node: _nodes)
|
||||
if (node->location().hasText())
|
||||
{
|
||||
sequencesToSearch.back().second = source.begin() + node->location().start;
|
||||
sequencesToSearch.emplace_back(source.begin() + node->location().end, source.end());
|
||||
}
|
||||
|
||||
vector<string> matches;
|
||||
for (auto const& [start, end]: sequencesToSearch)
|
||||
{
|
||||
smatch match;
|
||||
if (regex_search(start, end, match, licenseRegex))
|
||||
{
|
||||
string license{boost::trim_copy(string(match[1]))};
|
||||
if (!license.empty())
|
||||
matches.emplace_back(std::move(license));
|
||||
}
|
||||
}
|
||||
|
||||
if (matches.size() == 1)
|
||||
return matches.front();
|
||||
else if (matches.empty())
|
||||
parserWarning(
|
||||
1878_error,
|
||||
{-1, -1, m_scanner->charStream()},
|
||||
"SPDX license identifier not provided in source file. "
|
||||
"Before publishing, consider adding a comment containing "
|
||||
"\"SPDX-License-Identifier: <SPDX-License>\" to each source file. "
|
||||
"Use \"SPDX-License-Identifier: UNLICENSED\" for non-open-source code. "
|
||||
"Please see https://spdx.org for more information."
|
||||
);
|
||||
else
|
||||
parserError(
|
||||
3716_error,
|
||||
{-1, -1, m_scanner->charStream()},
|
||||
"Multiple SPDX license identifiers found in source file. "
|
||||
"Use \"AND\" or \"OR\" to combine multiple licenses. "
|
||||
"Please see https://spdx.org for more information."
|
||||
);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Parser::LookAheadInfo Parser::peekStatementType() const
|
||||
{
|
||||
// Distinguish between variable declaration (and potentially assignment) and expression statement
|
||||
|
||||
@@ -175,6 +175,8 @@ private:
|
||||
bool empty() const;
|
||||
};
|
||||
|
||||
std::optional<std::string> findLicenseString(std::vector<ASTPointer<ASTNode>> const& _nodes);
|
||||
|
||||
/// Returns the next AST node ID
|
||||
int64_t nextID() { return ++m_currentNodeID; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user