mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #4054 from ethereum/token-names
Improve parser error messages
This commit is contained in:
		
						commit
						54aa10ce36
					
				| @ -5,6 +5,7 @@ Features: | ||||
|  * Code Generator: Use native shift instructions on target Constantinople. | ||||
|  * Gas Estimator: Only explore paths with higher gas costs. This reduces accuracy but greatly improves the speed of gas estimation. | ||||
|  * Optimizer: Remove unnecessary masking of the result of known short instructions (``ADDRESS``, ``CALLER``, ``ORIGIN`` and ``COINBASE``). | ||||
|  * Parser: Display nicer error messages by showing the actual tokens and not internal names. | ||||
|  * Type Checker: Deprecate the ``years`` unit denomination and raise a warning for it (or an error as experimental 0.5.0 feature). | ||||
|  * Type Checker: Make literals (without explicit type casting) an error for tight packing as experimental 0.5.0 feature. | ||||
|  * Type Checker: Warn about wildcard tuple assignments (this will turn into an error with version 0.5.0). | ||||
|  | ||||
| @ -276,7 +276,7 @@ assembly::Expression Parser::parseExpression() | ||||
| 		int args = instructionInfo(instr.instruction).args; | ||||
| 		if (args > 0 && currentToken() != Token::LParen) | ||||
| 			fatalParserError(string( | ||||
| 				"Expected token \"(\" (\"" + | ||||
| 				"Expected '(' (instruction \"" + | ||||
| 				instructionNames().at(instr.instruction) + | ||||
| 				"\" expects " + | ||||
| 				boost::lexical_cast<string>(args) + | ||||
| @ -504,7 +504,7 @@ assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) | ||||
| 			/// check for premature closing parentheses
 | ||||
| 			if (currentToken() == Token::RParen) | ||||
| 				fatalParserError(string( | ||||
| 					"Expected expression (\"" + | ||||
| 					"Expected expression (instruction \"" + | ||||
| 					instructionNames().at(instr) + | ||||
| 					"\" expects " + | ||||
| 					boost::lexical_cast<string>(args) + | ||||
| @ -516,7 +516,7 @@ assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) | ||||
| 			{ | ||||
| 				if (currentToken() != Token::Comma) | ||||
| 					fatalParserError(string( | ||||
| 						"Expected comma (\"" + | ||||
| 						"Expected ',' (instruction \"" + | ||||
| 						instructionNames().at(instr) + | ||||
| 						"\" expects " + | ||||
| 						boost::lexical_cast<string>(args) + | ||||
| @ -529,7 +529,7 @@ assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) | ||||
| 		ret.location.end = endPosition(); | ||||
| 		if (currentToken() == Token::Comma) | ||||
| 			fatalParserError(string( | ||||
| 				"Expected ')' (\"" + | ||||
| 				"Expected ')' (instruction \"" + | ||||
| 				instructionNames().at(instr) + | ||||
| 				"\" expects " + | ||||
| 				boost::lexical_cast<string>(args) + | ||||
|  | ||||
| @ -528,7 +528,7 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition() | ||||
| 			break; | ||||
| 		expectToken(Token::Comma); | ||||
| 		if (m_scanner->currentToken() != Token::Identifier) | ||||
| 			fatalParserError(string("Expected Identifier after ','")); | ||||
| 			fatalParserError(string("Expected identifier after ','")); | ||||
| 	} | ||||
| 	if (members.size() == 0) | ||||
| 		parserError({"enum with no members is not allowed."}); | ||||
|  | ||||
| @ -68,35 +68,24 @@ void ParserBase::expectToken(Token::Value _value, bool _advance) | ||||
| 	Token::Value tok = m_scanner->currentToken(); | ||||
| 	if (tok != _value) | ||||
| 	{ | ||||
| 		if (Token::isReservedKeyword(tok)) | ||||
| 		auto tokenName = [this](Token::Value _token) | ||||
| 		{ | ||||
| 			fatalParserError( | ||||
| 				string("Expected token ") + | ||||
| 				string(Token::name(_value)) + | ||||
| 				string(" got reserved keyword '") + | ||||
| 				string(Token::name(tok)) + | ||||
| 				string("'") | ||||
| 			); | ||||
| 		} | ||||
| 		else if (Token::isElementaryTypeName(tok)) //for the sake of accuracy in reporting
 | ||||
| 		{ | ||||
| 			ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken(); | ||||
| 			fatalParserError( | ||||
| 				string("Expected token ") + | ||||
| 				string(Token::name(_value)) + | ||||
| 				string(" got '") + | ||||
| 				elemTypeName.toString() + | ||||
| 				string("'") | ||||
| 			); | ||||
| 		} | ||||
| 		else | ||||
| 			fatalParserError( | ||||
| 				string("Expected token ") + | ||||
| 				string(Token::name(_value)) + | ||||
| 				string(" got '") + | ||||
| 				string(Token::name(m_scanner->currentToken())) + | ||||
| 				string("'") | ||||
| 			); | ||||
| 			if (_token == Token::Identifier) | ||||
| 				return string("identifier"); | ||||
| 			else if (_token == Token::EOS) | ||||
| 				return string("end of source"); | ||||
| 			else if (Token::isReservedKeyword(_token)) | ||||
| 				return string("reserved keyword '") + Token::friendlyName(_token) + "'"; | ||||
| 			else if (Token::isElementaryTypeName(_token)) //for the sake of accuracy in reporting
 | ||||
| 			{ | ||||
| 				ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken(); | ||||
| 				return string("'") + elemTypeName.toString() + "'"; | ||||
| 			} | ||||
| 			else | ||||
| 				return string("'") + Token::friendlyName(_token) + "'"; | ||||
| 		}; | ||||
| 
 | ||||
| 		fatalParserError(string("Expected ") + tokenName(_value) + string(" but got ") + tokenName(tok)); | ||||
| 	} | ||||
| 	if (_advance) | ||||
| 		m_scanner->next(); | ||||
|  | ||||
| @ -304,6 +304,17 @@ public: | ||||
| 		return m_string[tok]; | ||||
| 	} | ||||
| 
 | ||||
| 	static std::string friendlyName(Value tok) | ||||
| 	{ | ||||
| 		char const* ret = toString(tok); | ||||
| 		if (ret == nullptr) | ||||
| 		{ | ||||
| 			ret = name(tok); | ||||
| 			solAssert(ret != nullptr, ""); | ||||
| 		} | ||||
| 		return std::string(ret); | ||||
| 	} | ||||
| 
 | ||||
| 	// @returns the precedence > 0 for binary and compare
 | ||||
| 	// operators; returns 0 otherwise.
 | ||||
| 	static int precedence(Value tok) | ||||
|  | ||||
| @ -212,10 +212,10 @@ BOOST_AUTO_TEST_CASE(tokens_as_identifers) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(lacking_types) | ||||
| { | ||||
| 	CHECK_ERROR("{ let x := 1:u256 }", ParserError, "Expected token Identifier got 'Assign'"); | ||||
| 	CHECK_ERROR("{ let x:u256 := 1 }", ParserError, "Expected token Colon got 'RBrace'"); | ||||
| 	CHECK_ERROR("{ function f(a) {} }", ParserError, "Expected token Colon got 'RParen'"); | ||||
| 	CHECK_ERROR("{ function f(a:u256) -> b {} }", ParserError, "Expected token Colon got 'LBrace'"); | ||||
| 	CHECK_ERROR("{ let x := 1:u256 }", ParserError, "Expected identifier but got '='"); | ||||
| 	CHECK_ERROR("{ let x:u256 := 1 }", ParserError, "Expected ':' but got '}'"); | ||||
| 	CHECK_ERROR("{ function f(a) {} }", ParserError, "Expected ':' but got ')'"); | ||||
| 	CHECK_ERROR("{ function f(a:u256) -> b {} }", ParserError, "Expected ':' but got '{'"); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(invalid_types) | ||||
| @ -294,7 +294,7 @@ BOOST_AUTO_TEST_CASE(if_statement) | ||||
| BOOST_AUTO_TEST_CASE(if_statement_invalid) | ||||
| { | ||||
| 	CHECK_ERROR("{ if let x:u256 {} }", ParserError, "Literal or identifier expected."); | ||||
| 	CHECK_ERROR("{ if true:bool let x:u256 := 3:u256 }", ParserError, "Expected token LBrace"); | ||||
| 	CHECK_ERROR("{ if true:bool let x:u256 := 3:u256 }", ParserError, "Expected '{' but got reserved keyword 'let'"); | ||||
| 	// TODO change this to an error once we check types.
 | ||||
| 	BOOST_CHECK(successParse("{ if 42:u256 { } }")); | ||||
| } | ||||
|  | ||||
| @ -170,7 +170,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(surplus_input) | ||||
| { | ||||
| 	CHECK_PARSE_ERROR("{ } { }", ParserError, "Expected token EOS"); | ||||
| 	CHECK_PARSE_ERROR("{ } { }", ParserError, "Expected end of source but got '{'"); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(simple_instructions) | ||||
| @ -246,7 +246,7 @@ BOOST_AUTO_TEST_CASE(functional) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(functional_partial) | ||||
| { | ||||
| 	CHECK_PARSE_ERROR("{ let x := byte }", ParserError, "Expected token \"(\""); | ||||
| 	CHECK_PARSE_ERROR("{ let x := byte }", ParserError, "Expected '(' (instruction \"byte\" expects 2 arguments)"); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(functional_partial_success) | ||||
| @ -290,10 +290,10 @@ BOOST_AUTO_TEST_CASE(if_statement_scope) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(if_statement_invalid) | ||||
| { | ||||
| 	CHECK_PARSE_ERROR("{ if mload {} }", ParserError, "Expected token \"(\""); | ||||
| 	CHECK_PARSE_ERROR("{ if mload {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)"); | ||||
| 	BOOST_CHECK("{ if calldatasize() {}"); | ||||
| 	CHECK_PARSE_ERROR("{ if mstore(1, 1) {} }", ParserError, "Instruction \"mstore\" not allowed in this context"); | ||||
| 	CHECK_PARSE_ERROR("{ if 32 let x := 3 }", ParserError, "Expected token LBrace"); | ||||
| 	CHECK_PARSE_ERROR("{ if 32 let x := 3 }", ParserError, "Expected '{' but got reserved keyword 'let'"); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(switch_statement) | ||||
| @ -320,7 +320,7 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case) | ||||
| BOOST_AUTO_TEST_CASE(switch_invalid_expression) | ||||
| { | ||||
| 	CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal, identifier or instruction expected."); | ||||
| 	CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected token \"(\""); | ||||
| 	CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)"); | ||||
| 	CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", ParserError, "Instruction \"mstore\" not allowed in this context"); | ||||
| } | ||||
| 
 | ||||
| @ -341,7 +341,7 @@ BOOST_AUTO_TEST_CASE(switch_invalid_case) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(switch_invalid_body) | ||||
| { | ||||
| 	CHECK_PARSE_ERROR("{ switch 42 case 1 mul case 2 {} default {} }", ParserError, "Expected token LBrace got 'Identifier'"); | ||||
| 	CHECK_PARSE_ERROR("{ switch 42 case 1 mul case 2 {} default {} }", ParserError, "Expected '{' but got identifier"); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement) | ||||
| @ -353,10 +353,10 @@ BOOST_AUTO_TEST_CASE(for_statement) | ||||
| BOOST_AUTO_TEST_CASE(for_invalid_expression) | ||||
| { | ||||
| 	CHECK_PARSE_ERROR("{ for {} {} {} {} }", ParserError, "Literal, identifier or instruction expected."); | ||||
| 	CHECK_PARSE_ERROR("{ for 1 1 {} {} }", ParserError, "Expected token LBrace got 'Number'"); | ||||
| 	CHECK_PARSE_ERROR("{ for {} 1 1 {} }", ParserError, "Expected token LBrace got 'Number'"); | ||||
| 	CHECK_PARSE_ERROR("{ for {} 1 {} 1 }", ParserError, "Expected token LBrace got 'Number'"); | ||||
| 	CHECK_PARSE_ERROR("{ for {} mload {} {} }", ParserError, "Expected token \"(\""); | ||||
| 	CHECK_PARSE_ERROR("{ for 1 1 {} {} }", ParserError, "Expected '{' but got 'Number'"); | ||||
| 	CHECK_PARSE_ERROR("{ for {} 1 1 {} }", ParserError, "Expected '{' but got 'Number'"); | ||||
| 	CHECK_PARSE_ERROR("{ for {} 1 {} 1 }", ParserError, "Expected '{' but got 'Number'"); | ||||
| 	CHECK_PARSE_ERROR("{ for {} mload {} {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)"); | ||||
| 	CHECK_PARSE_ERROR("{ for {} mstore(1, 1) {} {} }", ParserError, "Instruction \"mstore\" not allowed in this context"); | ||||
| } | ||||
| 
 | ||||
| @ -437,13 +437,13 @@ BOOST_AUTO_TEST_CASE(invalid_tuple_assignment) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(instruction_too_few_arguments) | ||||
| { | ||||
| 	CHECK_PARSE_ERROR("{ mul() }", ParserError, "Expected expression (\"mul\" expects 2 arguments)"); | ||||
| 	CHECK_PARSE_ERROR("{ mul(1) }", ParserError, "Expected comma (\"mul\" expects 2 arguments)"); | ||||
| 	CHECK_PARSE_ERROR("{ mul() }", ParserError, "Expected expression (instruction \"mul\" expects 2 arguments)"); | ||||
| 	CHECK_PARSE_ERROR("{ mul(1) }", ParserError, "Expected ',' (instruction \"mul\" expects 2 arguments)"); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(instruction_too_many_arguments) | ||||
| { | ||||
| 	CHECK_PARSE_ERROR("{ mul(1, 2, 3) }", ParserError, "Expected ')' (\"mul\" expects 2 arguments)"); | ||||
| 	CHECK_PARSE_ERROR("{ mul(1, 2, 3) }", ParserError, "Expected ')' (instruction \"mul\" expects 2 arguments)"); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(recursion_depth) | ||||
|  | ||||
| @ -992,7 +992,7 @@ BOOST_AUTO_TEST_CASE(keyword_is_reserved) | ||||
| 	for (const auto& keyword: keywords) | ||||
| 	{ | ||||
| 		auto text = std::string("contract ") + keyword + " {}"; | ||||
| 		CHECK_PARSE_ERROR(text.c_str(), "Expected token Identifier got reserved keyword"); | ||||
| 		CHECK_PARSE_ERROR(text.c_str(), "Expected identifier but got reserved keyword"); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -326,8 +326,8 @@ BOOST_AUTO_TEST_CASE(compilation_error) | ||||
| 		{ | ||||
| 			BOOST_CHECK_EQUAL( | ||||
| 				dev::jsonCompactPrint(error), | ||||
| 				"{\"component\":\"general\",\"formattedMessage\":\"fileA:1:23: ParserError: Expected token Identifier got 'RBrace'\\n" | ||||
| 				"contract A { function }\\n                      ^\\n\",\"message\":\"Expected token Identifier got 'RBrace'\"," | ||||
| 				"{\"component\":\"general\",\"formattedMessage\":\"fileA:1:23: ParserError: Expected identifier but got '}'\\n" | ||||
| 				"contract A { function }\\n                      ^\\n\",\"message\":\"Expected identifier but got '}'\"," | ||||
| 				"\"severity\":\"error\",\"sourceLocation\":{\"end\":22,\"file\":\"fileA\",\"start\":22},\"type\":\"ParserError\"}" | ||||
| 			); | ||||
| 		} | ||||
|  | ||||
| @ -5,4 +5,4 @@ contract C { | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (118-118): Expected token Semicolon got 'Identifier' | ||||
| // ParserError: (118-118): Expected ';' but got identifier | ||||
|  | ||||
| @ -2,4 +2,4 @@ contract Foo { | ||||
| 	uint constant = 4; | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (30-30): Expected token Identifier got 'Assign' | ||||
| // ParserError: (30-30): Expected identifier but got '=' | ||||
|  | ||||
| @ -5,4 +5,4 @@ contract C { | ||||
| 	} | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (49-49): Expected token LParen got 'Semicolon' | ||||
| // ParserError: (49-49): Expected '(' but got ';' | ||||
|  | ||||
| @ -2,4 +2,4 @@ contract c { | ||||
| 	event e; | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (21-21): Expected token LParen got 'Semicolon' | ||||
| // ParserError: (21-21): Expected '(' but got ';' | ||||
|  | ||||
| @ -2,4 +2,4 @@ contract c { | ||||
| 	uint external x; | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (19-19): Expected token Identifier got 'External' | ||||
| // ParserError: (19-19): Expected identifier but got 'external' | ||||
|  | ||||
| @ -2,4 +2,4 @@ contract A { | ||||
| 	fixed40x40 pi = 3.14.15; | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (34-34): Expected token Semicolon got 'Number' | ||||
| // ParserError: (34-34): Expected ';' but got 'Number' | ||||
|  | ||||
| @ -2,4 +2,4 @@ contract test { | ||||
| 	function (uint, uint) modifier1() returns (uint) f1; | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (66-66): Expected token LBrace got 'Identifier' | ||||
| // ParserError: (66-66): Expected '{' but got identifier | ||||
|  | ||||
| @ -6,4 +6,4 @@ contract Foo { | ||||
| 	} | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (67-67): Expected token Semicolon got 'Constant' | ||||
| // ParserError: (67-67): Expected ';' but got 'constant' | ||||
|  | ||||
| @ -2,4 +2,4 @@ contract Foo { | ||||
| 	uint[] memory x; | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (23-23): Expected token Identifier got 'Memory' | ||||
| // ParserError: (23-23): Expected identifier but got 'memory' | ||||
|  | ||||
| @ -2,4 +2,4 @@ contract c { | ||||
| 	enum foo { WARNING,} | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (33-33): Expected Identifier after ',' | ||||
| // ParserError: (33-33): Expected identifier after ',' | ||||
|  | ||||
| @ -3,4 +3,4 @@ contract test { | ||||
| 	function b() returns (uint r) { r = a({: 1, : 2, : 3}); } | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (143-143): Expected token Identifier got 'Colon' | ||||
| // ParserError: (143-143): Expected identifier but got ':' | ||||
|  | ||||
| @ -2,4 +2,4 @@ contract test { | ||||
|     uint256 ; | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (28-28): Expected token Identifier got 'Semicolon' | ||||
| // ParserError: (28-28): Expected identifier but got ';' | ||||
|  | ||||
| @ -2,4 +2,4 @@ contract c { | ||||
| 	modifier mod { if (msg.sender == 0) _ } | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (52-52): Expected token Semicolon got 'RBrace' | ||||
| // ParserError: (52-52): Expected ';' but got '}' | ||||
|  | ||||
| @ -2,4 +2,4 @@ contract test { | ||||
| 	uint payable x; | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (22-22): Expected token Identifier got 'Payable' | ||||
| // ParserError: (22-22): Expected identifier but got 'payable' | ||||
|  | ||||
| @ -4,4 +4,4 @@ contract C { | ||||
| 	} | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (42-42): Expected token Comma got 'Number' | ||||
| // ParserError: (42-42): Expected ',' but got 'Number' | ||||
|  | ||||
| @ -2,4 +2,4 @@ contract Foo { | ||||
| 	function f() { var[] a; } | ||||
| } | ||||
| // ---- | ||||
| // ParserError: (34-34): Expected token Identifier got 'LBrack' | ||||
| // ParserError: (34-34): Expected identifier but got '[' | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user