mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Remove parser error recovery mode
This commit is contained in:
		
							parent
							
								
									c96db51013
								
							
						
					
					
						commit
						9adbced98e
					
				| @ -4,6 +4,7 @@ Language Features: | ||||
| 
 | ||||
| 
 | ||||
| Compiler Features: | ||||
|  * Parser: Remove the experimental error recovery mode (``--error-recovery`` / ``settings.parserErrorRecovery``). | ||||
|  * Yul Optimizer: If ``PUSH0`` is supported, favor zero literals over storing zero values in variables. | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -74,56 +74,10 @@ void ParserBase::expectToken(Token _value, bool _advance) | ||||
| { | ||||
| 	Token tok = m_scanner->currentToken(); | ||||
| 	if (tok != _value) | ||||
| 	{ | ||||
| 		std::string const expectedToken = ParserBase::tokenName(_value); | ||||
| 		if (m_parserErrorRecovery) | ||||
| 			parserError(6635_error, "Expected " + expectedToken + " but got " + tokenName(tok)); | ||||
| 		else | ||||
| 			fatalParserError(2314_error, "Expected " + expectedToken + " but got " + tokenName(tok)); | ||||
| 		// Do not advance so that recovery can sync or make use of the current token.
 | ||||
| 		// This is especially useful if the expected token
 | ||||
| 		// is the only one that is missing and is at the end of a construct.
 | ||||
| 		// "{ ... ; }" is such an example.
 | ||||
| 		//        ^
 | ||||
| 		_advance = false; | ||||
| 	} | ||||
| 	if (_advance) | ||||
| 		advance(); | ||||
| } | ||||
| 
 | ||||
| void ParserBase::expectTokenOrConsumeUntil(Token _value, std::string const& _currentNodeName, bool _advance) | ||||
| { | ||||
| 	solAssert(m_inParserRecovery, "The function is supposed to be called during parser recovery only."); | ||||
| 
 | ||||
| 	Token tok = m_scanner->currentToken(); | ||||
| 	if (tok != _value) | ||||
| 	{ | ||||
| 		SourceLocation errorLoc = currentLocation(); | ||||
| 		int startPosition = errorLoc.start; | ||||
| 		while (m_scanner->currentToken() != _value && m_scanner->currentToken() != Token::EOS) | ||||
| 			advance(); | ||||
| 
 | ||||
| 		std::string const expectedToken = ParserBase::tokenName(_value); | ||||
| 		if (m_scanner->currentToken() == Token::EOS) | ||||
| 		{ | ||||
| 			// rollback to where the token started, and raise exception to be caught at a higher level.
 | ||||
| 			m_scanner->setPosition(static_cast<size_t>(startPosition)); | ||||
| 			std::string const msg = "In " + _currentNodeName + ", " + expectedToken + "is expected; got " + ParserBase::tokenName(tok) + " instead."; | ||||
| 			fatalParserError(1957_error, errorLoc, msg); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			parserWarning(3796_error, "Recovered in " + _currentNodeName + " at " + expectedToken + "."); | ||||
| 			m_inParserRecovery = false; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		std::string expectedToken = ParserBase::tokenName(_value); | ||||
| 		parserWarning(3347_error, "Recovered in " + _currentNodeName + " at " + expectedToken + "."); | ||||
| 		m_inParserRecovery = false; | ||||
| 	} | ||||
| 
 | ||||
| 		fatalParserError( | ||||
| 			2314_error, | ||||
| 			"Expected " + ParserBase::tokenName(_value) + " but got " + tokenName(tok) | ||||
| 		); | ||||
| 	if (_advance) | ||||
| 		advance(); | ||||
| } | ||||
|  | ||||
| @ -38,14 +38,9 @@ struct ErrorId; | ||||
| class ParserBase | ||||
| { | ||||
| public: | ||||
| 	/// Set @a _parserErrorRecovery to true for additional error
 | ||||
| 	/// recovery.  This is experimental and intended for use
 | ||||
| 	/// by front-end tools that need partial AST information even
 | ||||
| 	/// when errors occur.
 | ||||
| 	explicit ParserBase(ErrorReporter& errorReporter, bool _parserErrorRecovery = false): m_errorReporter(errorReporter) | ||||
| 	{ | ||||
| 		m_parserErrorRecovery = _parserErrorRecovery; | ||||
| 	} | ||||
| 	explicit ParserBase(ErrorReporter& errorReporter): | ||||
| 		m_errorReporter(errorReporter) | ||||
| 	{} | ||||
| 
 | ||||
| 	virtual ~ParserBase() = default; | ||||
| 
 | ||||
| @ -70,13 +65,9 @@ protected: | ||||
| 	///@{
 | ||||
| 	///@name Helper functions
 | ||||
| 	/// If current token value is not @a _value, throw exception otherwise advance token
 | ||||
| 	//  @a if _advance is true and error recovery is in effect.
 | ||||
| 	//  if @a _advance is true
 | ||||
| 	void expectToken(Token _value, bool _advance = true); | ||||
| 
 | ||||
| 	/// Like expectToken but if there is an error ignores tokens until
 | ||||
| 	/// the expected token or EOS is seen. If EOS is encountered, back up to the error point,
 | ||||
| 	/// and throw an exception so that a higher grammar rule has an opportunity to recover.
 | ||||
| 	void expectTokenOrConsumeUntil(Token _value, std::string const& _currentNodeName, bool _advance = true); | ||||
| 	Token currentToken() const; | ||||
| 	Token peekNextToken() const; | ||||
| 	std::string tokenName(Token _token); | ||||
| @ -108,10 +99,6 @@ protected: | ||||
| 	ErrorReporter& m_errorReporter; | ||||
| 	/// Current recursion depth during parsing.
 | ||||
| 	size_t m_recursionDepth = 0; | ||||
| 	/// True if we are in parser error recovery. Usually this means we are scanning for
 | ||||
| 	/// a synchronization token like ';', or '}'. We use this to reduce cascaded error messages.
 | ||||
| 	bool m_inParserRecovery = false; | ||||
| 	bool m_parserErrorRecovery = false; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -159,14 +159,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) | ||||
| 			SemVerMatchExpressionParser parser(tokens, literals); | ||||
| 			SemVerMatchExpression matchExpression = parser.parse(); | ||||
| 			static SemVerVersion const currentVersion{std::string(VersionString)}; | ||||
| 			if (!matchExpression.matches(currentVersion)) | ||||
| 				m_errorReporter.syntaxError( | ||||
| 					3997_error, | ||||
| 					_pragma.location(), | ||||
| 					"Source file requires different compiler version (current compiler is " + | ||||
| 					std::string(VersionString) + ") - note that nightly builds are considered to be " | ||||
| 					"strictly less than the released version" | ||||
| 				); | ||||
| 			solAssert(matchExpression.matches(currentVersion)); | ||||
| 			m_versionPragmaFound = true; | ||||
| 		} | ||||
| 		catch (SemVerError const&) | ||||
|  | ||||
| @ -352,7 +352,7 @@ bool CompilerStack::parse() | ||||
| 	if (SemVerVersion{std::string(VersionString)}.isPrerelease()) | ||||
| 		m_errorReporter.warning(3805_error, "This is a pre-release compiler version, please do not use it in production."); | ||||
| 
 | ||||
