From dd81d0555954678119d3920861645bb310f193ad Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Thu, 10 Sep 2020 12:01:23 +0200 Subject: [PATCH] Make annotations ``SetOnce`` or ``optional`` where feasible --- libsolidity/analysis/ContractLevelChecker.cpp | 13 ++- libsolidity/analysis/ImmutableValidator.cpp | 2 +- libsolidity/analysis/NameAndTypeResolver.cpp | 2 +- libsolidity/analysis/StaticAnalyzer.cpp | 6 +- libsolidity/analysis/TypeChecker.cpp | 107 ++++++++++++------ libsolidity/ast/AST.cpp | 2 +- libsolidity/ast/ASTAnnotations.h | 19 ++-- libsolidity/ast/ASTJsonConverter.cpp | 100 +++++++++++----- libsolidity/ast/Types.cpp | 16 +-- libsolidity/codegen/ExpressionCompiler.cpp | 4 +- libsolidity/interface/CompilerStack.cpp | 8 +- libsolutil/SetOnce.h | 2 + .../ASTJSON/assembly/nested_functions.json | 1 - .../assembly/nested_functions_legacy.json | 1 - test/libsolidity/ASTJSON/assembly/switch.json | 1 - .../ASTJSON/assembly/switch_legacy.json | 1 - .../SolidityNameAndTypeResolution.cpp | 12 +- tools/solidityUpgrade/Upgrade060.cpp | 2 +- 18 files changed, 194 insertions(+), 105 deletions(-) diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index e8804d359..d0ca9e2a4 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -51,6 +51,8 @@ bool hasEqualNameAndParameters(T const& _a, B const& _b) bool ContractLevelChecker::check(ContractDefinition const& _contract) { + _contract.annotation().unimplementedDeclarations = std::vector(); + checkDuplicateFunctions(_contract); checkDuplicateEvents(_contract); m_overrideChecker.check(_contract); @@ -210,9 +212,10 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c // Set to not fully implemented if at least one flag is false. // Note that `_contract.annotation().unimplementedDeclarations` has already been // pre-filled by `checkBaseConstructorArguments`. + // for (auto const& proxy: proxies) if (proxy.unimplemented()) - _contract.annotation().unimplementedDeclarations.push_back(proxy.declaration()); + _contract.annotation().unimplementedDeclarations->push_back(proxy.declaration()); if (_contract.abstract()) { @@ -229,17 +232,17 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c if ( _contract.contractKind() == ContractKind::Contract && !_contract.abstract() && - !_contract.annotation().unimplementedDeclarations.empty() + !_contract.annotation().unimplementedDeclarations->empty() ) { SecondarySourceLocation ssl; - for (auto declaration: _contract.annotation().unimplementedDeclarations) + for (auto declaration: *_contract.annotation().unimplementedDeclarations) ssl.append("Missing implementation: ", declaration->location()); m_errorReporter.typeError( 3656_error, _contract.location(), ssl, - "Contract \"" + _contract.annotation().canonicalName + "\" should be marked as abstract." + "Contract \"" + *_contract.annotation().canonicalName + "\" should be marked as abstract." ); } } @@ -289,7 +292,7 @@ void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition cons if (FunctionDefinition const* constructor = contract->constructor()) if (contract != &_contract && !constructor->parameters().empty()) if (!_contract.annotation().baseConstructorArguments.count(constructor)) - _contract.annotation().unimplementedDeclarations.push_back(constructor); + _contract.annotation().unimplementedDeclarations->push_back(constructor); } void ContractLevelChecker::annotateBaseConstructorArguments( diff --git a/libsolidity/analysis/ImmutableValidator.cpp b/libsolidity/analysis/ImmutableValidator.cpp index 93f676c6f..2cf34ae6d 100644 --- a/libsolidity/analysis/ImmutableValidator.cpp +++ b/libsolidity/analysis/ImmutableValidator.cpp @@ -167,7 +167,7 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va // If this is not an ordinary assignment, we write and read at the same time. bool write = _expression.annotation().willBeWrittenTo; - bool read = !_expression.annotation().willBeWrittenTo || !_expression.annotation().lValueOfOrdinaryAssignment; + bool read = !_expression.annotation().willBeWrittenTo || !*_expression.annotation().lValueOfOrdinaryAssignment; if (write) { if (!m_currentConstructor) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 990382aee..49f8b6d7c 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -74,7 +74,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map(node.get())) { - string const& path = imp->annotation().absolutePath; + string const& path = *imp->annotation().absolutePath; if (!_sourceUnits.count(path)) { m_errorReporter.declarationError( diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index c9be0a3d3..abfeae23d 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -185,7 +185,7 @@ bool StaticAnalyzer::visit(Return const& _return) bool StaticAnalyzer::visit(ExpressionStatement const& _statement) { - if (_statement.expression().annotation().isPure) + if (*_statement.expression().annotation().isPure) m_errorReporter.warning( 6133_error, _statement.location(), @@ -287,7 +287,7 @@ bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly) bool StaticAnalyzer::visit(BinaryOperation const& _operation) { if ( - _operation.rightExpression().annotation().isPure && + *_operation.rightExpression().annotation().isPure && (_operation.getOperator() == Token::Div || _operation.getOperator() == Token::Mod) ) if (auto rhs = dynamic_cast( @@ -312,7 +312,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall) if (functionType->kind() == FunctionType::Kind::AddMod || functionType->kind() == FunctionType::Kind::MulMod) { solAssert(_functionCall.arguments().size() == 3, ""); - if (_functionCall.arguments()[2]->annotation().isPure) + if (*_functionCall.arguments()[2]->annotation().isPure) if (auto lastArg = dynamic_cast( ConstantEvaluator(m_errorReporter).evaluate(*(_functionCall.arguments())[2]) )) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index e280fd85b..6f351faba 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -538,7 +538,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (!_variable.value()) m_errorReporter.typeError(4266_error, _variable.location(), "Uninitialized \"constant\" variable."); - else if (!_variable.value()->annotation().isPure) + else if (!*_variable.value()->annotation().isPure) m_errorReporter.typeError( 8349_error, _variable.value()->location(), @@ -1296,11 +1296,14 @@ bool TypeChecker::visit(Conditional const& _conditional) } } + _conditional.annotation().isConstant = false; _conditional.annotation().type = commonType; _conditional.annotation().isPure = - _conditional.condition().annotation().isPure && - _conditional.trueExpression().annotation().isPure && - _conditional.falseExpression().annotation().isPure; + *_conditional.condition().annotation().isPure && + *_conditional.trueExpression().annotation().isPure && + *_conditional.falseExpression().annotation().isPure; + + _conditional.annotation().isLValue = false; if (_conditional.annotation().willBeWrittenTo) m_errorReporter.typeError( @@ -1354,6 +1357,9 @@ bool TypeChecker::visit(Assignment const& _assignment) ); TypePointer t = type(_assignment.leftHandSide()); _assignment.annotation().type = t; + _assignment.annotation().isPure = false; + _assignment.annotation().isLValue = false; + _assignment.annotation().isConstant = false; checkExpressionAssignment(*t, _assignment.leftHandSide()); @@ -1401,6 +1407,7 @@ bool TypeChecker::visit(Assignment const& _assignment) bool TypeChecker::visit(TupleExpression const& _tuple) { + _tuple.annotation().isConstant = false; vector> const& components = _tuple.components(); TypePointers types; @@ -1413,7 +1420,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) { requireLValue( *component, - _tuple.annotation().lValueOfOrdinaryAssignment + *_tuple.annotation().lValueOfOrdinaryAssignment ); types.push_back(type(*component)); } @@ -1425,6 +1432,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) _tuple.annotation().type = TypeProvider::tuple(move(types)); // If some of the components are not LValues, the error is reported above. _tuple.annotation().isLValue = true; + _tuple.annotation().isPure = false; } else { @@ -1464,7 +1472,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) else if (inlineArrayType) inlineArrayType = Type::commonType(inlineArrayType, types[i]); } - if (!components[i]->annotation().isPure) + if (!*components[i]->annotation().isPure) isPure = false; } _tuple.annotation().isPure = isPure; @@ -1495,6 +1503,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) _tuple.annotation().type = TypeProvider::tuple(move(types)); } + _tuple.annotation().isLValue = false; } return false; } @@ -1522,7 +1531,9 @@ bool TypeChecker::visit(UnaryOperation const& _operation) t = subExprType; } _operation.annotation().type = t; - _operation.annotation().isPure = !modifying && _operation.subExpression().annotation().isPure; + _operation.annotation().isConstant = false; + _operation.annotation().isPure = !modifying && *_operation.subExpression().annotation().isPure; + _operation.annotation().isLValue = false; return false; } @@ -1553,8 +1564,10 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) TypeProvider::boolean() : commonType; _operation.annotation().isPure = - _operation.leftExpression().annotation().isPure && - _operation.rightExpression().annotation().isPure; + *_operation.leftExpression().annotation().isPure && + *_operation.rightExpression().annotation().isPure; + _operation.annotation().isLValue = false; + _operation.annotation().isConstant = false; if (_operation.getOperator() == Token::Exp || _operation.getOperator() == Token::SHL) { @@ -2174,7 +2187,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) for (ASTPointer const& argument: arguments) { argument->accept(*this); - if (!argument->annotation().isPure) + if (!*argument->annotation().isPure) argumentsArePure = false; } @@ -2197,6 +2210,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) // Determine function call kind and function type for this FunctionCall node FunctionCallAnnotation& funcCallAnno = _functionCall.annotation(); FunctionTypePointer functionType = nullptr; + funcCallAnno.isConstant = false; + + bool isLValue = false; // Determine and assign function call kind, lvalue, purity and function type for this FunctionCall node switch (expressionType->category()) @@ -2208,7 +2224,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) // Purity for function calls also depends upon the callee and its FunctionType funcCallAnno.isPure = argumentsArePure && - _functionCall.expression().annotation().isPure && + *_functionCall.expression().annotation().isPure && functionType && functionType->isPure(); @@ -2216,7 +2232,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) functionType->kind() == FunctionType::Kind::ArrayPush || functionType->kind() == FunctionType::Kind::ByteArrayPush ) - funcCallAnno.isLValue = functionType->parameterTypes().empty(); + isLValue = functionType->parameterTypes().empty(); break; @@ -2236,13 +2252,11 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) ); functionType = dynamic_cast(*actualType).constructorType(); funcCallAnno.kind = FunctionCallKind::StructConstructorCall; - funcCallAnno.isPure = argumentsArePure; } else - { funcCallAnno.kind = FunctionCallKind::TypeConversion; - funcCallAnno.isPure = argumentsArePure; - } + + funcCallAnno.isPure = argumentsArePure; break; } @@ -2255,6 +2269,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) break; } + funcCallAnno.isLValue = isLValue; + // Determine return types switch (*funcCallAnno.kind) { @@ -2325,6 +2341,9 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) _functionCallOptions.expression().accept(*this); + _functionCallOptions.annotation().isPure = false; + _functionCallOptions.annotation().isConstant = false; + auto expressionFunctionType = dynamic_cast(type(_functionCallOptions.expression())); if (!expressionFunctionType) { @@ -2455,6 +2474,8 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) TypePointer type = _newExpression.typeName().annotation().type; solAssert(!!type, "Type name not resolved."); + _newExpression.annotation().isConstant = false; + if (auto contractName = dynamic_cast(&_newExpression.typeName())) { auto contract = dynamic_cast(&dereference(*contractName)); @@ -2485,6 +2506,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) } _newExpression.annotation().type = FunctionType::newExpressionType(*contract); + _newExpression.annotation().isPure = false; } else if (type->category() == Type::Category::Array) { @@ -2541,6 +2563,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) auto& annotation = _memberAccess.annotation(); + annotation.isConstant = false; + if (possibleMembers.empty()) { if (initialMemberCount == 0 && !dynamic_cast(exprType)) @@ -2673,11 +2697,16 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) functionType && functionType->kind() == FunctionType::Kind::Declaration ) - annotation.isPure = _memberAccess.expression().annotation().isPure; + annotation.isPure = *_memberAccess.expression().annotation().isPure; } } else if (exprType->category() == Type::Category::Module) - annotation.isPure = _memberAccess.expression().annotation().isPure; + { + annotation.isPure = *_memberAccess.expression().annotation().isPure; + annotation.isLValue = false; + } + else + annotation.isLValue = false; // TODO some members might be pure, but for example `address(0x123).balance` is not pure // although every subexpression is, so leaving this limited for now. @@ -2693,11 +2722,14 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) ) if (auto const* parentAccess = dynamic_cast(&_memberAccess.expression())) { - annotation.isPure = parentAccess->expression().annotation().isPure; + bool isPure = *parentAccess->expression().annotation().isPure; if (auto const* exprInt = dynamic_cast(&parentAccess->expression())) if (exprInt->name() == "this" || exprInt->name() == "super") - annotation.isPure = true; + isPure = true; + + annotation.isPure = isPure; } + if (auto magicType = dynamic_cast(exprType)) { if (magicType->kind() == MagicType::Kind::ABI) @@ -2745,16 +2777,20 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) annotation.isPure = true; } + if (!annotation.isPure.set()) + annotation.isPure = false; + return false; } bool TypeChecker::visit(IndexAccess const& _access) { + _access.annotation().isConstant = false; _access.baseExpression().accept(*this); TypePointer baseType = type(_access.baseExpression()); TypePointer resultType = nullptr; bool isLValue = false; - bool isPure = _access.baseExpression().annotation().isPure; + bool isPure = *_access.baseExpression().annotation().isPure; Expression const* index = _access.indexExpression(); switch (baseType->category()) { @@ -2856,7 +2892,7 @@ bool TypeChecker::visit(IndexAccess const& _access) } _access.annotation().type = resultType; _access.annotation().isLValue = isLValue; - if (index && !index->annotation().isPure) + if (index && !*index->annotation().isPure) isPure = false; _access.annotation().isPure = isPure; @@ -2865,21 +2901,22 @@ bool TypeChecker::visit(IndexAccess const& _access) bool TypeChecker::visit(IndexRangeAccess const& _access) { + _access.annotation().isConstant = false; _access.baseExpression().accept(*this); bool isLValue = false; // TODO: set this correctly when implementing slices for memory and storage arrays - bool isPure = _access.baseExpression().annotation().isPure; + bool isPure = *_access.baseExpression().annotation().isPure; if (Expression const* start = _access.startExpression()) { expectType(*start, *TypeProvider::uint256()); - if (!start->annotation().isPure) + if (!*start->annotation().isPure) isPure = false; } if (Expression const* end = _access.endExpression()) { expectType(*end, *TypeProvider::uint256()); - if (!end->annotation().isPure) + if (!*end->annotation().isPure) isPure = false; } @@ -3022,20 +3059,22 @@ bool TypeChecker::visit(Identifier const& _identifier) !!annotation.referencedDeclaration, "Referenced declaration is null after overload resolution." ); + bool isConstant = false; annotation.isLValue = annotation.referencedDeclaration->isLValue(); annotation.type = annotation.referencedDeclaration->type(); solAssert(annotation.type, "Declaration referenced before type could be determined."); if (auto variableDeclaration = dynamic_cast(annotation.referencedDeclaration)) - annotation.isPure = annotation.isConstant = variableDeclaration->isConstant(); + annotation.isPure = isConstant = variableDeclaration->isConstant(); else if (dynamic_cast(annotation.referencedDeclaration)) - { - if (dynamic_cast(annotation.type)) - annotation.isPure = true; - } + annotation.isPure = dynamic_cast(annotation.type); else if (dynamic_cast(annotation.type)) annotation.isPure = true; else if (dynamic_cast(annotation.type)) annotation.isPure = true; + else + annotation.isPure = false; + + annotation.isConstant = isConstant; // Check for deprecated function names. // The check is done here for the case without an actual function call. @@ -3076,6 +3115,8 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr) { _expr.annotation().type = TypeProvider::typeType(TypeProvider::fromElementaryTypeName(_expr.type().typeName(), _expr.type().stateMutability())); _expr.annotation().isPure = true; + _expr.annotation().isLValue = false; + _expr.annotation().isConstant = false; } void TypeChecker::endVisit(Literal const& _literal) @@ -3131,6 +3172,8 @@ void TypeChecker::endVisit(Literal const& _literal) m_errorReporter.fatalTypeError(2826_error, _literal.location(), "Invalid literal value."); _literal.annotation().isPure = true; + _literal.annotation().isLValue = false; + _literal.annotation().isConstant = false; } void TypeChecker::endVisit(UsingForDirective const& _usingFor) @@ -3215,11 +3258,11 @@ void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAss _expression.annotation().lValueOfOrdinaryAssignment = _ordinaryAssignment; _expression.accept(*this); - if (_expression.annotation().isLValue) + if (*_expression.annotation().isLValue) return; auto [errorId, description] = [&]() -> tuple { - if (_expression.annotation().isConstant) + if (*_expression.annotation().isConstant) return { 6520_error, "Cannot assign to a constant variable." }; if (auto indexAccess = dynamic_cast(&_expression)) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index dd3e99854..7094ccf83 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -492,7 +492,7 @@ CallableDeclaration const* Scopable::functionOrModifierDefinition() const string Scopable::sourceUnitName() const { - return sourceUnit().annotation().path; + return *sourceUnit().annotation().path; } DeclarationAnnotation& Declaration::annotation() const diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 768bac502..2aa48d03e 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -47,6 +47,7 @@ namespace solidity::frontend class Type; using TypePointer = Type const*; +using namespace util; struct ASTAnnotation { @@ -88,9 +89,9 @@ struct StructurallyDocumentedAnnotation struct SourceUnitAnnotation: ASTAnnotation { /// The "absolute" (in the compiler sense) path of this source unit. - std::string path; + SetOnce path; /// The exported symbols (all global symbols). - std::map> exportedSymbols; + SetOnce>> exportedSymbols; /// Experimental features. std::set experimentalFeatures; }; @@ -122,7 +123,7 @@ struct DeclarationAnnotation: ASTAnnotation, ScopableAnnotation struct ImportAnnotation: DeclarationAnnotation { /// The absolute path of the source unit to import. - std::string absolutePath; + SetOnce absolutePath; /// The actual source unit. SourceUnit const* sourceUnit = nullptr; }; @@ -130,7 +131,7 @@ struct ImportAnnotation: DeclarationAnnotation struct TypeDeclarationAnnotation: DeclarationAnnotation { /// The name of this type, prefixed by proper namespaces if globally accessible. - std::string canonicalName; + SetOnce canonicalName; }; struct StructDeclarationAnnotation: TypeDeclarationAnnotation @@ -149,7 +150,7 @@ struct StructDeclarationAnnotation: TypeDeclarationAnnotation struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation { /// List of functions and modifiers without a body. Can also contain functions from base classes. - std::vector unimplementedDeclarations; + std::optional> unimplementedDeclarations; /// List of all (direct and indirect) base contracts in order from derived to /// base, including the contract itself. std::vector linearizedBaseContracts; @@ -243,16 +244,16 @@ struct ExpressionAnnotation: ASTAnnotation /// Inferred type of the expression. TypePointer type = nullptr; /// Whether the expression is a constant variable - bool isConstant = false; + SetOnce isConstant; /// Whether the expression is pure, i.e. compile-time constant. - bool isPure = false; + SetOnce isPure; /// Whether it is an LValue (i.e. something that can be assigned to). - bool isLValue = false; + SetOnce isLValue; /// Whether the expression is used in a context where the LValue is actually required. bool willBeWrittenTo = false; /// Whether the expression is an lvalue that is only assigned. /// Would be false for --, ++, delete, +=, -=, .... - bool lValueOfOrdinaryAssignment = false; + SetOnce lValueOfOrdinaryAssignment; /// Types and - if given - names of arguments if the expr. is a function /// that is called, used for overload resolution diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 6d6d40775..34ae3c2b8 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -39,10 +39,33 @@ #include #include #include +#include using namespace std; using namespace solidity::langutil; +namespace +{ + +template typename C> +void addIfSet(std::vector>& _attributes, string const& _name, C const& _value) +{ + if constexpr (std::is_same_v, solidity::util::SetOnce>) + { + if (!_value.set()) + return; + } + else if constexpr (std::is_same_v, optional>) + { + if (!_value.has_value()) + return; + } + + _attributes.emplace_back(_name, *_value); +} + +} + namespace solidity::frontend { @@ -181,12 +204,14 @@ void ASTJsonConverter::appendExpressionAttributes( { std::vector> exprAttributes = { make_pair("typeDescriptions", typePointerToJson(_annotation.type)), - make_pair("isConstant", _annotation.isConstant), - make_pair("isPure", _annotation.isPure), - make_pair("isLValue", _annotation.isLValue), make_pair("lValueRequested", _annotation.willBeWrittenTo), make_pair("argumentTypes", typePointerToJson(_annotation.arguments)) }; + + addIfSet(exprAttributes, "isLValue", _annotation.isLValue); + addIfSet(exprAttributes, "isPure", _annotation.isPure); + addIfSet(exprAttributes, "isConstant", _annotation.isConstant); + _attributes += exprAttributes; } @@ -214,23 +239,27 @@ Json::Value ASTJsonConverter::toJson(ASTNode const& _node) bool ASTJsonConverter::visit(SourceUnit const& _node) { - Json::Value exportedSymbols = Json::objectValue; - for (auto const& sym: _node.annotation().exportedSymbols) + std::vector> attributes = { + make_pair("license", _node.licenseString() ? Json::Value(*_node.licenseString()) : Json::nullValue), + make_pair("nodes", toJson(_node.nodes())) + }; + + if (_node.annotation().exportedSymbols.set()) { - exportedSymbols[sym.first] = Json::arrayValue; - for (Declaration const* overload: sym.second) - exportedSymbols[sym.first].append(nodeId(*overload)); - } - setJsonNode( - _node, - "SourceUnit", + Json::Value exportedSymbols = Json::objectValue; + for (auto const& sym: *_node.annotation().exportedSymbols) { - 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())) + exportedSymbols[sym.first] = Json::arrayValue; + for (Declaration const* overload: sym.second) + exportedSymbols[sym.first].append(nodeId(*overload)); } - ); + + attributes.emplace_back("exportedSymbols", exportedSymbols); + }; + + addIfSet(attributes, "absolutePath", _node.annotation().path); + + setJsonNode(_node, "SourceUnit", std::move(attributes)); return false; } @@ -249,10 +278,12 @@ bool ASTJsonConverter::visit(ImportDirective const& _node) { std::vector> attributes = { make_pair("file", _node.path()), - make_pair("absolutePath", _node.annotation().absolutePath), make_pair(m_legacy ? "SourceUnit" : "sourceUnit", nodeId(*_node.annotation().sourceUnit)), make_pair("scope", idOrNull(_node.scope())) }; + + addIfSet(attributes, "absolutePath", _node.annotation().absolutePath); + attributes.emplace_back("unitAlias", _node.name()); Json::Value symbolAliases(Json::arrayValue); for (auto const& symbolAlias: _node.symbolAliases()) @@ -270,18 +301,23 @@ bool ASTJsonConverter::visit(ImportDirective const& _node) bool ASTJsonConverter::visit(ContractDefinition const& _node) { - setJsonNode(_node, "ContractDefinition", { + std::vector> attributes = { make_pair("name", _node.name()), make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), make_pair("contractKind", contractKind(_node.contractKind())), make_pair("abstract", _node.abstract()), - make_pair("fullyImplemented", _node.annotation().unimplementedDeclarations.empty()), - make_pair("linearizedBaseContracts", getContainerIds(_node.annotation().linearizedBaseContracts)), make_pair("baseContracts", toJson(_node.baseContracts())), make_pair("contractDependencies", getContainerIds(_node.annotation().contractDependencies, true)), make_pair("nodes", toJson(_node.subNodes())), make_pair("scope", idOrNull(_node.scope())) - }); + }; + + if (_node.annotation().unimplementedDeclarations.has_value()) + attributes.emplace_back("fullyImplemented", _node.annotation().unimplementedDeclarations->empty()); + if (!_node.annotation().linearizedBaseContracts.empty()) + attributes.emplace_back("linearizedBaseContracts", getContainerIds(_node.annotation().linearizedBaseContracts)); + + setJsonNode(_node, "ContractDefinition", std::move(attributes)); return false; } @@ -305,23 +341,31 @@ bool ASTJsonConverter::visit(UsingForDirective const& _node) bool ASTJsonConverter::visit(StructDefinition const& _node) { - setJsonNode(_node, "StructDefinition", { + std::vector> attributes = { make_pair("name", _node.name()), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), - make_pair("canonicalName", _node.annotation().canonicalName), make_pair("members", toJson(_node.members())), make_pair("scope", idOrNull(_node.scope())) - }); + }; + + addIfSet(attributes,"canonicalName", _node.annotation().canonicalName); + + setJsonNode(_node, "StructDefinition", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(EnumDefinition const& _node) { - setJsonNode(_node, "EnumDefinition", { + std::vector> attributes = { make_pair("name", _node.name()), - make_pair("canonicalName", _node.annotation().canonicalName), make_pair("members", toJson(_node.members())) - }); + }; + + addIfSet(attributes,"canonicalName", _node.annotation().canonicalName); + + setJsonNode(_node, "EnumDefinition", std::move(attributes)); + return false; } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index af52f63d9..b4d2887f1 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2109,7 +2109,7 @@ string ContractType::toString(bool) const string ContractType::canonicalName() const { - return m_contract.annotation().canonicalName; + return *m_contract.annotation().canonicalName; } MemberList::MemberMap ContractType::nativeMembers(ASTNode const*) const @@ -2360,7 +2360,7 @@ bool StructType::containsNestedMapping() const string StructType::toString(bool _short) const { - string ret = "struct " + m_struct.annotation().canonicalName; + string ret = "struct " + *m_struct.annotation().canonicalName; if (!_short) ret += " " + stringForReferencePart(); return ret; @@ -2539,7 +2539,7 @@ string StructType::signatureInExternalFunction(bool _structsByName) const string StructType::canonicalName() const { - return m_struct.annotation().canonicalName; + return *m_struct.annotation().canonicalName; } FunctionTypePointer StructType::constructorType() const @@ -2646,12 +2646,12 @@ unsigned EnumType::storageBytes() const string EnumType::toString(bool) const { - return string("enum ") + m_enum.annotation().canonicalName; + return string("enum ") + *m_enum.annotation().canonicalName; } string EnumType::canonicalName() const { - return m_enum.annotation().canonicalName; + return *m_enum.annotation().canonicalName; } size_t EnumType::numberOfMembers() const @@ -3124,7 +3124,7 @@ string FunctionType::toString(bool _short) const auto const* functionDefinition = dynamic_cast(m_declaration); solAssert(functionDefinition, ""); if (auto const* contract = dynamic_cast(functionDefinition->scope())) - name += contract->annotation().canonicalName + "."; + name += *contract->annotation().canonicalName + "."; name += functionDefinition->name(); } name += '('; @@ -3915,7 +3915,7 @@ bool ModuleType::operator==(Type const& _other) const MemberList::MemberMap ModuleType::nativeMembers(ASTNode const*) const { MemberList::MemberMap symbols; - for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols) + for (auto const& symbolName: *m_sourceUnit.annotation().exportedSymbols) for (Declaration const* symbol: symbolName.second) symbols.emplace_back(symbolName.first, symbol->type(), symbol); return symbols; @@ -3923,7 +3923,7 @@ MemberList::MemberMap ModuleType::nativeMembers(ASTNode const*) const string ModuleType::toString(bool) const { - return string("module \"") + m_sourceUnit.annotation().path + string("\""); + return string("module \"") + *m_sourceUnit.annotation().path + string("\""); } string MagicType::richIdentifier() const diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index f92117689..f096583db 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -779,7 +779,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) solAssert(function.parameterTypes().size() == 1, ""); if (m_context.revertStrings() == RevertStrings::Strip) { - if (!arguments.front()->annotation().isPure) + if (!*arguments.front()->annotation().isPure) { arguments.front()->accept(*this); utils().popStackElement(*arguments.front()->annotation().type); @@ -1078,7 +1078,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) solAssert(function.kind() == FunctionType::Kind::Require, ""); if (m_context.revertStrings() == RevertStrings::Strip) { - if (!arguments.at(1)->annotation().isPure) + if (!*arguments.at(1)->annotation().isPure) { arguments.at(1)->accept(*this); utils().popStackElement(*arguments.at(1)->annotation().type); diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 32469e566..db7cab879 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -1097,7 +1097,7 @@ void CompilerStack::resolveImports() for (ASTPointer const& node: _source->ast->nodes()) if (ImportDirective const* import = dynamic_cast(node.get())) { - string const& path = import->annotation().absolutePath; + string const& path = *import->annotation().absolutePath; solAssert(m_sources.count(path), ""); import->annotation().sourceUnit = m_sources[path].ast.get(); toposort(&m_sources[path]); @@ -1295,9 +1295,9 @@ string CompilerStack::createMetadata(Contract const& _contract) const /// All the source files (including self), which should be included in the metadata. set referencedSources; - referencedSources.insert(_contract.contract->sourceUnit().annotation().path); + referencedSources.insert(*_contract.contract->sourceUnit().annotation().path); for (auto const sourceUnit: _contract.contract->sourceUnit().referencedSourceUnits(true)) - referencedSources.insert(sourceUnit->annotation().path); + referencedSources.insert(*sourceUnit->annotation().path); meta["sources"] = Json::objectValue; for (auto const& s: m_sources) @@ -1363,7 +1363,7 @@ string CompilerStack::createMetadata(Contract const& _contract) const meta["settings"]["evmVersion"] = m_evmVersion.name(); meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = - _contract.contract->annotation().canonicalName; + *_contract.contract->annotation().canonicalName; meta["settings"]["remappings"] = Json::arrayValue; set remappings; diff --git a/libsolutil/SetOnce.h b/libsolutil/SetOnce.h index 289258e7b..007424ff1 100644 --- a/libsolutil/SetOnce.h +++ b/libsolutil/SetOnce.h @@ -79,6 +79,8 @@ public: /// @throws BadSetOnceAccess when the stored value has not yet been set T const* operator->() const { return std::addressof(**this); } + /// @return true if a value was assigned + bool set() const { return m_value.has_value(); } private: std::optional m_value = std::nullopt; }; diff --git a/test/libsolidity/ASTJSON/assembly/nested_functions.json b/test/libsolidity/ASTJSON/assembly/nested_functions.json index f945494ba..984c05cc9 100644 --- a/test/libsolidity/ASTJSON/assembly/nested_functions.json +++ b/test/libsolidity/ASTJSON/assembly/nested_functions.json @@ -16,7 +16,6 @@ "baseContracts": [], "contractDependencies": [], "contractKind": "contract", - "fullyImplemented": true, "id": 8, "linearizedBaseContracts": [ diff --git a/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json b/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json index 393646221..4f5bfb268 100644 --- a/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json @@ -25,7 +25,6 @@ null ], "contractKind": "contract", - "fullyImplemented": true, "linearizedBaseContracts": [ 8 diff --git a/test/libsolidity/ASTJSON/assembly/switch.json b/test/libsolidity/ASTJSON/assembly/switch.json index 9ae71f15c..9a2d1f5c5 100644 --- a/test/libsolidity/ASTJSON/assembly/switch.json +++ b/test/libsolidity/ASTJSON/assembly/switch.json @@ -16,7 +16,6 @@ "baseContracts": [], "contractDependencies": [], "contractKind": "contract", - "fullyImplemented": true, "id": 6, "linearizedBaseContracts": [ diff --git a/test/libsolidity/ASTJSON/assembly/switch_legacy.json b/test/libsolidity/ASTJSON/assembly/switch_legacy.json index 92e2e2c61..beddc74e1 100644 --- a/test/libsolidity/ASTJSON/assembly/switch_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/switch_legacy.json @@ -25,7 +25,6 @@ null ], "contractKind": "contract", - "fullyImplemented": true, "linearizedBaseContracts": [ 6 diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 65a8026a4..1ecd0e9f9 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -52,7 +52,7 @@ BOOST_AUTO_TEST_CASE(function_no_implementation) std::vector> nodes = sourceUnit->nodes(); ContractDefinition* contract = dynamic_cast(nodes[1].get()); BOOST_REQUIRE(contract); - BOOST_CHECK(!contract->annotation().unimplementedDeclarations.empty()); + BOOST_CHECK(!contract->annotation().unimplementedDeclarations->empty()); BOOST_CHECK(!contract->definedFunctions()[0]->isImplemented()); } @@ -68,10 +68,10 @@ BOOST_AUTO_TEST_CASE(abstract_contract) ContractDefinition* base = dynamic_cast(nodes[1].get()); ContractDefinition* derived = dynamic_cast(nodes[2].get()); BOOST_REQUIRE(base); - BOOST_CHECK(!base->annotation().unimplementedDeclarations.empty()); + BOOST_CHECK(!base->annotation().unimplementedDeclarations->empty()); BOOST_CHECK(!base->definedFunctions()[0]->isImplemented()); BOOST_REQUIRE(derived); - BOOST_CHECK(derived->annotation().unimplementedDeclarations.empty()); + BOOST_CHECK(derived->annotation().unimplementedDeclarations->empty()); BOOST_CHECK(derived->definedFunctions()[0]->isImplemented()); } @@ -87,9 +87,9 @@ BOOST_AUTO_TEST_CASE(abstract_contract_with_overload) ContractDefinition* base = dynamic_cast(nodes[1].get()); ContractDefinition* derived = dynamic_cast(nodes[2].get()); BOOST_REQUIRE(base); - BOOST_CHECK(!base->annotation().unimplementedDeclarations.empty()); + BOOST_CHECK(!base->annotation().unimplementedDeclarations->empty()); BOOST_REQUIRE(derived); - BOOST_CHECK(!derived->annotation().unimplementedDeclarations.empty()); + BOOST_CHECK(!derived->annotation().unimplementedDeclarations->empty()); } BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor) @@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor) BOOST_CHECK_EQUAL(nodes.size(), 3); ContractDefinition* derived = dynamic_cast(nodes[2].get()); BOOST_REQUIRE(derived); - BOOST_CHECK(!derived->annotation().unimplementedDeclarations.empty()); + BOOST_CHECK(!derived->annotation().unimplementedDeclarations->empty()); } BOOST_AUTO_TEST_CASE(function_canonical_signature) diff --git a/tools/solidityUpgrade/Upgrade060.cpp b/tools/solidityUpgrade/Upgrade060.cpp index 58aa94d15..a5d2f0af6 100644 --- a/tools/solidityUpgrade/Upgrade060.cpp +++ b/tools/solidityUpgrade/Upgrade060.cpp @@ -100,7 +100,7 @@ inline string appendVirtual(FunctionDefinition const& _function) void AbstractContract::endVisit(ContractDefinition const& _contract) { - bool isFullyImplemented = _contract.annotation().unimplementedDeclarations.empty(); + bool isFullyImplemented = _contract.annotation().unimplementedDeclarations->empty(); if ( !isFullyImplemented &&