mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #9839 from ethereum/annotations-optional
Make annotations ``SetOnce`` or ``optional`` where feasible
This commit is contained in:
		
						commit
						9c5e14e309
					
				| @ -51,6 +51,8 @@ bool hasEqualNameAndParameters(T const& _a, B const& _b) | ||||
| 
 | ||||
| bool ContractLevelChecker::check(ContractDefinition const& _contract) | ||||
| { | ||||
| 	_contract.annotation().unimplementedDeclarations = std::vector<Declaration const*>(); | ||||
| 
 | ||||
| 	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( | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -74,7 +74,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So | ||||
| 	for (auto const& node: _sourceUnit.nodes()) | ||||
| 		if (auto imp = dynamic_cast<ImportDirective const*>(node.get())) | ||||
| 		{ | ||||
| 			string const& path = imp->annotation().absolutePath; | ||||
| 			string const& path = *imp->annotation().absolutePath; | ||||
| 			if (!_sourceUnits.count(path)) | ||||
| 			{ | ||||
| 				m_errorReporter.declarationError( | ||||
|  | ||||
| @ -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<RationalNumberType const*>( | ||||
| @ -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<RationalNumberType const*>( | ||||
| 					ConstantEvaluator(m_errorReporter).evaluate(*(_functionCall.arguments())[2]) | ||||
| 				)) | ||||
|  | ||||
| @ -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<ASTPointer<Expression>> 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<Expression const> 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<StructType const&>(*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<FunctionType const*>(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<UserDefinedTypeName const*>(&_newExpression.typeName())) | ||||
| 	{ | ||||
| 		auto contract = dynamic_cast<ContractDefinition const*>(&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<ArraySliceType const*>(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 const*>(&_memberAccess.expression())) | ||||
| 		{ | ||||
| 			annotation.isPure = parentAccess->expression().annotation().isPure; | ||||
| 			bool isPure = *parentAccess->expression().annotation().isPure; | ||||
| 			if (auto const* exprInt = dynamic_cast<Identifier const*>(&parentAccess->expression())) | ||||
| 				if (exprInt->name() == "this" || exprInt->name() == "super") | ||||
| 					annotation.isPure = true; | ||||
| 					isPure = true; | ||||
| 
 | ||||
| 			annotation.isPure = isPure; | ||||
| 		} | ||||
| 
 | ||||
| 	if (auto magicType = dynamic_cast<MagicType const*>(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<VariableDeclaration const*>(annotation.referencedDeclaration)) | ||||
| 		annotation.isPure = annotation.isConstant = variableDeclaration->isConstant(); | ||||
| 		annotation.isPure = isConstant = variableDeclaration->isConstant(); | ||||
| 	else if (dynamic_cast<MagicVariableDeclaration const*>(annotation.referencedDeclaration)) | ||||
| 	{ | ||||
| 		if (dynamic_cast<FunctionType const*>(annotation.type)) | ||||
| 			annotation.isPure = true; | ||||
| 	} | ||||
| 		annotation.isPure = dynamic_cast<FunctionType const*>(annotation.type); | ||||
| 	else if (dynamic_cast<TypeType const*>(annotation.type)) | ||||
| 		annotation.isPure = true; | ||||
| 	else if (dynamic_cast<ModuleType const*>(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<ErrorId, string> { | ||||
| 		if (_expression.annotation().isConstant) | ||||
| 		if (*_expression.annotation().isConstant) | ||||
| 			return { 6520_error, "Cannot assign to a constant variable." }; | ||||
| 
 | ||||
| 		if (auto indexAccess = dynamic_cast<IndexAccess const*>(&_expression)) | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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<std::string> path; | ||||
| 	/// The exported symbols (all global symbols).
 | ||||
| 	std::map<ASTString, std::vector<Declaration const*>> exportedSymbols; | ||||
| 	SetOnce<std::map<ASTString, std::vector<Declaration const*>>> exportedSymbols; | ||||
| 	/// Experimental features.
 | ||||
| 	std::set<ExperimentalFeature> 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<std::string> 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<std::string> 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<Declaration const*> unimplementedDeclarations; | ||||
| 	std::optional<std::vector<Declaration const*>> unimplementedDeclarations; | ||||
| 	/// List of all (direct and indirect) base contracts in order from derived to
 | ||||
| 	/// base, including the contract itself.
 | ||||
| 	std::vector<ContractDefinition const*> 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<bool> isConstant; | ||||
| 	/// Whether the expression is pure, i.e. compile-time constant.
 | ||||
| 	bool isPure = false; | ||||
| 	SetOnce<bool> isPure; | ||||
| 	/// Whether it is an LValue (i.e. something that can be assigned to).
 | ||||
| 	bool isLValue = false; | ||||
| 	SetOnce<bool> 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<bool> lValueOfOrdinaryAssignment; | ||||
| 
 | ||||
| 	/// Types and - if given - names of arguments if the expr. is a function
 | ||||
| 	/// that is called, used for overload resolution
 | ||||
|  | ||||
| @ -39,10 +39,33 @@ | ||||
| #include <vector> | ||||
| #include <algorithm> | ||||
| #include <limits> | ||||
| #include <type_traits> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace solidity::langutil; | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
| 
 | ||||
| template<typename V, template<typename> typename C> | ||||
| void addIfSet(std::vector<pair<string, Json::Value>>& _attributes, string const& _name, C<V> const& _value) | ||||
| { | ||||
| 	if constexpr (std::is_same_v<C<V>, solidity::util::SetOnce<V>>) | ||||
| 	{ | ||||
| 		if (!_value.set()) | ||||
| 			return; | ||||
| 	} | ||||
| 	else if constexpr (std::is_same_v<C<V>, optional<V>>) | ||||
| 	{ | ||||
| 		if (!_value.has_value()) | ||||
| 			return; | ||||
| 	} | ||||
| 
 | ||||
| 	_attributes.emplace_back(_name, *_value); | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| namespace solidity::frontend | ||||
| { | ||||
| 
 | ||||
| @ -181,12 +204,14 @@ void ASTJsonConverter::appendExpressionAttributes( | ||||
| { | ||||
| 	std::vector<pair<string, Json::Value>> 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<pair<string, Json::Value>> 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<pair<string, Json::Value>> 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<pair<string, Json::Value>> 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<pair<string, Json::Value>> 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<pair<string, Json::Value>> 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; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -2115,7 +2115,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 | ||||
| @ -2366,7 +2366,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; | ||||
| @ -2545,7 +2545,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 | ||||
| @ -2652,12 +2652,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 | ||||
| @ -3130,7 +3130,7 @@ string FunctionType::toString(bool _short) const | ||||
| 		auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(m_declaration); | ||||
| 		solAssert(functionDefinition, ""); | ||||
| 		if (auto const* contract = dynamic_cast<ContractDefinition const*>(functionDefinition->scope())) | ||||
| 			name += contract->annotation().canonicalName + "."; | ||||
| 			name += *contract->annotation().canonicalName + "."; | ||||
| 		name += functionDefinition->name(); | ||||
| 	} | ||||
| 	name += '('; | ||||
| @ -3921,7 +3921,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; | ||||
| @ -3929,7 +3929,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 | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -1097,7 +1097,7 @@ void CompilerStack::resolveImports() | ||||
| 			for (ASTPointer<ASTNode> const& node: _source->ast->nodes()) | ||||
| 				if (ImportDirective const* import = dynamic_cast<ImportDirective*>(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<string> 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<string> remappings; | ||||
|  | ||||
| @ -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<T> m_value = std::nullopt; | ||||
| }; | ||||
|  | ||||
| @ -16,7 +16,6 @@ | ||||
|       "baseContracts": [], | ||||
|       "contractDependencies": [], | ||||
|       "contractKind": "contract", | ||||
|       "fullyImplemented": true, | ||||
|       "id": 8, | ||||
|       "linearizedBaseContracts": | ||||
|       [ | ||||
|  | ||||
| @ -25,7 +25,6 @@ | ||||
|           null | ||||
|         ], | ||||
|         "contractKind": "contract", | ||||
|         "fullyImplemented": true, | ||||
|         "linearizedBaseContracts": | ||||
|         [ | ||||
|           8 | ||||
|  | ||||
| @ -16,7 +16,6 @@ | ||||
|       "baseContracts": [], | ||||
|       "contractDependencies": [], | ||||
|       "contractKind": "contract", | ||||
|       "fullyImplemented": true, | ||||
|       "id": 6, | ||||
|       "linearizedBaseContracts": | ||||
|       [ | ||||
|  | ||||
| @ -25,7 +25,6 @@ | ||||
|           null | ||||
|         ], | ||||
|         "contractKind": "contract", | ||||
|         "fullyImplemented": true, | ||||
|         "linearizedBaseContracts": | ||||
|         [ | ||||
|           6 | ||||
|  | ||||
| @ -52,7 +52,7 @@ BOOST_AUTO_TEST_CASE(function_no_implementation) | ||||
| 	std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); | ||||
| 	ContractDefinition* contract = dynamic_cast<ContractDefinition*>(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<ContractDefinition*>(nodes[1].get()); | ||||
| 	ContractDefinition* derived = dynamic_cast<ContractDefinition*>(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<ContractDefinition*>(nodes[1].get()); | ||||
| 	ContractDefinition* derived = dynamic_cast<ContractDefinition*>(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<ContractDefinition*>(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) | ||||
|  | ||||
| @ -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 && | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user