| 	Parser parser{m_errorReporter, m_evmVersion, m_parserErrorRecovery}; | ||||
| 	Parser parser{m_errorReporter, m_evmVersion}; | ||||
| 
 | ||||
| 	std::vector<std::string> sourcesToParse; | ||||
| 	for (auto const& s: m_sources) | ||||
| @ -1104,7 +1104,7 @@ SourceUnit const& CompilerStack::ast(std::string const& _sourceName) const | ||||
| { | ||||
| 	if (m_stackState < Parsed) | ||||
| 		solThrow(CompilerError, "Parsing not yet performed."); | ||||
| 	if (!source(_sourceName).ast && !m_parserErrorRecovery) | ||||
| 	if (!source(_sourceName).ast) | ||||
| 		solThrow(CompilerError, "Parsing was not successful."); | ||||
| 
 | ||||
| 	return *source(_sourceName).ast; | ||||
|  | ||||
| @ -158,14 +158,6 @@ public: | ||||
| 	/// Sets whether to strip revert strings, add additional strings or do nothing at all.
 | ||||
| 	void setRevertStringBehaviour(RevertStrings _revertStrings); | ||||
| 
 | ||||
| 	/// Set whether or not parser error is desired.
 | ||||
| 	/// When called without an argument it will revert to the default.
 | ||||
| 	/// Must be set before parsing.
 | ||||
| 	void setParserErrorRecovery(bool _wantErrorRecovery = false) | ||||
| 	{ | ||||
| 		m_parserErrorRecovery = _wantErrorRecovery; | ||||
| 	} | ||||
| 
 | ||||
| 	/// Sets the pipeline to go through the Yul IR or not.
 | ||||
| 	/// Must be set before parsing.
 | ||||
| 	void setViaIR(bool _viaIR); | ||||
| @ -511,7 +503,6 @@ private: | ||||
| 	bool m_metadataLiteralSources = false; | ||||
| 	MetadataHash m_metadataHash = MetadataHash::IPFS; | ||||
| 	langutil::DebugInfoSelection m_debugInfoSelection = langutil::DebugInfoSelection::Default(); | ||||
| 	bool m_parserErrorRecovery = false; | ||||
| 	State m_stackState = Empty; | ||||
| 	CompilationSourceType m_compilationSourceType = CompilationSourceType::Solidity; | ||||
| 	MetadataFormat m_metadataFormat = defaultMetadataFormat(); | ||||
|  | ||||
| @ -423,7 +423,7 @@ std::optional<Json::Value> checkAuxiliaryInputKeys(Json::Value const& _input) | ||||
| 
 | ||||
| std::optional<Json::Value> checkSettingsKeys(Json::Value const& _input) | ||||
| { | ||||
| 	static std::set<std::string> keys{"parserErrorRecovery", "debug", "evmVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR"}; | ||||
| 	static std::set<std::string> keys{"debug", "evmVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR"}; | ||||
| 	return checkKeys(_input, keys, "settings"); | ||||
| } | ||||
| 
 | ||||
| @ -773,13 +773,6 @@ std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler: | ||||
| 		ret.stopAfter = CompilerStack::State::Parsed; | ||||
| 	} | ||||
| 
 | ||||
| 	if (settings.isMember("parserErrorRecovery")) | ||||
| 	{ | ||||
| 		if (!settings["parserErrorRecovery"].isBool()) | ||||
| 			return formatFatalError(Error::Type::JSONError, "\"settings.parserErrorRecovery\" must be a Boolean."); | ||||
| 		ret.parserErrorRecovery = settings["parserErrorRecovery"].asBool(); | ||||
| 	} | ||||
| 
 | ||||
