mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #6857 from ethereum/revertRecoveringParser
Revert "Add Steve Johnson-style parser recovery rules:"
This commit is contained in:
		
						commit
						4eb1722176
					
				| @ -8,7 +8,6 @@ Language Features: | |||||||
| Compiler Features: | Compiler Features: | ||||||
|  * Assembler: Encode the compiler version in the deployed bytecode. |  * Assembler: Encode the compiler version in the deployed bytecode. | ||||||
|  * Code Generator: Fix handling of structs of dynamic size as constructor parameters. |  * Code Generator: Fix handling of structs of dynamic size as constructor parameters. | ||||||
|  * Commandline Interface: Experimental parser error recovery via the ``--error-recovery`` commandline switch. |  | ||||||
|  * Inline Assembly: Disallow the combination of ``msize()`` and the Yul optimizer. |  * Inline Assembly: Disallow the combination of ``msize()`` and the Yul optimizer. | ||||||
|  * Metadata: Add IPFS hashes of source files. |  * Metadata: Add IPFS hashes of source files. | ||||||
|  * Optimizer: Add rule to simplify SHL/SHR combinations. |  * Optimizer: Add rule to simplify SHL/SHR combinations. | ||||||
| @ -22,6 +21,7 @@ Compiler Features: | |||||||
|  * Yul Optimizer: Do not inline recursive functions. |  * Yul Optimizer: Do not inline recursive functions. | ||||||
|  * Yul Optimizer: Do not remove instructions that affect ``msize()`` if ``msize()`` is used. |  * Yul Optimizer: Do not remove instructions that affect ``msize()`` if ``msize()`` is used. | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| Bugfixes: | Bugfixes: | ||||||
|  * Code Generator: Explicitly turn uninitialized internal function pointers into invalid functions when loaded from storage. |  * Code Generator: Explicitly turn uninitialized internal function pointers into invalid functions when loaded from storage. | ||||||
|  * Code Generator: Fix assertion failure when assigning structs containing array of mapping. |  * Code Generator: Fix assertion failure when assigning structs containing array of mapping. | ||||||
|  | |||||||
| @ -73,13 +73,6 @@ char CharStream::rollback(size_t _amount) | |||||||
| 	return get(); | 	return get(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| char CharStream::setPosition(size_t _location) |  | ||||||
| { |  | ||||||
| 	solAssert(_location <= m_source.size(), "Attempting to set position past end of source."); |  | ||||||
| 	m_position = _location; |  | ||||||
| 	return get(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| string CharStream::lineAtPosition(int _position) const | string CharStream::lineAtPosition(int _position) const | ||||||
| { | { | ||||||
| 	// if _position points to \n, it returns the line before the \n
 | 	// if _position points to \n, it returns the line before the \n
 | ||||||
| @ -113,3 +106,5 @@ tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const | |||||||
| 	} | 	} | ||||||
| 	return tuple<int, int>(lineNumber, searchPosition - lineStart); | 	return tuple<int, int>(lineNumber, searchPosition - lineStart); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -76,13 +76,7 @@ public: | |||||||
| 
 | 
 | ||||||
| 	char get(size_t _charsForward = 0) const { return m_source[m_position + _charsForward]; } | 	char get(size_t _charsForward = 0) const { return m_source[m_position + _charsForward]; } | ||||||
| 	char advanceAndGet(size_t _chars = 1); | 	char advanceAndGet(size_t _chars = 1); | ||||||
| 	/// Sets scanner position to @ _amount characters backwards in source text.
 |  | ||||||
| 	/// @returns The character of the current location after update is returned.
 |  | ||||||
| 	char rollback(size_t _amount); | 	char rollback(size_t _amount); | ||||||
| 	/// Sets scanner position to @ _location if it refers a valid offset in m_source.
 |  | ||||||
| 	/// If not, nothing is done.
 |  | ||||||
| 	/// @returns The character of the current location after update is returned.
 |  | ||||||
| 	char setPosition(size_t _location); |  | ||||||
| 
 | 
 | ||||||
| 	void reset() { m_position = 0; } | 	void reset() { m_position = 0; } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -86,11 +86,6 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, Se | |||||||
| 	m_errorList.push_back(err); | 	m_errorList.push_back(err); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool ErrorReporter::hasExcessiveErrors() const |  | ||||||
| { |  | ||||||
| 	return m_errorCount > c_maxErrorsAllowed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool ErrorReporter::checkForExcessiveErrors(Error::Type _type) | bool ErrorReporter::checkForExcessiveErrors(Error::Type _type) | ||||||
| { | { | ||||||
| 	if (_type == Error::Type::Warning) | 	if (_type == Error::Type::Warning) | ||||||
|  | |||||||
| @ -118,9 +118,6 @@ public: | |||||||
| 		return m_errorCount > 0; | 		return m_errorCount > 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// @returns true if the maximum error count has been reached.
 |  | ||||||
| 	bool hasExcessiveErrors() const; |  | ||||||
| 
 |  | ||||||
| private: | private: | ||||||
| 	void error( | 	void error( | ||||||
| 		Error::Type _type, | 		Error::Type _type, | ||||||
| @ -152,3 +149,4 @@ private: | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -47,7 +47,7 @@ Token ParserBase::peekNextToken() const | |||||||
| 	return m_scanner->peekNextToken(); | 	return m_scanner->peekNextToken(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| string ParserBase::currentLiteral() const | std::string ParserBase::currentLiteral() const | ||||||
| { | { | ||||||
| 	return m_scanner->currentLiteral(); | 	return m_scanner->currentLiteral(); | ||||||
| } | } | ||||||
| @ -57,79 +57,30 @@ Token ParserBase::advance() | |||||||
| 	return m_scanner->next(); | 	return m_scanner->next(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| string ParserBase::tokenName(Token _token) |  | ||||||
| { |  | ||||||
| 	if (_token == Token::Identifier) |  | ||||||
| 		return "identifier"; |  | ||||||
| 	else if (_token == Token::EOS) |  | ||||||
| 		return "end of source"; |  | ||||||
| 	else if (TokenTraits::isReservedKeyword(_token)) |  | ||||||
| 		return "reserved keyword '" + TokenTraits::friendlyName(_token) + "'"; |  | ||||||
| 	else if (TokenTraits::isElementaryTypeName(_token)) //for the sake of accuracy in reporting
 |  | ||||||
| 	{ |  | ||||||
| 		ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken(); |  | ||||||
| 		return "'" + elemTypeName.toString() + "'"; |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 		return "'" + TokenTraits::friendlyName(_token) + "'"; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ParserBase::expectToken(Token _value, bool _advance) | void ParserBase::expectToken(Token _value, bool _advance) | ||||||
| { | { | ||||||
| 	Token tok = m_scanner->currentToken(); | 	Token tok = m_scanner->currentToken(); | ||||||
| 	if (tok != _value) | 	if (tok != _value) | ||||||
| 	{ | 	{ | ||||||
| 		string const expectedToken = ParserBase::tokenName(_value); | 		auto tokenName = [this](Token _token) | ||||||
| 		if (m_parserErrorRecovery) |  | ||||||
| 			parserError("Expected " + expectedToken + " but got " + tokenName(tok)); |  | ||||||
| 		else |  | ||||||
| 			fatalParserError("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) |  | ||||||
| 		m_scanner->next(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentNodeName, bool _advance) |  | ||||||
| { |  | ||||||
| 	Token tok = m_scanner->currentToken(); |  | ||||||
| 	if (tok != _value) |  | ||||||
| 	{ |  | ||||||
| 		int startPosition = position(); |  | ||||||
| 		SourceLocation errorLoc = SourceLocation{startPosition, endPosition(), source()}; |  | ||||||
| 		while (m_scanner->currentToken() != _value && m_scanner->currentToken() != Token::EOS) |  | ||||||
| 			m_scanner->next(); |  | ||||||
| 
 |  | ||||||
| 		string const expectedToken = ParserBase::tokenName(_value); |  | ||||||
| 		string const msg = "In " + _currentNodeName + ", " + expectedToken + "is expected; got " +  ParserBase::tokenName(tok) +  "instead."; |  | ||||||
| 		if (m_scanner->currentToken() == Token::EOS) |  | ||||||
| 		{ | 		{ | ||||||
| 			// rollback to where the token started, and raise exception to be caught at a higher level.
 | 			if (_token == Token::Identifier) | ||||||
| 			m_scanner->setPosition(startPosition); | 				return string("identifier"); | ||||||
| 			m_inParserRecovery = true; | 			else if (_token == Token::EOS) | ||||||
| 			fatalParserError(errorLoc, msg); | 				return string("end of source"); | ||||||
| 		} | 			else if (TokenTraits::isReservedKeyword(_token)) | ||||||
| 		else | 				return string("reserved keyword '") + TokenTraits::friendlyName(_token) + "'"; | ||||||
| 		{ | 			else if (TokenTraits::isElementaryTypeName(_token)) //for the sake of accuracy in reporting
 | ||||||
| 			if (m_inParserRecovery) | 			{ | ||||||
| 				parserWarning("Recovered in " + _currentNodeName + " at " + expectedToken + "."); | 				ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken(); | ||||||
|  | 				return string("'") + elemTypeName.toString() + "'"; | ||||||
|  | 			} | ||||||
| 			else | 			else | ||||||
| 				parserError(errorLoc, msg + "Recovered at next " + expectedToken); | 				return string("'") + TokenTraits::friendlyName(_token) + "'"; | ||||||
| 			m_inParserRecovery = false; | 		}; | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	else if (m_inParserRecovery) |  | ||||||
| 	{ |  | ||||||
| 		string expectedToken = ParserBase::tokenName(_value); |  | ||||||
| 		parserWarning("Recovered in " + _currentNodeName + " at " + expectedToken + "."); |  | ||||||
| 		m_inParserRecovery = false; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
|  | 		fatalParserError(string("Expected ") + tokenName(_value) + string(" but got ") + tokenName(tok)); | ||||||
|  | 	} | ||||||
| 	if (_advance) | 	if (_advance) | ||||||
| 		m_scanner->next(); | 		m_scanner->next(); | ||||||
| } | } | ||||||
| @ -147,27 +98,12 @@ void ParserBase::decreaseRecursionDepth() | |||||||
| 	m_recursionDepth--; | 	m_recursionDepth--; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ParserBase::parserWarning(string const& _description) |  | ||||||
| { |  | ||||||
| 	m_errorReporter.warning(SourceLocation{position(), endPosition(), source()}, _description); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ParserBase::parserError(SourceLocation const& _location, string const& _description) |  | ||||||
| { |  | ||||||
| 	m_errorReporter.parserError(_location, _description); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ParserBase::parserError(string const& _description) | void ParserBase::parserError(string const& _description) | ||||||
| { | { | ||||||
| 	parserError(SourceLocation{position(), endPosition(), source()}, _description); | 	m_errorReporter.parserError(SourceLocation{position(), endPosition(), source()}, _description); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ParserBase::fatalParserError(string const& _description) | void ParserBase::fatalParserError(string const& _description) | ||||||
| { | { | ||||||
| 	fatalParserError(SourceLocation{position(), endPosition(), source()}, _description); | 	m_errorReporter.fatalParserError(SourceLocation{position(), endPosition(), source()}, _description); | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ParserBase::fatalParserError(SourceLocation const& _location, string const& _description) |  | ||||||
| { |  | ||||||
| 	m_errorReporter.fatalParserError(_location, _description); |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -36,14 +36,7 @@ class Scanner; | |||||||
| class ParserBase | class ParserBase | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	/// Set @a _parserErrorRecovery to true for additional error
 | 	explicit ParserBase(ErrorReporter& errorReporter): m_errorReporter(errorReporter) {} | ||||||
| 	/// 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; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	std::shared_ptr<CharStream> source() const { return m_scanner->charStream(); } | 	std::shared_ptr<CharStream> source() const { return m_scanner->charStream(); } | ||||||
| 
 | 
 | ||||||
| @ -69,17 +62,10 @@ protected: | |||||||
| 
 | 
 | ||||||
| 	///@{
 | 	///@{
 | ||||||
| 	///@name Helper functions
 | 	///@name Helper functions
 | ||||||
| 	/// If current token value is not @a _value, throw exception otherwise advance token
 | 	/// If current token value is not _value, throw exception otherwise advance token.
 | ||||||
| 	//  @a if _advance is true and error recovery is in effect.
 |  | ||||||
| 	void expectToken(Token _value, bool _advance = 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 currentToken() const; | ||||||
| 	Token peekNextToken() const; | 	Token peekNextToken() const; | ||||||
| 	std::string tokenName(Token _token); |  | ||||||
| 	std::string currentLiteral() const; | 	std::string currentLiteral() const; | ||||||
| 	Token advance(); | 	Token advance(); | ||||||
| 	///@}
 | 	///@}
 | ||||||
| @ -91,26 +77,16 @@ protected: | |||||||
| 	/// Creates a @ref ParserError and annotates it with the current position and the
 | 	/// Creates a @ref ParserError and annotates it with the current position and the
 | ||||||
| 	/// given @a _description.
 | 	/// given @a _description.
 | ||||||
| 	void parserError(std::string const& _description); | 	void parserError(std::string const& _description); | ||||||
| 	void parserError(SourceLocation const& _location, std::string const& _description); |  | ||||||
| 
 |  | ||||||
| 	/// Creates a @ref ParserWarning and annotates it with the current position and the
 |  | ||||||
| 	/// given @a _description.
 |  | ||||||
| 	void parserWarning(std::string const& _description); |  | ||||||
| 
 | 
 | ||||||
| 	/// Creates a @ref ParserError and annotates it with the current position and the
 | 	/// Creates a @ref ParserError and annotates it with the current position and the
 | ||||||
| 	/// given @a _description. Throws the FatalError.
 | 	/// given @a _description. Throws the FatalError.
 | ||||||
| 	void fatalParserError(std::string const& _description); | 	void fatalParserError(std::string const& _description); | ||||||
| 	void fatalParserError(SourceLocation const& _location, std::string const& _description); |  | ||||||
| 
 | 
 | ||||||
| 	std::shared_ptr<Scanner> m_scanner; | 	std::shared_ptr<Scanner> m_scanner; | ||||||
| 	/// The reference to the list of errors and warning to add errors/warnings during parsing
 | 	/// The reference to the list of errors and warning to add errors/warnings during parsing
 | ||||||
| 	ErrorReporter& m_errorReporter; | 	ErrorReporter& m_errorReporter; | ||||||
| 	/// Current recursion depth during parsing.
 | 	/// Current recursion depth during parsing.
 | ||||||
| 	size_t m_recursionDepth = 0; | 	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; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -156,13 +156,6 @@ void Scanner::reset() | |||||||
| 	next(); | 	next(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Scanner::setPosition(size_t _offset) |  | ||||||
| { |  | ||||||
| 	m_char = m_source->setPosition(_offset); |  | ||||||
| 	scanToken(); |  | ||||||
| 	next(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Scanner::supportPeriodInIdentifier(bool _value) | void Scanner::supportPeriodInIdentifier(bool _value) | ||||||
| { | { | ||||||
| 	m_supportPeriodInIdentifier = _value; | 	m_supportPeriodInIdentifier = _value; | ||||||
|  | |||||||
| @ -110,9 +110,6 @@ public: | |||||||
| 	/// @returns the next token and advances input
 | 	/// @returns the next token and advances input
 | ||||||
| 	Token next(); | 	Token next(); | ||||||
| 
 | 
 | ||||||
| 	/// Set scanner to a specific offset. This is used in error recovery.
 |  | ||||||
| 	void setPosition(size_t _offset); |  | ||||||
| 
 |  | ||||||
| 	///@{
 | 	///@{
 | ||||||
| 	///@name Information about the current token
 | 	///@name Information about the current token
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -217,7 +217,7 @@ bool CompilerStack::parse() | |||||||
| 		string const& path = sourcesToParse[i]; | 		string const& path = sourcesToParse[i]; | ||||||
| 		Source& source = m_sources[path]; | 		Source& source = m_sources[path]; | ||||||
| 		source.scanner->reset(); | 		source.scanner->reset(); | ||||||
| 		source.ast = Parser(m_errorReporter, m_evmVersion, m_parserErrorRecovery).parse(source.scanner); | 		source.ast = Parser(m_errorReporter, m_evmVersion).parse(source.scanner); | ||||||
| 		if (!source.ast) | 		if (!source.ast) | ||||||
| 			solAssert(!Error::containsOnlyWarnings(m_errorReporter.errors()), "Parser returned null but did not report error."); | 			solAssert(!Error::containsOnlyWarnings(m_errorReporter.errors()), "Parser returned null but did not report error."); | ||||||
| 		else | 		else | ||||||
|  | |||||||
| @ -132,14 +132,6 @@ public: | |||||||
| 	/// Must be set before parsing.
 | 	/// Must be set before parsing.
 | ||||||
| 	void setOptimiserSettings(OptimiserSettings _settings); | 	void setOptimiserSettings(OptimiserSettings _settings); | ||||||
| 
 | 
 | ||||||
| 	/// 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; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/// Set the EVM version used before running compile.
 | 	/// Set the EVM version used before running compile.
 | ||||||
| 	/// When called without an argument it will revert to the default version.
 | 	/// When called without an argument it will revert to the default version.
 | ||||||
| 	/// Must be set before parsing.
 | 	/// Must be set before parsing.
 | ||||||
| @ -394,7 +386,6 @@ private: | |||||||
| 	langutil::ErrorList m_errorList; | 	langutil::ErrorList m_errorList; | ||||||
| 	langutil::ErrorReporter m_errorReporter; | 	langutil::ErrorReporter m_errorReporter; | ||||||
| 	bool m_metadataLiteralSources = false; | 	bool m_metadataLiteralSources = false; | ||||||
| 	bool m_parserErrorRecovery = false; |  | ||||||
| 	State m_stackState = Empty; | 	State m_stackState = Empty; | ||||||
| 	bool m_release = VersionIsRelease; | 	bool m_release = VersionIsRelease; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -258,75 +258,57 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition() | |||||||
| { | { | ||||||
| 	RecursionGuard recursionGuard(*this); | 	RecursionGuard recursionGuard(*this); | ||||||
| 	ASTNodeFactory nodeFactory(*this); | 	ASTNodeFactory nodeFactory(*this); | ||||||
| 	ASTPointer<ASTString> name =  nullptr; |  | ||||||
| 	ASTPointer<ASTString> docString; | 	ASTPointer<ASTString> docString; | ||||||
|  | 	if (m_scanner->currentCommentLiteral() != "") | ||||||
|  | 		docString = make_shared<ASTString>(m_scanner->currentCommentLiteral()); | ||||||
|  | 	ContractDefinition::ContractKind contractKind = parseContractKind(); | ||||||
|  | 	ASTPointer<ASTString> name = expectIdentifierToken(); | ||||||
| 	vector<ASTPointer<InheritanceSpecifier>> baseContracts; | 	vector<ASTPointer<InheritanceSpecifier>> baseContracts; | ||||||
| 	vector<ASTPointer<ASTNode>> subNodes; | 	if (m_scanner->currentToken() == Token::Is) | ||||||
| 	ContractDefinition::ContractKind contractKind = ContractDefinition::ContractKind::Contract; | 		do | ||||||
| 	try |  | ||||||
| 	{ |  | ||||||
| 		if (m_scanner->currentCommentLiteral() != "") |  | ||||||
| 			docString = make_shared<ASTString>(m_scanner->currentCommentLiteral()); |  | ||||||
| 		contractKind = parseContractKind(); |  | ||||||
| 		name = expectIdentifierToken(); |  | ||||||
| 		if (m_scanner->currentToken() == Token::Is) |  | ||||||
| 			do |  | ||||||
| 			{ |  | ||||||
| 				m_scanner->next(); |  | ||||||
| 				baseContracts.push_back(parseInheritanceSpecifier()); |  | ||||||
| 			} |  | ||||||
| 			while (m_scanner->currentToken() == Token::Comma); |  | ||||||
| 		expectToken(Token::LBrace); |  | ||||||
| 		while (true) |  | ||||||
| 		{ | 		{ | ||||||
| 			Token currentTokenValue = m_scanner->currentToken(); | 			m_scanner->next(); | ||||||
| 			if (currentTokenValue == Token::RBrace) | 			baseContracts.push_back(parseInheritanceSpecifier()); | ||||||
| 				break; |  | ||||||
| 			else if (currentTokenValue == Token::Function || currentTokenValue == Token::Constructor) |  | ||||||
| 				// This can be a function or a state variable of function type (especially
 |  | ||||||
| 				// complicated to distinguish fallback function from function type state variable)
 |  | ||||||
| 				subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable()); |  | ||||||
| 			else if (currentTokenValue == Token::Struct) |  | ||||||
| 				subNodes.push_back(parseStructDefinition()); |  | ||||||
| 			else if (currentTokenValue == Token::Enum) |  | ||||||
| 				subNodes.push_back(parseEnumDefinition()); |  | ||||||
| 			else if ( |  | ||||||
| 					currentTokenValue == Token::Identifier || |  | ||||||
| 					currentTokenValue == Token::Mapping || |  | ||||||
| 					TokenTraits::isElementaryTypeName(currentTokenValue) |  | ||||||
| 					) |  | ||||||
| 			{ |  | ||||||
| 				VarDeclParserOptions options; |  | ||||||
| 				options.isStateVariable = true; |  | ||||||
| 				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(string("Function, variable, struct or modifier declaration expected.")); |  | ||||||
| 		} | 		} | ||||||
| 	} | 		while (m_scanner->currentToken() == Token::Comma); | ||||||
| 	catch (FatalError const&) | 	vector<ASTPointer<ASTNode>> subNodes; | ||||||
|  | 	expectToken(Token::LBrace); | ||||||
|  | 	while (true) | ||||||
| 	{ | 	{ | ||||||
| 		if ( | 		Token currentTokenValue = m_scanner->currentToken(); | ||||||
| 			!m_errorReporter.hasErrors() || | 		if (currentTokenValue == Token::RBrace) | ||||||
| 			!m_parserErrorRecovery || | 			break; | ||||||
| 			m_errorReporter.hasExcessiveErrors() | 		else if (currentTokenValue == Token::Function || currentTokenValue == Token::Constructor) | ||||||
|  | 			// This can be a function or a state variable of function type (especially
 | ||||||
|  | 			// complicated to distinguish fallback function from function type state variable)
 | ||||||
|  | 			subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable()); | ||||||
|  | 		else if (currentTokenValue == Token::Struct) | ||||||
|  | 			subNodes.push_back(parseStructDefinition()); | ||||||
|  | 		else if (currentTokenValue == Token::Enum) | ||||||
|  | 			subNodes.push_back(parseEnumDefinition()); | ||||||
|  | 		else if ( | ||||||
|  | 			currentTokenValue == Token::Identifier || | ||||||
|  | 			currentTokenValue == Token::Mapping || | ||||||
|  | 			TokenTraits::isElementaryTypeName(currentTokenValue) | ||||||
| 		) | 		) | ||||||
| 			BOOST_THROW_EXCEPTION(FatalError()); /* Don't try to recover here. */ | 		{ | ||||||
| 		m_inParserRecovery = true; | 			VarDeclParserOptions options; | ||||||
|  | 			options.isStateVariable = true; | ||||||
|  | 			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(string("Function, variable, struct or modifier declaration expected.")); | ||||||
| 	} | 	} | ||||||
| 	nodeFactory.markEndPosition(); | 	nodeFactory.markEndPosition(); | ||||||
| 	if (m_inParserRecovery) | 	expectToken(Token::RBrace); | ||||||
| 		expectTokenOrConsumeUntil(Token::RBrace, "ContractDefinition"); |  | ||||||
| 	else |  | ||||||
| 		expectToken(Token::RBrace); |  | ||||||
| 	return nodeFactory.createNode<ContractDefinition>( | 	return nodeFactory.createNode<ContractDefinition>( | ||||||
| 		name, | 		name, | ||||||
| 		docString, | 		docString, | ||||||
| @ -977,26 +959,10 @@ ASTPointer<Block> Parser::parseBlock(ASTPointer<ASTString> const& _docString) | |||||||
| 	ASTNodeFactory nodeFactory(*this); | 	ASTNodeFactory nodeFactory(*this); | ||||||
| 	expectToken(Token::LBrace); | 	expectToken(Token::LBrace); | ||||||
| 	vector<ASTPointer<Statement>> statements; | 	vector<ASTPointer<Statement>> statements; | ||||||
| 	try | 	while (m_scanner->currentToken() != Token::RBrace) | ||||||
| 	{ | 		statements.push_back(parseStatement()); | ||||||
| 		while (m_scanner->currentToken() != Token::RBrace) | 	nodeFactory.markEndPosition(); | ||||||
| 			statements.push_back(parseStatement()); | 	expectToken(Token::RBrace); | ||||||
| 		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_parserErrorRecovery) |  | ||||||
| 		expectTokenOrConsumeUntil(Token::RBrace, "Block"); |  | ||||||
| 	else |  | ||||||
| 		expectToken(Token::RBrace); |  | ||||||
| 	return nodeFactory.createNode<Block>(_docString, statements); | 	return nodeFactory.createNode<Block>(_docString, statements); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -1004,83 +970,67 @@ ASTPointer<Statement> Parser::parseStatement() | |||||||
| { | { | ||||||
| 	RecursionGuard recursionGuard(*this); | 	RecursionGuard recursionGuard(*this); | ||||||
| 	ASTPointer<ASTString> docString; | 	ASTPointer<ASTString> docString; | ||||||
|  | 	if (m_scanner->currentCommentLiteral() != "") | ||||||
|  | 		docString = make_shared<ASTString>(m_scanner->currentCommentLiteral()); | ||||||
| 	ASTPointer<Statement> statement; | 	ASTPointer<Statement> statement; | ||||||
| 	try | 	switch (m_scanner->currentToken()) | ||||||
| 	{ | 	{ | ||||||
| 		if (m_scanner->currentCommentLiteral() != "") | 	case Token::If: | ||||||
| 			docString = make_shared<ASTString>(m_scanner->currentCommentLiteral()); | 		return parseIfStatement(docString); | ||||||
| 		switch (m_scanner->currentToken()) | 	case Token::While: | ||||||
|  | 		return parseWhileStatement(docString); | ||||||
|  | 	case Token::Do: | ||||||
|  | 		return parseDoWhileStatement(docString); | ||||||
|  | 	case Token::For: | ||||||
|  | 		return parseForStatement(docString); | ||||||
|  | 	case Token::LBrace: | ||||||
|  | 		return parseBlock(docString); | ||||||
|  | 		// starting from here, all statements must be terminated by a semicolon
 | ||||||
|  | 	case Token::Continue: | ||||||
|  | 		statement = ASTNodeFactory(*this).createNode<Continue>(docString); | ||||||
|  | 		m_scanner->next(); | ||||||
|  | 		break; | ||||||
|  | 	case Token::Break: | ||||||
|  | 		statement = ASTNodeFactory(*this).createNode<Break>(docString); | ||||||
|  | 		m_scanner->next(); | ||||||
|  | 		break; | ||||||
|  | 	case Token::Return: | ||||||
|  | 	{ | ||||||
|  | 		ASTNodeFactory nodeFactory(*this); | ||||||
|  | 		ASTPointer<Expression> expression; | ||||||
|  | 		if (m_scanner->next() != Token::Semicolon) | ||||||
| 		{ | 		{ | ||||||
| 		case Token::If: | 			expression = parseExpression(); | ||||||
| 			return parseIfStatement(docString); | 			nodeFactory.setEndPositionFromNode(expression); | ||||||
| 		case Token::While: |  | ||||||
| 			return parseWhileStatement(docString); |  | ||||||
| 		case Token::Do: |  | ||||||
| 			return parseDoWhileStatement(docString); |  | ||||||
| 		case Token::For: |  | ||||||
| 			return parseForStatement(docString); |  | ||||||
| 		case Token::LBrace: |  | ||||||
| 			return parseBlock(docString); |  | ||||||
| 			// starting from here, all statements must be terminated by a semicolon
 |  | ||||||
| 		case Token::Continue: |  | ||||||
| 			statement = ASTNodeFactory(*this).createNode<Continue>(docString); |  | ||||||
| 			m_scanner->next(); |  | ||||||
| 			break; |  | ||||||
| 		case Token::Break: |  | ||||||
| 			statement = ASTNodeFactory(*this).createNode<Break>(docString); |  | ||||||
| 			m_scanner->next(); |  | ||||||
| 			break; |  | ||||||
| 		case Token::Return: |  | ||||||
| 		{ |  | ||||||
| 			ASTNodeFactory nodeFactory(*this); |  | ||||||
| 			ASTPointer<Expression> expression; |  | ||||||
| 			if (m_scanner->next() != Token::Semicolon) |  | ||||||
| 				{ |  | ||||||
| 					expression = parseExpression(); |  | ||||||
| 					nodeFactory.setEndPositionFromNode(expression); |  | ||||||
| 				} |  | ||||||
| 			statement = nodeFactory.createNode<Return>(docString, expression); |  | ||||||
| 				break; |  | ||||||
| 			} |  | ||||||
| 		case Token::Throw: |  | ||||||
| 			{ |  | ||||||
| 				statement = ASTNodeFactory(*this).createNode<Throw>(docString); |  | ||||||
| 				m_scanner->next(); |  | ||||||
| 				break; |  | ||||||
| 			} |  | ||||||
| 		case Token::Assembly: |  | ||||||
| 			return parseInlineAssembly(docString); |  | ||||||
| 		case Token::Emit: |  | ||||||
| 			statement = parseEmitStatement(docString); |  | ||||||
| 			break; |  | ||||||
| 		case Token::Identifier: |  | ||||||
| 			if (m_insideModifier && m_scanner->currentLiteral() == "_") |  | ||||||
| 				{ |  | ||||||
| 					statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString); |  | ||||||
| 					m_scanner->next(); |  | ||||||
| 				} |  | ||||||
| 			else |  | ||||||
| 				statement = parseSimpleStatement(docString); |  | ||||||
| 			break; |  | ||||||
| 		default: |  | ||||||
| 			statement = parseSimpleStatement(docString); |  | ||||||
| 			break; |  | ||||||
| 		} | 		} | ||||||
|  | 		statement = nodeFactory.createNode<Return>(docString, expression); | ||||||
|  | 		break; | ||||||
| 	} | 	} | ||||||
| 	catch (FatalError const&) | 	case Token::Throw: | ||||||
| 	{ | 	{ | ||||||
| 		if ( | 		statement = ASTNodeFactory(*this).createNode<Throw>(docString); | ||||||
| 			!m_errorReporter.hasErrors() || | 		m_scanner->next(); | ||||||
| 			!m_parserErrorRecovery || | 		break; | ||||||
| 			m_errorReporter.hasExcessiveErrors() |  | ||||||
| 		) |  | ||||||
| 			BOOST_THROW_EXCEPTION(FatalError()); /* Don't try to recover here. */ |  | ||||||
| 		m_inParserRecovery = true; |  | ||||||
| 	} | 	} | ||||||
| 	if (m_inParserRecovery) | 	case Token::Assembly: | ||||||
| 		expectTokenOrConsumeUntil(Token::Semicolon, "Statement"); | 		return parseInlineAssembly(docString); | ||||||
| 	else | 	case Token::Emit: | ||||||
| 		expectToken(Token::Semicolon); | 		statement = parseEmitStatement(docString); | ||||||
|  | 		break; | ||||||
|  | 	case Token::Identifier: | ||||||
|  | 		if (m_insideModifier && m_scanner->currentLiteral() == "_") | ||||||
|  | 		{ | ||||||
|  | 			statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString); | ||||||
|  | 			m_scanner->next(); | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 			statement = parseSimpleStatement(docString); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		statement = parseSimpleStatement(docString); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	expectToken(Token::Semicolon); | ||||||
| 	return statement; | 	return statement; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -41,10 +41,9 @@ class Parser: public langutil::ParserBase | |||||||
| public: | public: | ||||||
| 	explicit Parser( | 	explicit Parser( | ||||||
| 		langutil::ErrorReporter& _errorReporter, | 		langutil::ErrorReporter& _errorReporter, | ||||||
| 		langutil::EVMVersion _evmVersion, | 		langutil::EVMVersion _evmVersion | ||||||
| 		bool _errorRecovery = false |  | ||||||
| 	): | 	): | ||||||
| 		ParserBase(_errorReporter, _errorRecovery), | 		ParserBase(_errorReporter), | ||||||
| 		m_evmVersion(_evmVersion) | 		m_evmVersion(_evmVersion) | ||||||
| 	{} | 	{} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -112,7 +112,6 @@ static string const g_strBinaryRuntime = "bin-runtime"; | |||||||
| static string const g_strCombinedJson = "combined-json"; | static string const g_strCombinedJson = "combined-json"; | ||||||
| static string const g_strCompactJSON = "compact-format"; | static string const g_strCompactJSON = "compact-format"; | ||||||
| static string const g_strContracts = "contracts"; | static string const g_strContracts = "contracts"; | ||||||
| static string const g_strErrorRecovery = "error-recovery"; |  | ||||||
| static string const g_strEVM = "evm"; | static string const g_strEVM = "evm"; | ||||||
| static string const g_strEVM15 = "evm15"; | static string const g_strEVM15 = "evm15"; | ||||||
| static string const g_strEVMVersion = "evm-version"; | static string const g_strEVMVersion = "evm-version"; | ||||||
| @ -164,7 +163,6 @@ static string const g_argBinary = g_strBinary; | |||||||
| static string const g_argBinaryRuntime = g_strBinaryRuntime; | static string const g_argBinaryRuntime = g_strBinaryRuntime; | ||||||
| static string const g_argCombinedJson = g_strCombinedJson; | static string const g_argCombinedJson = g_strCombinedJson; | ||||||
| static string const g_argCompactJSON = g_strCompactJSON; | static string const g_argCompactJSON = g_strCompactJSON; | ||||||
| static string const g_argErrorRecovery = g_strErrorRecovery; |  | ||||||
| static string const g_argGas = g_strGas; | static string const g_argGas = g_strGas; | ||||||
| static string const g_argHelp = g_strHelp; | static string const g_argHelp = g_strHelp; | ||||||
| static string const g_argInputFile = g_strInputFile; | static string const g_argInputFile = g_strInputFile; | ||||||
| @ -691,7 +689,6 @@ Allowed options)", | |||||||
| 		(g_argColor.c_str(), "Force colored output.") | 		(g_argColor.c_str(), "Force colored output.") | ||||||
| 		(g_argNoColor.c_str(), "Explicitly disable colored output, disabling terminal auto-detection.") | 		(g_argNoColor.c_str(), "Explicitly disable colored output, disabling terminal auto-detection.") | ||||||
| 		(g_argNewReporter.c_str(), "Enables new diagnostics reporter.") | 		(g_argNewReporter.c_str(), "Enables new diagnostics reporter.") | ||||||
| 		(g_argErrorRecovery.c_str(), "Enables additional parser error recovery.") |  | ||||||
| 		(g_argIgnoreMissingFiles.c_str(), "Ignore missing files."); | 		(g_argIgnoreMissingFiles.c_str(), "Ignore missing files."); | ||||||
| 	po::options_description outputComponents("Output Components"); | 	po::options_description outputComponents("Output Components"); | ||||||
| 	outputComponents.add_options() | 	outputComponents.add_options() | ||||||
| @ -926,8 +923,6 @@ bool CommandLineInterface::processInput() | |||||||
| 		m_compiler->setSources(m_sourceCodes); | 		m_compiler->setSources(m_sourceCodes); | ||||||
| 		if (m_args.count(g_argLibraries)) | 		if (m_args.count(g_argLibraries)) | ||||||
| 			m_compiler->setLibraries(m_libraries); | 			m_compiler->setLibraries(m_libraries); | ||||||
| 		if (m_args.count(g_argErrorRecovery)) |  | ||||||
| 			m_compiler->setParserErrorRecovery(true); |  | ||||||
| 		m_compiler->setEVMVersion(m_evmVersion); | 		m_compiler->setEVMVersion(m_evmVersion); | ||||||
| 		// TODO: Perhaps we should not compile unless requested
 | 		// TODO: Perhaps we should not compile unless requested
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -56,7 +56,6 @@ Testsuite const g_interactiveTestsuites[] = { | |||||||
| 	{"Yul Interpreter",     "libyul",      "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, | 	{"Yul Interpreter",     "libyul",      "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, | ||||||
| 	{"Yul Object Compiler", "libyul",      "objectCompiler",      false, false, &yul::test::ObjectCompilerTest::create}, | 	{"Yul Object Compiler", "libyul",      "objectCompiler",      false, false, &yul::test::ObjectCompilerTest::create}, | ||||||
| 	{"Syntax",              "libsolidity", "syntaxTests",         false, false, &SyntaxTest::create}, | 	{"Syntax",              "libsolidity", "syntaxTests",         false, false, &SyntaxTest::create}, | ||||||
| 	{"ErrorRecovery",       "libsolidity", "errorRecoveryTests",  false, false, &SyntaxTest::createErrorRecovery}, |  | ||||||
| 	{"Semantic",            "libsolidity", "semanticTests",       false, true,  &SemanticTest::create}, | 	{"Semantic",            "libsolidity", "semanticTests",       false, true,  &SemanticTest::create}, | ||||||
| 	{"JSON AST",            "libsolidity", "ASTJSON",             false, false, &ASTJSONTest::create}, | 	{"JSON AST",            "libsolidity", "ASTJSON",             false, false, &ASTJSONTest::create}, | ||||||
| 	{"SMT Checker",         "libsolidity", "smtCheckerTests",     true,  false, &SyntaxTest::create}, | 	{"SMT Checker",         "libsolidity", "smtCheckerTests",     true,  false, &SyntaxTest::create}, | ||||||
| @ -67,3 +66,4 @@ Testsuite const g_interactiveTestsuites[] = { | |||||||
| } | } | ||||||
| } | } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -1,53 +0,0 @@ | |||||||
| /*
 |  | ||||||
| 	This file is part of solidity. |  | ||||||
| 
 |  | ||||||
| 	solidity is free software: you can redistribute it and/or modify |  | ||||||
| 	it under the terms of the GNU General Public License as published by |  | ||||||
| 	the Free Software Foundation, either version 3 of the License, or |  | ||||||
| 	(at your option) any later version. |  | ||||||
| 
 |  | ||||||
| 	solidity is distributed in the hope that it will be useful, |  | ||||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
| 	GNU General Public License for more details. |  | ||||||
| 
 |  | ||||||
| 	You should have received a copy of the GNU General Public License |  | ||||||
| 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 |  | ||||||
| */ |  | ||||||
| /**
 |  | ||||||
|  * @author Rocky Bernstein <rocky.bernstein@consensys.net> |  | ||||||
|  * @date 2019 |  | ||||||
|  * Unit tests for the CharStream class. |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| #include <liblangutil/CharStream.h> |  | ||||||
| #include <liblangutil/Exceptions.h> |  | ||||||
| 
 |  | ||||||
| #include <test/Options.h> |  | ||||||
| 
 |  | ||||||
| namespace langutil |  | ||||||
| { |  | ||||||
| namespace test |  | ||||||
| { |  | ||||||
| 
 |  | ||||||
| BOOST_AUTO_TEST_SUITE(CharStreamtest) |  | ||||||
| 
 |  | ||||||
| BOOST_AUTO_TEST_CASE(test_fail) |  | ||||||
| { |  | ||||||
| 	auto const source = std::make_shared<CharStream>("now is the time for testing", "source"); |  | ||||||
| 
 |  | ||||||
| 	BOOST_CHECK('n' == source->get()); |  | ||||||
| 	BOOST_CHECK('n' == source->get()); |  | ||||||
| 	BOOST_CHECK('o' == source->advanceAndGet()); |  | ||||||
| 	BOOST_CHECK('n' == source->rollback(1)); |  | ||||||
| 	BOOST_CHECK('w' == source->setPosition(2)); |  | ||||||
| 	BOOST_REQUIRE_THROW( |  | ||||||
| 		source->setPosition(200), |  | ||||||
| 		::langutil::InternalCompilerError |  | ||||||
| 	); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| BOOST_AUTO_TEST_SUITE_END() |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| } // end namespaces
 |  | ||||||
| @ -44,15 +44,12 @@ AnalysisFramework::parseAnalyseAndReturnError( | |||||||
| 	string const& _source, | 	string const& _source, | ||||||
| 	bool _reportWarnings, | 	bool _reportWarnings, | ||||||
| 	bool _insertVersionPragma, | 	bool _insertVersionPragma, | ||||||
| 	bool _allowMultipleErrors, | 	bool _allowMultipleErrors | ||||||
| 	bool _allowRecoveryErrors |  | ||||||
| ) | ) | ||||||
| { | { | ||||||
| 	compiler().reset(); | 	compiler().reset(); | ||||||
| 	compiler().setSources({{"", _insertVersionPragma ? "pragma solidity >=0.0;\n" + _source : _source}}); | 	compiler().setSources({{"", _insertVersionPragma ? "pragma solidity >=0.0;\n" + _source : _source}}); | ||||||
| 	compiler().setEVMVersion(dev::test::Options::get().evmVersion()); | 	compiler().setEVMVersion(dev::test::Options::get().evmVersion()); | ||||||
| 	compiler().setParserErrorRecovery(_allowRecoveryErrors); |  | ||||||
| 	_allowMultipleErrors = _allowMultipleErrors || _allowRecoveryErrors; |  | ||||||
| 	if (!compiler().parse()) | 	if (!compiler().parse()) | ||||||
| 	{ | 	{ | ||||||
| 		BOOST_FAIL("Parsing contract failed in analysis test suite:" + formatErrors()); | 		BOOST_FAIL("Parsing contract failed in analysis test suite:" + formatErrors()); | ||||||
|  | |||||||
| @ -50,8 +50,7 @@ protected: | |||||||
| 		std::string const& _source, | 		std::string const& _source, | ||||||
| 		bool _reportWarnings = false, | 		bool _reportWarnings = false, | ||||||
| 		bool _insertVersionPragma = true, | 		bool _insertVersionPragma = true, | ||||||
| 		bool _allowMultipleErrors = false, | 		bool _allowMultipleErrors = false | ||||||
| 		bool _allowRecoveryErrors = false |  | ||||||
| 	); | 	); | ||||||
| 	virtual ~AnalysisFramework() = default; | 	virtual ~AnalysisFramework() = default; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -42,16 +42,14 @@ protected: | |||||||
| 		std::string const& _source, | 		std::string const& _source, | ||||||
| 		bool _reportWarnings = false, | 		bool _reportWarnings = false, | ||||||
| 		bool _insertVersionPragma = true, | 		bool _insertVersionPragma = true, | ||||||
| 		bool _allowMultipleErrors = false, | 		bool _allowMultipleErrors = false | ||||||
| 		bool _allowRecoveryErrors = false |  | ||||||
| 	) | 	) | ||||||
| 	{ | 	{ | ||||||
| 		return AnalysisFramework::parseAnalyseAndReturnError( | 		return AnalysisFramework::parseAnalyseAndReturnError( | ||||||
| 			"pragma experimental SMTChecker;\n" + _source, | 			"pragma experimental SMTChecker;\n" + _source, | ||||||
| 			_reportWarnings, | 			_reportWarnings, | ||||||
| 			_insertVersionPragma, | 			_insertVersionPragma, | ||||||
| 			_allowMultipleErrors, | 			_allowMultipleErrors | ||||||
| 			_allowRecoveryErrors |  | ||||||
| 		); | 		); | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -52,7 +52,7 @@ int parseUnsignedInteger(string::iterator& _it, string::iterator _end) | |||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion, bool _errorRecovery): m_evmVersion(_evmVersion) | SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion) | ||||||
| { | { | ||||||
| 	ifstream file(_filename); | 	ifstream file(_filename); | ||||||
| 	if (!file) | 	if (!file) | ||||||
| @ -67,7 +67,6 @@ SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion | |||||||
| 		m_settings.erase("optimize-yul"); | 		m_settings.erase("optimize-yul"); | ||||||
| 	} | 	} | ||||||
| 	m_expectations = parseExpectations(file); | 	m_expectations = parseExpectations(file); | ||||||
| 	m_errorRecovery = _errorRecovery; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| TestCase::TestResult SyntaxTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) | TestCase::TestResult SyntaxTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) | ||||||
| @ -76,7 +75,6 @@ TestCase::TestResult SyntaxTest::run(ostream& _stream, string const& _linePrefix | |||||||
| 	compiler().reset(); | 	compiler().reset(); | ||||||
| 	compiler().setSources({{"", versionPragma + m_source}}); | 	compiler().setSources({{"", versionPragma + m_source}}); | ||||||
| 	compiler().setEVMVersion(m_evmVersion); | 	compiler().setEVMVersion(m_evmVersion); | ||||||
| 	compiler().setParserErrorRecovery(m_errorRecovery); |  | ||||||
| 	compiler().setOptimiserSettings( | 	compiler().setOptimiserSettings( | ||||||
| 		m_optimiseYul ? | 		m_optimiseYul ? | ||||||
| 		OptimiserSettings::full() : | 		OptimiserSettings::full() : | ||||||
|  | |||||||
| @ -54,14 +54,8 @@ class SyntaxTest: AnalysisFramework, public EVMVersionRestrictedTestCase | |||||||
| { | { | ||||||
| public: | public: | ||||||
| 	static std::unique_ptr<TestCase> create(Config const& _config) | 	static std::unique_ptr<TestCase> create(Config const& _config) | ||||||
| 	{ | 	{ return std::make_unique<SyntaxTest>(_config.filename, _config.evmVersion); } | ||||||
| 		return std::make_unique<SyntaxTest>(_config.filename, _config.evmVersion, false); | 	SyntaxTest(std::string const& _filename, langutil::EVMVersion _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 _errorRecovery = false); |  | ||||||
| 
 | 
 | ||||||
| 	TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; | 	TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; | ||||||
| 
 | 
 | ||||||
| @ -90,7 +84,6 @@ protected: | |||||||
| 	std::vector<SyntaxTestError> m_errorList; | 	std::vector<SyntaxTestError> m_errorList; | ||||||
| 	bool m_optimiseYul = false; | 	bool m_optimiseYul = false; | ||||||
| 	langutil::EVMVersion const m_evmVersion; | 	langutil::EVMVersion const m_evmVersion; | ||||||
| 	bool m_errorRecovery = false; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,20 +0,0 @@ | |||||||
| pragma solidity >=0.0.0; |  | ||||||
| 
 |  | ||||||
| contract Error1 { |  | ||||||
|   constructor() public { |  | ||||||
|     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: (95-96): Expected primary expression. |  | ||||||
| // Warning: (95-96): Recovered in Statement at ';'. |  | ||||||
| @ -1,7 +0,0 @@ | |||||||
| contract Errort6 { |  | ||||||
|   using foo for  ; // missing type name |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ---- |  | ||||||
| // ParserError: (36-37): Expected type name |  | ||||||
| // Warning: (59-60): Recovered in ContractDefinition at '}'. |  | ||||||
| @ -1,13 +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: (417-425): Expected ')' but got identifier |  | ||||||
| @ -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: (212-220): Expected ')' but got identifier |  | ||||||
| // ParserError: (220-221): Expected ';' but got ')' |  | ||||||
| // ParserError: (220-221): Function, variable, struct or modifier declaration expected. |  | ||||||
| // Warning: (235-236): Recovered in ContractDefinition at '}'. |  | ||||||
| @ -1,11 +0,0 @@ | |||||||
| pragma solidity >=0.0.0; |  | ||||||
| 
 |  | ||||||
| contract Error3 { |  | ||||||
| 	constructor() public { |  | ||||||
| 	    balances[tx.origin] = ; // missing RHS. |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| // ---- |  | ||||||
| // ParserError: (95-96): Expected primary expression. |  | ||||||
| // Warning: (95-96): 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() public { |  | ||||||
|     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: (249-250): Expected ';' but got 'Number' |  | ||||||
| // ParserError: (471-479): Expected ';' but got identifier |  | ||||||
| // ParserError: (529-533): Expected ';' but got 'emit' |  | ||||||
| // ParserError: (577-583): Expected ',' but got 'return' |  | ||||||
| // ParserError: (577-583): Expected primary expression. |  | ||||||
| // Warning: (588-589): Recovered in Statement at ';'. |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user