| 	if (settings.isMember("viaIR")) | ||||
| 	{ | ||||
| 		if (!settings["viaIR"].isBool()) | ||||
| @ -1166,7 +1159,6 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting | ||||
| 		compilerStack.addSMTLib2Response(smtLib2Response.first, smtLib2Response.second); | ||||
| 	compilerStack.setViaIR(_inputsAndSettings.viaIR); | ||||
| 	compilerStack.setEVMVersion(_inputsAndSettings.evmVersion); | ||||
| 	compilerStack.setParserErrorRecovery(_inputsAndSettings.parserErrorRecovery); | ||||
| 	compilerStack.setRemappings(std::move(_inputsAndSettings.remappings)); | ||||
| 	compilerStack.setOptimiserSettings(std::move(_inputsAndSettings.optimiserSettings)); | ||||
| 	compilerStack.setRevertStringBehaviour(_inputsAndSettings.revertStrings); | ||||
|  | ||||
| @ -72,7 +72,6 @@ private: | ||||
| 	{ | ||||
| 		std::string language; | ||||
| 		Json::Value errors; | ||||
| 		bool parserErrorRecovery = false; | ||||
| 		CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful; | ||||
| 		std::map<std::string, std::string> sources; | ||||
| 		std::map<util::h256, std::string> smtLib2Responses; | ||||
|  | ||||
| @ -173,16 +173,13 @@ void Parser::parsePragmaVersion(SourceLocation const& _location, std::vector<Tok | ||||
| 		static SemVerVersion const currentVersion{std::string(VersionString)}; | ||||
| 		// FIXME: only match for major version incompatibility
 | ||||
| 		if (!matchExpression.matches(currentVersion)) | ||||
| 			// If m_parserErrorRecovery is true, the same message will appear from SyntaxChecker::visit(),
 | ||||
| 			// so we don't need to report anything here.
 | ||||
| 			if (!m_parserErrorRecovery) | ||||
| 				m_errorReporter.fatalParserError( | ||||
| 					5333_error, | ||||
| 					_location, | ||||
| 					"Source file requires different compiler version (current compiler is " + | ||||
| 					std::string(VersionString) + ") - note that nightly builds are considered to be " | ||||
| 					"strictly less than the released version" | ||||
| 				); | ||||
| 			m_errorReporter.fatalParserError( | ||||
| 				5333_error, | ||||
| 				_location, | ||||
| 				"Source file requires different compiler version (current compiler is " + | ||||
| 				std::string(VersionString) + ") - note that nightly builds are considered to be " | ||||
| 				"strictly less than the released version" | ||||
| 			); | ||||
| 	} | ||||
| 	catch (SemVerError const& matchError) | ||||
| 	{ | ||||
| @ -364,78 +361,62 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition() | ||||
| 	std::vector<ASTPointer<InheritanceSpecifier>> baseContracts; | ||||
| 	std::vector<ASTPointer<ASTNode>> subNodes; | ||||
| 	std::pair<ContractKind, bool> contractKind{}; | ||||
| 	try | ||||
| 	{ | ||||
| 		documentation = parseStructuredDocumentation(); | ||||
| 		contractKind = parseContractKind(); | ||||
| 		tie(name, nameLocation) = expectIdentifierWithLocation(); | ||||
| 		if (m_scanner->currentToken() == Token::Is) | ||||
| 			do | ||||
| 			{ | ||||
| 				advance(); | ||||
| 				baseContracts.push_back(parseInheritanceSpecifier()); | ||||
| 			} | ||||
| 			while (m_scanner->currentToken() == Token::Comma); | ||||
| 		expectToken(Token::LBrace); | ||||
| 		while (true) | ||||
| 	documentation = parseStructuredDocumentation(); | ||||
| 	contractKind = parseContractKind(); | ||||
| 	std::tie(name, nameLocation) = expectIdentifierWithLocation(); | ||||
| 	if (m_scanner->currentToken() == Token::Is) | ||||
| 		do | ||||
| 		{ | ||||
| 			Token currentTokenValue = m_scanner->currentToken(); | ||||
| 			if (currentTokenValue == Token::RBrace) | ||||
| 				break; | ||||
| 			else if ( | ||||
| 				(currentTokenValue == Token::Function && m_scanner->peekNextToken() != Token::LParen) || | ||||
| 				currentTokenValue == Token::Constructor || | ||||
| 				currentTokenValue == Token::Receive || | ||||
| 				currentTokenValue == Token::Fallback | ||||
| 			) | ||||
| 				subNodes.push_back(parseFunctionDefinition()); | ||||
| 			else if (currentTokenValue == Token::Struct) | ||||
| 				subNodes.push_back(parseStructDefinition()); | ||||
| 			else if (currentTokenValue == Token::Enum) | ||||
| 				subNodes.push_back(parseEnumDefinition()); | ||||
| 			else if (currentTokenValue == Token::Type) | ||||
| 				subNodes.push_back(parseUserDefinedValueTypeDefinition()); | ||||
| 			else if ( | ||||
| 				// Workaround because `error` is not a keyword.
 | ||||
| 				currentTokenValue == Token::Identifier && | ||||
| 				currentLiteral() == "error" && | ||||
| 				m_scanner->peekNextToken() == Token::Identifier && | ||||
| 				m_scanner->peekNextNextToken() == Token::LParen | ||||
| 			) | ||||
| 				subNodes.push_back(parseErrorDefinition()); | ||||
| 			else if (variableDeclarationStart()) | ||||
| 			{ | ||||
| 				VarDeclParserOptions options; | ||||
| 				options.kind = VarDeclKind::State; | ||||
| 				options.allowInitialValue = true; | ||||
| 				subNodes.push_back(parseVariableDeclaration(options)); | ||||
| 				expectToken(Token::Semicolon); | ||||
| 			} | ||||
| 			else if (currentTokenValue == Token::Modifier) | ||||
| 				subNodes.push_back(parseModifierDefinition()); | ||||
| 			else if (currentTokenValue == Token::Event) | ||||
| 				subNodes.push_back(parseEventDefinition()); | ||||
| 			else if (currentTokenValue == Token::Using) | ||||
| 				subNodes.push_back(parseUsingDirective()); | ||||
| 			else | ||||
| 				fatalParserError(9182_error, "Function, variable, struct or modifier declaration expected."); | ||||
| 			advance(); | ||||
| 			baseContracts.push_back(parseInheritanceSpecifier()); | ||||
| 		} | ||||
| 	} | ||||
| 	catch (FatalError const&) | ||||
| 		while (m_scanner->currentToken() == Token::Comma); | ||||
| 	expectToken(Token::LBrace); | ||||
| 	while (true) | ||||
| 	{ | ||||
| 		if ( | ||||
| 			!m_errorReporter.hasErrors() || | ||||
| 			!m_parserErrorRecovery || | ||||
| 			m_errorReporter.hasExcessiveErrors() | ||||
| 		Token currentTokenValue = m_scanner->currentToken(); | ||||
| 		if (currentTokenValue == Token::RBrace) | ||||
| 			break; | ||||
| 		else if ( | ||||
| 			(currentTokenValue == Token::Function && m_scanner->peekNextToken() != Token::LParen) || | ||||
| 			currentTokenValue == Token::Constructor || | ||||
| 			currentTokenValue == Token::Receive || | ||||
| 			currentTokenValue == Token::Fallback | ||||
| 		) | ||||
| 			BOOST_THROW_EXCEPTION(FatalError()); /* Don't try to recover here. */ | ||||
| 		m_inParserRecovery = true; | ||||
| 			subNodes.push_back(parseFunctionDefinition()); | ||||
| 		else if (currentTokenValue == Token::Struct) | ||||
| 			subNodes.push_back(parseStructDefinition()); | ||||
| 		else if (currentTokenValue == Token::Enum) | ||||
| 			subNodes.push_back(parseEnumDefinition()); | ||||
| 		else if (currentTokenValue == Token::Type) | ||||
| 			subNodes.push_back(parseUserDefinedValueTypeDefinition()); | ||||
| 		else if ( | ||||
| 			// Workaround because `error` is not a keyword.
 | ||||
| 			currentTokenValue == Token::Identifier && | ||||
| 			currentLiteral() == "error" && | ||||
| 			m_scanner->peekNextToken() == Token::Identifier && | ||||
| 			m_scanner->peekNextNextToken() == Token::LParen | ||||
| 		) | ||||
| 			subNodes.push_back(parseErrorDefinition()); | ||||
| 		else if (variableDeclarationStart()) | ||||
| 		{ | ||||
| 			VarDeclParserOptions options; | ||||
| 			options.kind = VarDeclKind::State; | ||||
| 			options.allowInitialValue = true; | ||||
| 			subNodes.push_back(parseVariableDeclaration(options)); | ||||
| 			expectToken(Token::Semicolon); | ||||
| 		} | ||||
| 		else if (currentTokenValue == Token::Modifier) | ||||
| 			subNodes.push_back(parseModifierDefinition()); | ||||
| 		else if (currentTokenValue == Token::Event) | ||||
| 			subNodes.push_back(parseEventDefinition()); | ||||
| 		else if (currentTokenValue == Token::Using) | ||||
| 			subNodes.push_back(parseUsingDirective()); | ||||
| 		else | ||||
| 			fatalParserError(9182_error, "Function, variable, struct or modifier declaration expected."); | ||||
| 	} | ||||
| 	nodeFactory.markEndPosition(); | ||||
| 	if (m_inParserRecovery) | ||||
| 		expectTokenOrConsumeUntil(Token::RBrace, "ContractDefinition"); | ||||
| 	else | ||||
| 		expectToken(Token::RBrace); | ||||
| 	expectToken(Token::RBrace); | ||||
| 	return nodeFactory.createNode<ContractDefinition>( | ||||
| 		name, | ||||
| 		nameLocation, | ||||
| @ -1295,26 +1276,10 @@ ASTPointer<Block> Parser::parseBlock(bool _allowUnchecked, ASTPointer<ASTString> | ||||
| 	} | ||||
| 	expectToken(Token::LBrace); | ||||
| 	std::vector<ASTPointer<Statement>> statements; | ||||
| 	try | ||||
| 	{ | ||||
| 		while (m_scanner->currentToken() != Token::RBrace) | ||||
| 			statements.push_back(parseStatement(true)); | ||||
| 		nodeFactory.markEndPosition(); | ||||
| 	} | ||||
| 	catch (FatalError const&) | ||||
| 	{ | ||||
| 		if ( | ||||
| 			!m_errorReporter.hasErrors() || | ||||
| 			!m_parserErrorRecovery || | ||||
| 			m_errorReporter.hasExcessiveErrors() | ||||
| 		) | ||||
| 			BOOST_THROW_EXCEPTION(FatalError()); /* Don't try to recover here. */ | ||||
| 		m_inParserRecovery = true; | ||||
| 	} | ||||
| 	if (m_inParserRecovery) | ||||
| 		expectTokenOrConsumeUntil(Token::RBrace, "Block"); | ||||
| 	else | ||||
| 		expectToken(Token::RBrace); | ||||
| 	while (m_scanner->currentToken() != Token::RBrace) | ||||
| 		statements.push_back(parseStatement(true)); | ||||
| 	nodeFactory.markEndPosition(); | ||||
| 	expectToken(Token::RBrace); | ||||
| 	return nodeFactory.createNode<Block>(_docString, unchecked, statements); | ||||
| } | ||||
| 
 | ||||
| @ -1323,86 +1288,70 @@ ASTPointer<Statement> Parser::parseStatement(bool _allowUnchecked) | ||||
| 	RecursionGuard recursionGuard(*this); | ||||
| 	ASTPointer<ASTString> docString; | ||||
| 	ASTPointer<Statement> statement; | ||||
| 	try | ||||
| 	if (m_scanner->currentCommentLiteral() != "") | ||||
| 		docString = std::make_shared<ASTString>(m_scanner->currentCommentLiteral()); | ||||
| 	switch (m_scanner->currentToken()) | ||||
| 	{ | ||||
| 		if (m_scanner->currentCommentLiteral() != "") | ||||
| 			docString = std::make_shared<ASTString>(m_scanner->currentCommentLiteral()); | ||||
| 		switch (m_scanner->currentToken()) | ||||
| 		{ | ||||
| 		case Token::If: | ||||
| 			return parseIfStatement(docString); | ||||
| 		case Token::While: | ||||
| 			return parseWhileStatement(docString); | ||||
| 		case Token::Do: | ||||
| 			return parseDoWhileStatement(docString); | ||||
| 		case Token::For: | ||||
| 			return parseForStatement(docString); | ||||
| 		case Token::Unchecked: | ||||
| 		case Token::LBrace: | ||||
| 			return parseBlock(_allowUnchecked, docString); | ||||
| 		case Token::Continue: | ||||
| 			statement = ASTNodeFactory(*this).createNode<Continue>(docString); | ||||
| 			advance(); | ||||
| 			break; | ||||
| 		case Token::Break: | ||||
| 			statement = ASTNodeFactory(*this).createNode<Break>(docString); | ||||
| 			advance(); | ||||
| 			break; | ||||
| 		case Token::Return: | ||||
| 		{ | ||||
| 			ASTNodeFactory nodeFactory(*this); | ||||
| 			ASTPointer<Expression> expression; | ||||
| 			if (advance() != Token::Semicolon) | ||||
| 				{ | ||||
| 					expression = parseExpression(); | ||||
| 					nodeFactory.setEndPositionFromNode(expression); | ||||
| 				} | ||||
| 			statement = nodeFactory.createNode<Return>(docString, expression); | ||||
| 				break; | ||||
| 		} | ||||
| 		case Token::Throw: | ||||
| 		{ | ||||
| 			statement = ASTNodeFactory(*this).createNode<Throw>(docString); | ||||
| 			advance(); | ||||
| 			break; | ||||
| 		} | ||||
| 		case Token::Try: | ||||
| 			return parseTryStatement(docString); | ||||
| 		case Token::Assembly: | ||||
| 			return parseInlineAssembly(docString); | ||||
| 		case Token::Emit: | ||||
| 			statement = parseEmitStatement(docString); | ||||
| 			break; | ||||
| 		case Token::Identifier: | ||||
| 			if (m_scanner->currentLiteral() == "revert" && m_scanner->peekNextToken() == Token::Identifier) | ||||
| 				statement = parseRevertStatement(docString); | ||||
| 			else if (m_insideModifier && m_scanner->currentLiteral() == "_") | ||||
| 	case Token::If: | ||||
| 		return parseIfStatement(docString); | ||||
| 	case Token::While: | ||||
| 		return parseWhileStatement(docString); | ||||
| 	case Token::Do: | ||||
| 		return parseDoWhileStatement(docString); | ||||
| 	case Token::For: | ||||
| 		return parseForStatement(docString); | ||||
| 	case Token::Unchecked: | ||||
| 	case Token::LBrace: | ||||
| 		return parseBlock(_allowUnchecked, docString); | ||||
| 	case Token::Continue: | ||||
| 		statement = ASTNodeFactory(*this).createNode<Continue>(docString); | ||||
| 		advance(); | ||||
| 		break; | ||||
| 	case Token::Break: | ||||
| 		statement = ASTNodeFactory(*this).createNode<Break>(docString); | ||||
| 		advance(); | ||||
| 		break; | ||||
| 	case Token::Return: | ||||
| 	{ | ||||
| 		ASTNodeFactory nodeFactory(*this); | ||||
| 		ASTPointer<Expression> expression; | ||||
| 		if (advance() != Token::Semicolon) | ||||
| 			{ | ||||
| 				statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString); | ||||
| 				advance(); | ||||
| 				expression = parseExpression(); | ||||
| 				nodeFactory.setEndPositionFromNode(expression); | ||||
| 			} | ||||
| 			else | ||||
| 				statement = parseSimpleStatement(docString); | ||||
| 		statement = nodeFactory.createNode<Return>(docString, expression); | ||||
| 			break; | ||||
| 		default: | ||||
| 			statement = parseSimpleStatement(docString); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	catch (FatalError const&) | ||||
| 	case Token::Throw: | ||||
| 	{ | ||||
| 		if ( | ||||
| 			!m_errorReporter.hasErrors() || | ||||
| 			!m_parserErrorRecovery || | ||||
| 			m_errorReporter.hasExcessiveErrors() | ||||
| 		) | ||||
| 			BOOST_THROW_EXCEPTION(FatalError()); /* Don't try to recover here. */ | ||||
| 		m_inParserRecovery = true; | ||||
| 		statement = ASTNodeFactory(*this).createNode<Throw>(docString); | ||||
| 		advance(); | ||||
| 		break; | ||||
| 	} | ||||
| 	if (m_inParserRecovery) | ||||
| 		expectTokenOrConsumeUntil(Token::Semicolon, "Statement"); | ||||
| 	else | ||||
| 		expectToken(Token::Semicolon); | ||||
| 	case Token::Try: | ||||
| 		return parseTryStatement(docString); | ||||
| 	case Token::Assembly: | ||||
| 		return parseInlineAssembly(docString); | ||||
| 	case Token::Emit: | ||||
| 		statement = parseEmitStatement(docString); | ||||
| 		break; | ||||
| 	case Token::Identifier: | ||||
| 		if (m_scanner->currentLiteral() == "revert" && m_scanner->peekNextToken() == Token::Identifier) | ||||
| 			statement = parseRevertStatement(docString); | ||||
| 		else if (m_insideModifier && m_scanner->currentLiteral() == "_") | ||||
| 		{ | ||||
| 			statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString); | ||||
| 			advance(); | ||||
| 		} | ||||
| 		else | ||||
| 			statement = parseSimpleStatement(docString); | ||||
| 		break; | ||||
| 	default: | ||||
| 		statement = parseSimpleStatement(docString); | ||||
| 		break; | ||||
| 	} | ||||
| 	expectToken(Token::Semicolon); | ||||
| 	return statement; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -40,10 +40,9 @@ class Parser: public langutil::ParserBase | ||||
| public: | ||||
| 	explicit Parser( | ||||
| 		langutil::ErrorReporter& _errorReporter, | ||||
| 		langutil::EVMVersion _evmVersion, | ||||
| 		bool _errorRecovery = false | ||||
| 		langutil::EVMVersion _evmVersion | ||||
| 	): | ||||
| 		ParserBase(_errorReporter, _errorRecovery), | ||||
| 		ParserBase(_errorReporter), | ||||
| 		m_evmVersion(_evmVersion) | ||||
| 	{} | ||||
| 
 | ||||
|  | ||||
| @ -171,7 +171,6 @@ def print_ids_per_file(ids, id_to_file_names, top_dir): | ||||
| 
 | ||||
| def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): | ||||
|     test_sub_dirs = [ | ||||
|         path.join("test", "libsolidity", "errorRecoveryTests"), | ||||
|         path.join("test", "libsolidity", "smtCheckerTests"), | ||||
|         path.join("test", "libsolidity", "syntaxTests"), | ||||
|         path.join("test", "libyul", "yulSyntaxTests") | ||||
|  | ||||
| @ -792,10 +792,7 @@ void CommandLineInterface::compile() | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			m_compiler->setSources(m_fileReader.sourceUnits()); | ||||
| 			m_compiler->setParserErrorRecovery(m_options.input.errorRecovery); | ||||
| 		} | ||||
| 
 | ||||
| 		bool successful = m_compiler->compile(m_options.output.stopAfter); | ||||
| 
 | ||||
|  | ||||
| @ -43,7 +43,6 @@ static string const g_strBasePath = "base-path"; | ||||
| static string const g_strIncludePath = "include-path"; | ||||
| static string const g_strAssemble = "assemble"; | ||||
| static string const g_strCombinedJson = "combined-json"; | ||||
| static string const g_strErrorRecovery = "error-recovery"; | ||||
| static string const g_strEVM = "evm"; | ||||
| static string const g_strEVMVersion = "evm-version"; | ||||
| static string const g_strEOFVersion = "experimental-eof-version"; | ||||
| @ -226,7 +225,6 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex | ||||
| 		input.includePaths == _other.input.includePaths && | ||||
| 		input.allowedDirectories == _other.input.allowedDirectories && | ||||
| 		input.ignoreMissingFiles == _other.input.ignoreMissingFiles && | ||||
| 		input.errorRecovery == _other.input.errorRecovery && | ||||
| 		output.dir == _other.output.dir && | ||||
| 		output.overwriteFiles == _other.output.overwriteFiles && | ||||
| 		output.evmVersion == _other.output.evmVersion && | ||||
| @ -570,10 +568,6 @@ General Information)").c_str(), | ||||
| 			g_strIgnoreMissingFiles.c_str(), | ||||
| 			"Ignore missing files." | ||||
| 		) | ||||
| 		( | ||||
| 			g_strErrorRecovery.c_str(), | ||||
| 			"Enables additional parser error recovery." | ||||
| 		) | ||||
| 	; | ||||
| 	desc.add(inputOptions); | ||||
| 
 | ||||
| @ -962,7 +956,6 @@ void CommandLineParser::processArgs() | ||||
| 
 | ||||
| 	map<string, set<InputMode>> validOptionInputModeCombinations = { | ||||
| 		// TODO: This should eventually contain all options.
 | ||||
| 		{g_strErrorRecovery, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, | ||||
| 		{g_strExperimentalViaIR, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, | ||||
| 		{g_strViaIR, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, | ||||
| 		{g_strMetadataLiteral, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, | ||||
| @ -1369,8 +1362,6 @@ void CommandLineParser::processArgs() | ||||
| 		m_args.count(g_strModelCheckerTargets) || | ||||
| 		m_args.count(g_strModelCheckerTimeout); | ||||
| 	m_options.output.viaIR = (m_args.count(g_strExperimentalViaIR) > 0 || m_args.count(g_strViaIR) > 0); | ||||
| 	if (m_options.input.mode == InputMode::Compiler) | ||||
| 		m_options.input.errorRecovery = (m_args.count(g_strErrorRecovery) > 0); | ||||
| 
 | ||||
| 	solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport); | ||||
| } | ||||
|  | ||||
| @ -174,7 +174,6 @@ struct CommandLineOptions | ||||
| 		std::vector<boost::filesystem::path> includePaths; | ||||
| 		FileReader::FileSystemPathSet allowedDirectories; | ||||
| 		bool ignoreMissingFiles = false; | ||||
| 		bool errorRecovery = false; | ||||
| 	} input; | ||||
| 
 | ||||
| 	struct | ||||
|  | ||||
| @ -71,7 +71,6 @@ Testsuite const g_interactiveTestsuites[] = { | ||||
| 	{"Yul Syntax",             "libyul",      "yulSyntaxTests",        false, false, &yul::test::SyntaxTest::create}, | ||||
| 	{"EVM Code Transform",     "libyul",      "evmCodeTransform",      false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}}, | ||||
| 	{"Syntax",                 "libsolidity", "syntaxTests",           false, false, &SyntaxTest::create}, | ||||
| 	{"Error Recovery",         "libsolidity", "errorRecoveryTests",    false, false, &SyntaxTest::createErrorRecovery}, | ||||
| 	{"Semantic",               "libsolidity", "semanticTests",         false, true,  &SemanticTest::create}, | ||||
| 	{"JSON AST",               "libsolidity", "ASTJSON",               false, false, &ASTJSONTest::create}, | ||||
| 	{"JSON ABI",               "libsolidity", "ABIJson",               false, false, &ABIJsonTest::create}, | ||||
|  | ||||
| @ -1 +0,0 @@ | ||||
| --error-recovery --ast-compact-json --pretty-json  --hashes | ||||
| @ -1,11 +0,0 @@ | ||||
| Error: Expected primary expression. | ||||
|  --> recovery_ast_constructor/input.sol:6:27: | ||||
|   | | ||||
| 6 |     balances[tx.origin] = ; // missing RHS. | ||||
|   |                           ^ | ||||
| 
 | ||||
| Warning: Recovered in Statement at ';'. | ||||
|  --> recovery_ast_constructor/input.sol:6:27: | ||||
|   | | ||||
| 6 |     balances[tx.origin] = ; // missing RHS. | ||||
|   |                           ^ | ||||
| @ -1 +0,0 @@ | ||||
| 1 | ||||
| @ -1,13 +0,0 @@ | ||||
| // SPDX-License-Identifier: GPL-3.0 | ||||
| pragma solidity >=0.0.0; | ||||
| 
 | ||||
| contract Error1 { | ||||
|   constructor() { | ||||
|     balances[tx.origin] = ; // missing RHS. | ||||
|   } | ||||
| 
 | ||||
|   // This function parses properly | ||||
|   function five() public view returns(uint) { | ||||
|     return 5; | ||||
|   } | ||||
| } | ||||
| @ -1 +0,0 @@ | ||||
| --error-recovery | ||||
| @ -1,5 +0,0 @@ | ||||
| Error: Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition. | ||||
|  --> recovery_ast_empty_contract/input.sol:3:1: | ||||
|   | | ||||
| 3 | c | ||||
|   | ^ | ||||
| @ -1 +0,0 @@ | ||||
| 1 | ||||
| @ -1,3 +0,0 @@ | ||||
| // SPDX-License-Identifier: GPL-3.0 | ||||
| pragma 0.5.11; | ||||
| c | ||||
| @ -1,18 +0,0 @@ | ||||
| { | ||||
| 	"language": "Solidity", | ||||
| 	"sources": | ||||
| 	{ | ||||
| 		"A": | ||||
| 		{ | ||||
| 			"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract Errort6 { using foo for  ; /* missing type name */ }" | ||||
| 		} | ||||
| 	}, | ||||
| 	"settings": | ||||
| 	{ | ||||
| 		"parserErrorRecovery": true, | ||||
| 		"outputSelection": | ||||
| 		{ | ||||
| 			"*": { "": ["ast"] } | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @ -1,46 +0,0 @@ | ||||
| { | ||||
|     "errors": | ||||
|     [ | ||||
|         { | ||||
|             "component": "general", | ||||
|             "errorCode": "3546", | ||||
|             "formattedMessage": "ParserError: Expected type name | ||||
|  --> A:2:58: | ||||
|   | | ||||
| 2 | pragma solidity >=0.0; contract Errort6 { using foo for  ; /* missing type name */ } | ||||
|   |                                                          ^ | ||||
| 
 | ||||
| ", | ||||
|             "message": "Expected type name", | ||||
|             "severity": "error", | ||||
|             "sourceLocation": | ||||
|             { | ||||
|                 "end": 94, | ||||
|                 "file": "A", | ||||
|                 "start": 93 | ||||
|             }, | ||||
|             "type": "ParserError" | ||||
|         }, | ||||
|         { | ||||
|             "component": "general", | ||||
|             "errorCode": "3796", | ||||
|             "formattedMessage": "Warning: Recovered in ContractDefinition at '}'. | ||||
|  --> A:2:84: | ||||
|   | | ||||
| 2 | pragma solidity >=0.0; contract Errort6 { using foo for  ; /* missing type name */ } | ||||
|   |                                                                                    ^ | ||||
| 
 | ||||
| ", | ||||
|             "message": "Recovered in ContractDefinition at '}'.", | ||||
|             "severity": "warning", | ||||
|             "sourceLocation": | ||||
|             { | ||||
|                 "end": 120, | ||||
|                 "file": "A", | ||||
|                 "start": 119 | ||||
|             }, | ||||
|             "type": "Warning" | ||||
|         } | ||||
|     ], | ||||
|     "sources": {} | ||||
| } | ||||
| @ -47,15 +47,12 @@ AnalysisFramework::parseAnalyseAndReturnError( | ||||
| 	string const& _source, | ||||
| 	bool _reportWarnings, | ||||
| 	bool _insertLicenseAndVersionPragma, | ||||
| 	bool _allowMultipleErrors, | ||||
| 	bool _allowRecoveryErrors | ||||
| 	bool _allowMultipleErrors | ||||
| ) | ||||
| { | ||||
| 	compiler().reset(); | ||||
| 	compiler().setSources({{"", _insertLicenseAndVersionPragma ? withPreamble(_source) : _source}}); | ||||
| 	compiler().setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); | ||||
| 	compiler().setParserErrorRecovery(_allowRecoveryErrors); | ||||
| 	_allowMultipleErrors = _allowMultipleErrors || _allowRecoveryErrors; | ||||
| 	if (!compiler().parse()) | ||||
| 	{ | ||||
| 		BOOST_FAIL("Parsing contract failed in analysis test suite:" + formatErrors(compiler().errors())); | ||||
|  | ||||
| @ -48,8 +48,7 @@ protected: | ||||
| 		std::string const& _source, | ||||
| 		bool _reportWarnings = false, | ||||
| 		bool _insertLicenseAndVersionPragma = true, | ||||
| 		bool _allowMultipleErrors = false, | ||||
| 		bool _allowRecoveryErrors = false | ||||
| 		bool _allowMultipleErrors = false | ||||
| 	); | ||||
| 	virtual ~AnalysisFramework() = default; | ||||
| 
 | ||||
|  | ||||
| @ -39,14 +39,13 @@ namespace solidity::frontend::test | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
| ASTPointer<ContractDefinition> parseText(std::string const& _source, ErrorList& _errors, bool errorRecovery = false) | ||||
| ASTPointer<ContractDefinition> parseText(std::string const& _source, ErrorList& _errors) | ||||
| { | ||||
| 	ErrorReporter errorReporter(_errors); | ||||
| 	auto charStream = CharStream(_source, ""); | ||||
| 	ASTPointer<SourceUnit> sourceUnit = Parser( | ||||
| 		errorReporter, | ||||
| 		solidity::test::CommonOptions::get().evmVersion(), | ||||
| 		errorRecovery | ||||
| 		solidity::test::CommonOptions::get().evmVersion() | ||||
| 	).parse(charStream); | ||||
| 	if (!sourceUnit) | ||||
| 		return ASTPointer<ContractDefinition>(); | ||||
| @ -78,12 +77,12 @@ bool successParse(std::string const& _source) | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| Error getError(std::string const& _source, bool errorRecovery = false) | ||||
| Error getError(std::string const& _source) | ||||
| { | ||||
| 	ErrorList errors; | ||||
| 	try | ||||
| 	{ | ||||
| 		parseText(_source, errors, errorRecovery); | ||||
| 		parseText(_source, errors); | ||||
| 	} | ||||
| 	catch (FatalError const& /*_exception*/) | ||||
| 	{ | ||||
|  | ||||
| @ -282,43 +282,6 @@ BOOST_AUTO_TEST_CASE(smoke_test) | ||||
| 	BOOST_CHECK(containsAtMostWarnings(result)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(error_recovery_field) | ||||
| { | ||||
| 	auto input = R"( | ||||
| 	{ | ||||
| 		"language": "Solidity", | ||||
| 		"settings": { | ||||
| 			"parserErrorRecovery": "1" | ||||
| 		}, | ||||
| 		"sources": { | ||||
| 			"empty": { | ||||
| 				"content": "" | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	)"; | ||||
| 
 | ||||
| 	Json::Value result = compile(input); | ||||
| 	BOOST_CHECK(containsError(result, "JSONError", "\"settings.parserErrorRecovery\" must be a Boolean.")); | ||||
| 
 | ||||
| 	input = R"( | ||||
| 	{ | ||||
| 		"language": "Solidity", | ||||
| 		"settings": { | ||||
| 			"parserErrorRecovery": true | ||||
| 		}, | ||||
| 		"sources": { | ||||
| 			"empty": { | ||||
| 				"content": "" | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	)"; | ||||
| 
 | ||||
| 	result = compile(input); | ||||
| 	BOOST_CHECK(containsAtMostWarnings(result)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(optimizer_enabled_not_boolean) | ||||
| { | ||||
| 	char const* input = R"( | ||||
|  | ||||
| @ -38,10 +38,9 @@ using namespace solidity::frontend::test; | ||||
| using namespace boost::unit_test; | ||||
| namespace fs = boost::filesystem; | ||||
| 
 | ||||
| SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion, bool _parserErrorRecovery): CommonSyntaxTest(_filename, _evmVersion) | ||||
| SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): CommonSyntaxTest(_filename, _evmVersion) | ||||
| { | ||||
| 	m_optimiseYul = m_reader.boolSetting("optimize-yul", true); | ||||
| 	m_parserErrorRecovery = _parserErrorRecovery; | ||||
| } | ||||
| 
 | ||||
| void SyntaxTest::setupCompiler() | ||||
| @ -49,7 +48,6 @@ void SyntaxTest::setupCompiler() | ||||
| 	compiler().reset(); | ||||
| 	compiler().setSources(withPreamble(m_sources.sources)); | ||||
| 	compiler().setEVMVersion(m_evmVersion); | ||||
| 	compiler().setParserErrorRecovery(m_parserErrorRecovery); | ||||
| 	compiler().setOptimiserSettings( | ||||
| 		m_optimiseYul ? | ||||
| 		OptimiserSettings::full() : | ||||
|  | ||||
| @ -39,13 +39,9 @@ class SyntaxTest: public AnalysisFramework, public solidity::test::CommonSyntaxT | ||||
| public: | ||||
| 	static std::unique_ptr<TestCase> create(Config const& _config) | ||||
| 	{ | ||||
| 		return std::make_unique<SyntaxTest>(_config.filename, _config.evmVersion, false); | ||||
| 		return std::make_unique<SyntaxTest>(_config.filename, _config.evmVersion); | ||||
| 	} | ||||
| 	static std::unique_ptr<TestCase> createErrorRecovery(Config const& _config) | ||||
| 	{ | ||||
| 		return std::make_unique<SyntaxTest>(_config.filename, _config.evmVersion, true); | ||||
| 	} | ||||
| 	SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion, bool _parserErrorRecovery = false); | ||||
| 	SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion); | ||||
| 
 | ||||
| protected: | ||||
| 	virtual void setupCompiler(); | ||||
| @ -53,7 +49,6 @@ protected: | ||||
| 	virtual void filterObtainedErrors(); | ||||
| 
 | ||||
| 	bool m_optimiseYul = true; | ||||
| 	bool m_parserErrorRecovery = false; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -1,20 +0,0 @@ | ||||
| pragma solidity >=0.0.0; | ||||
| 
 | ||||
| contract Error1 { | ||||
|   constructor() { | ||||
|     balances[tx.origin] = ; // missing RHS. | ||||
|   } | ||||
| 
 | ||||
|   // Without error recovery we stop due to the above error. | ||||
|   // Error recovery however recovers at the above ';' | ||||
|   // There should be an AST for the above, albeit with error | ||||
|   // nodes. | ||||
| 
 | ||||
|   // This function parses properly and should give AST info. | ||||
|   function five() public view returns(uint) { | ||||
|     return 5; | ||||
|   } | ||||
| } | ||||
| // ---- | ||||
| // ParserError 6933: (88-89): Expected primary expression. | ||||
| // Warning 3347: (88-89): Recovered in Statement at ';'. | ||||
| @ -1,6 +0,0 @@ | ||||
| contract Errort6 { | ||||
|   using foo for  ; // missing type name | ||||
| } | ||||
| // ---- | ||||
| // ParserError 3546: (36-37): Expected type name | ||||
| // Warning 3796: (59-60): Recovered in ContractDefinition at '}'. | ||||
| @ -1,15 +0,0 @@ | ||||
| pragma solidity >=0.0.0; | ||||
| 
 | ||||
| // Example to show why deleting the token at the | ||||
| // is bad when error recovery is in effect. Here, ")" is missing | ||||
| // and there is a ";" instead. That causes us to | ||||
| // not be able to synchronize to ';'. Advance again and | ||||
| // '}' is deleted and then we can't synchronize the contract. | ||||
| // There should be an an AST created this contract (with errors). | ||||
| contract Error2 { | ||||
| 	mapping (address => uint balances; // missing ) before "balances" | ||||
| } | ||||
| // ---- | ||||
| // ParserError 6635: (425-426): Expected ')' but got ';' | ||||
| // ParserError 6635: (425-426): Expected identifier but got ';' | ||||
| // ParserError 6635: (458-459): Expected ';' but got '}' | ||||
| @ -1,23 +0,0 @@ | ||||
| // Example which where scanning hits EOS, so we reset. | ||||
| // Here we recover in the contractDefinition. | ||||
| // There should be an an AST created this contract (with errors). | ||||
| contract Error2 { | ||||
|   mapping (address => uint balances) // missing ; | ||||
| } | ||||
| 
 | ||||
| // There is no error in this contract | ||||
| contract SendCoin { | ||||
|   function sendCoin(address receiver, uint amount) public returns(bool sufficient) { | ||||
|     if (balances[msg.sender] < amount) return false; | ||||
|     balances[msg.sender] -= amount; | ||||
|     balances[receiver] += amount; | ||||
|     emit Transfer(msg.sender, receiver, amount); | ||||
|     return true; | ||||
|   } | ||||
| } | ||||
| // ---- | ||||
| // ParserError 6635: (235-236): Expected identifier but got '}' | ||||
| // ParserError 6635: (276-284): Expected ';' but got 'contract' | ||||
| // ParserError 9182: (276-284): Function, variable, struct or modifier declaration expected. | ||||
| // Warning 3796: (572-573): Recovered in ContractDefinition at '}'. | ||||
| // ParserError 7858: (574-575): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition. | ||||
| @ -1,11 +0,0 @@ | ||||
| pragma solidity >=0.0.0; | ||||
| 
 | ||||
| contract Error3 { | ||||
| 	constructor() { | ||||
| 	    balances[tx.origin] = ; // missing RHS. | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| // ---- | ||||
| // ParserError 6933: (88-89): Expected primary expression. | ||||
| // Warning 3347: (88-89): Recovered in Statement at ';'. | ||||
| @ -1,29 +0,0 @@ | ||||
| // An example with multiple errors. | ||||
| // Most are caught by inserting an expected token. | ||||
| // However some us S C Johnson recovery to | ||||
| // skip over tokens. | ||||
| 
 | ||||
| pragma solidity >=0.0.0; | ||||
| 
 | ||||
| contract Error4 { | ||||
|   constructor() { | ||||
|     balances[tx.origin] = 1 2; // missing operator | ||||
|   } | ||||
| 
 | ||||
|   function sendCoin(address receiver, uint amount) public returns(bool sufficient) { | ||||
|     if (balances[msg.sender] < amount) return false; | ||||
|     balances[msg.sender] -= amount   // Missing ";" | ||||
|     balances[receiver] += amount   // Another missing ";" | ||||
|     emit Transfer(msg.sender  // truncated line | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| // ---- | ||||
| // ParserError 6635: (242-243): Expected ';' but got 'Number' | ||||
| // ParserError 6635: (464-472): Expected ';' but got identifier | ||||
| // ParserError 6635: (522-526): Expected ';' but got 'emit' | ||||
| // ParserError 6635: (570-576): Expected ',' but got 'return' | ||||
| // ParserError 6933: (570-576): Expected primary expression. | ||||
| // Warning 3796: (581-582): Recovered in Statement at ';'. | ||||
| @ -1,10 +0,0 @@ | ||||
| pragma solidity >=0.0.0; | ||||
| 
 | ||||
| contract Error7 { | ||||
|     constructor() { | ||||
|         a = | ||||
| // ---- | ||||
| // ParserError 6933: (76-76): Expected primary expression. | ||||
| // ParserError 1957: (76-76): In Statement, ';'is expected; got end of source instead. | ||||
| // ParserError 1957: (76-76): In Block, '}'is expected; got end of source instead. | ||||
| // ParserError 1957: (76-76): In ContractDefinition, '}'is expected; got end of source instead. | ||||
| @ -1,3 +0,0 @@ | ||||
| pragma solidity ^99.99.0; | ||||
| // ---- | ||||
| // SyntaxError 3997: (0-25): Source file requires different compiler version (current compiler is .... | ||||
| @ -1,6 +0,0 @@ | ||||
| pragma solidity ^99.99.0; | ||||
| this is surely invalid | ||||
| // ---- | ||||
| // ParserError 6635: (31-33): Expected identifier but got 'is' | ||||
| // ParserError 6635: (34-40): Expected ';' but got identifier | ||||
| // ParserError 6635: (49-49): Expected ';' but got end of source | ||||
| @ -1,7 +0,0 @@ | ||||
| pragma solidity ^99.99.0; | ||||
| contract C { | ||||
|     uint ; | ||||
| } | ||||
| // ---- | ||||
| // ParserError 6635: (48-49): Expected identifier but got ';' | ||||
| // ParserError 6635: (50-51): Expected ';' but got '}' | ||||
| @ -1,7 +0,0 @@ | ||||
| pragma solidity ^99.99.0; | ||||
| contract C { | ||||
|     function f() {} | ||||
| } | ||||
| // ---- | ||||
| // SyntaxError 3997: (0-25): Source file requires different compiler version (current compiler is .... | ||||
| // SyntaxError 4937: (43-58): No visibility specified. Did you intend to add "public"? | ||||
| @ -33,10 +33,6 @@ public: | ||||
| 	{ | ||||
| 		return std::make_unique<SyntaxTest>(_config.filename, _config.evmVersion); | ||||
| 	} | ||||
| 	static std::unique_ptr<TestCase> createErrorRecovery(Config const& _config) | ||||
| 	{ | ||||
| 		return std::make_unique<SyntaxTest>(_config.filename, _config.evmVersion); | ||||
| 	} | ||||
| 	SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion); | ||||
| 	~SyntaxTest() override {} | ||||
| protected: | ||||
|  | ||||
| @ -116,7 +116,6 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) | ||||
| 			"--include-path=/home/user/include", | ||||
| 			"--allow-paths=/tmp,/home,project,../contracts", | ||||
| 			"--ignore-missing", | ||||
| 			"--error-recovery", | ||||
| 			"--output-dir=/tmp/out", | ||||
| 			"--overwrite", | ||||
| 			"--evm-version=spuriousDragon", | ||||
| @ -177,7 +176,6 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) | ||||
| 
 | ||||
| 		expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts", "c", "/usr/lib"}; | ||||
| 		expectedOptions.input.ignoreMissingFiles = true; | ||||
| 		expectedOptions.input.errorRecovery = (inputMode == InputMode::Compiler); | ||||
| 		expectedOptions.output.dir = "/tmp/out"; | ||||
| 		expectedOptions.output.overwriteFiles = true; | ||||
| 		expectedOptions.output.evmVersion = EVMVersion::spuriousDragon(); | ||||
| @ -405,7 +403,6 @@ BOOST_AUTO_TEST_CASE(invalid_options_input_modes_combinations) | ||||
| { | ||||
| 	map<string, vector<string>> invalidOptionInputModeCombinations = { | ||||
| 		// TODO: This should eventually contain all options.
 | ||||
| 		{"--error-recovery", {"--assemble", "--yul", "--strict-assembly", "--standard-json", "--link"}}, | ||||
| 		{"--experimental-via-ir", {"--assemble", "--yul", "--strict-assembly", "--standard-json", "--link"}}, | ||||
| 		{"--via-ir", {"--assemble", "--yul", "--strict-assembly", "--standard-json", "--link"}}, | ||||
| 		{"--metadata-literal", {"--assemble", "--yul", "--strict-assembly", "--standard-json", "--link"}}, | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user