2016-01-14 01:58:09 +00:00
/*
2016-11-18 23:13:20 +00:00
This file is part of solidity .
2016-01-14 01:58:09 +00:00
2016-11-18 23:13:20 +00:00
solidity is free software : you can redistribute it and / or modify
2016-01-14 01:58:09 +00:00
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 .
2016-11-18 23:13:20 +00:00
solidity is distributed in the hope that it will be useful ,
2016-01-14 01:58:09 +00:00
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
2016-11-18 23:13:20 +00:00
along with solidity . If not , see < http : //www.gnu.org/licenses/>.
2016-01-14 01:58:09 +00:00
*/
2016-08-19 17:57:21 +00:00
# include <libsolidity/analysis/SyntaxChecker.h>
2018-12-17 11:30:08 +00:00
2016-01-14 01:58:09 +00:00
# include <libsolidity/ast/AST.h>
2017-08-08 13:09:57 +00:00
# include <libsolidity/ast/ExperimentalFeatures.h>
2016-08-19 17:57:21 +00:00
# include <libsolidity/interface/Version.h>
2016-01-14 01:58:09 +00:00
2019-05-27 12:01:53 +00:00
# include <libyul/optimiser/Semantics.h>
# include <libyul/AsmData.h>
2018-12-17 11:30:08 +00:00
# include <liblangutil/ErrorReporter.h>
2019-04-17 11:02:30 +00:00
# include <liblangutil/SemVerHandler.h>
2018-12-17 11:30:08 +00:00
# include <boost/algorithm/cxx11/all_of.hpp>
2017-10-25 08:12:07 +00:00
# include <boost/algorithm/string.hpp>
2018-12-17 11:30:08 +00:00
# include <memory>
2017-10-25 08:12:07 +00:00
# include <string>
2016-01-14 01:58:09 +00:00
using namespace std ;
using namespace dev ;
2018-11-14 16:11:55 +00:00
using namespace langutil ;
2016-01-14 01:58:09 +00:00
using namespace dev : : solidity ;
2017-01-27 17:27:59 +00:00
bool SyntaxChecker : : checkSyntax ( ASTNode const & _astRoot )
2016-01-14 01:58:09 +00:00
{
2017-01-27 17:27:59 +00:00
_astRoot . accept ( * this ) ;
2017-05-11 13:26:35 +00:00
return Error : : containsOnlyWarnings ( m_errorReporter . errors ( ) ) ;
2016-01-14 01:58:09 +00:00
}
2017-07-08 23:10:22 +00:00
bool SyntaxChecker : : visit ( SourceUnit const & _sourceUnit )
2016-08-19 17:57:21 +00:00
{
m_versionPragmaFound = false ;
2017-07-08 23:10:22 +00:00
m_sourceUnit = & _sourceUnit ;
2016-08-19 17:57:21 +00:00
return true ;
}
void SyntaxChecker : : endVisit ( SourceUnit const & _sourceUnit )
{
if ( ! m_versionPragmaFound )
{
2016-10-05 09:58:25 +00:00
string errorString ( " Source file does not specify required compiler version! " ) ;
SemVerVersion recommendedVersion { string ( VersionString ) } ;
if ( ! recommendedVersion . isPrerelease ( ) )
errorString + =
2018-10-08 20:58:34 +00:00
" Consider adding \" pragma solidity ^ " +
2016-10-05 09:58:25 +00:00
to_string ( recommendedVersion . major ( ) ) +
string ( " . " ) +
to_string ( recommendedVersion . minor ( ) ) +
string ( " . " ) +
2017-11-14 12:45:51 +00:00
to_string ( recommendedVersion . patch ( ) ) +
2016-10-05 09:58:25 +00:00
string ( " ; \" " ) ;
2017-05-11 13:26:35 +00:00
m_errorReporter . warning ( _sourceUnit . location ( ) , errorString ) ;
2016-08-19 17:57:21 +00:00
}
2017-07-08 23:10:22 +00:00
m_sourceUnit = nullptr ;
2016-08-19 17:57:21 +00:00
}
bool SyntaxChecker : : visit ( PragmaDirective const & _pragma )
{
solAssert ( ! _pragma . tokens ( ) . empty ( ) , " " ) ;
solAssert ( _pragma . tokens ( ) . size ( ) = = _pragma . literals ( ) . size ( ) , " " ) ;
2017-07-08 23:10:22 +00:00
if ( _pragma . tokens ( ) [ 0 ] ! = Token : : Identifier )
m_errorReporter . syntaxError ( _pragma . location ( ) , " Invalid pragma \" " + _pragma . literals ( ) [ 0 ] + " \" " ) ;
else if ( _pragma . literals ( ) [ 0 ] = = " experimental " )
{
solAssert ( m_sourceUnit , " " ) ;
vector < string > literals ( _pragma . literals ( ) . begin ( ) + 1 , _pragma . literals ( ) . end ( ) ) ;
2018-10-09 03:29:37 +00:00
if ( literals . empty ( ) )
2017-07-08 23:10:22 +00:00
m_errorReporter . syntaxError (
_pragma . location ( ) ,
2017-08-04 22:28:28 +00:00
" Experimental feature name is missing. "
) ;
else if ( literals . size ( ) > 1 )
m_errorReporter . syntaxError (
_pragma . location ( ) ,
" Stray arguments. "
2017-07-08 23:10:22 +00:00
) ;
else
2017-08-02 19:05:35 +00:00
{
2017-08-04 22:28:28 +00:00
string const literal = literals [ 0 ] ;
if ( literal . empty ( ) )
m_errorReporter . syntaxError ( _pragma . location ( ) , " Empty experimental feature name is invalid. " ) ;
2017-08-08 13:09:57 +00:00
else if ( ! ExperimentalFeatureNames . count ( literal ) )
2017-08-04 22:28:28 +00:00
m_errorReporter . syntaxError ( _pragma . location ( ) , " Unsupported experimental feature name. " ) ;
2017-08-08 13:09:57 +00:00
else if ( m_sourceUnit - > annotation ( ) . experimentalFeatures . count ( ExperimentalFeatureNames . at ( literal ) ) )
2017-08-04 22:28:28 +00:00
m_errorReporter . syntaxError ( _pragma . location ( ) , " Duplicate experimental feature name. " ) ;
else
2017-07-08 23:10:22 +00:00
{
2018-02-21 15:33:59 +00:00
auto feature = ExperimentalFeatureNames . at ( literal ) ;
m_sourceUnit - > annotation ( ) . experimentalFeatures . insert ( feature ) ;
if ( ! ExperimentalFeatureOnlyAnalysis . count ( feature ) )
m_errorReporter . warning ( _pragma . location ( ) , " Experimental features are turned on. Do not use experimental features on live deployments. " ) ;
2017-07-08 23:10:22 +00:00
}
2017-08-02 19:05:35 +00:00
}
2017-07-08 23:10:22 +00:00
}
else if ( _pragma . literals ( ) [ 0 ] = = " solidity " )
2016-08-19 17:57:21 +00:00
{
2018-10-22 14:48:21 +00:00
vector < Token > tokens ( _pragma . tokens ( ) . begin ( ) + 1 , _pragma . tokens ( ) . end ( ) ) ;
2016-08-19 17:57:21 +00:00
vector < string > literals ( _pragma . literals ( ) . begin ( ) + 1 , _pragma . literals ( ) . end ( ) ) ;
SemVerMatchExpressionParser parser ( tokens , literals ) ;
auto matchExpression = parser . parse ( ) ;
2018-12-12 15:45:17 +00:00
static SemVerVersion const currentVersion { string ( VersionString ) } ;
2016-08-19 17:57:21 +00:00
if ( ! matchExpression . matches ( currentVersion ) )
2017-05-11 13:26:35 +00:00
m_errorReporter . syntaxError (
2016-08-19 17:57:21 +00:00
_pragma . location ( ) ,
" Source file requires different compiler version (current compiler is " +
2016-08-23 15:03:23 +00:00
string ( VersionString ) + " - note that nightly builds are considered to be "
" strictly less than the released version "
2016-08-19 17:57:21 +00:00
) ;
m_versionPragmaFound = true ;
}
2017-07-08 23:10:22 +00:00
else
m_errorReporter . syntaxError ( _pragma . location ( ) , " Unknown pragma \" " + _pragma . literals ( ) [ 0 ] + " \" " ) ;
2016-08-19 17:57:21 +00:00
return true ;
}
2016-08-06 13:08:06 +00:00
bool SyntaxChecker : : visit ( ModifierDefinition const & )
{
m_placeholderFound = false ;
return true ;
}
void SyntaxChecker : : endVisit ( ModifierDefinition const & _modifier )
{
if ( ! m_placeholderFound )
2017-05-11 13:26:35 +00:00
m_errorReporter . syntaxError ( _modifier . body ( ) . location ( ) , " Modifier body does not contain '_'. " ) ;
2016-08-06 13:08:06 +00:00
m_placeholderFound = false ;
}
2018-09-04 10:14:04 +00:00
void SyntaxChecker : : checkSingleStatementVariableDeclaration ( ASTNode const & _statement )
2018-09-03 16:17:59 +00:00
{
2018-09-04 10:14:04 +00:00
auto varDecl = dynamic_cast < VariableDeclarationStatement const * > ( & _statement ) ;
2018-09-03 16:17:59 +00:00
if ( varDecl )
2018-09-04 10:14:04 +00:00
m_errorReporter . syntaxError ( _statement . location ( ) , " Variable declarations can only be used inside blocks. " ) ;
2018-09-03 16:17:59 +00:00
}
bool SyntaxChecker : : visit ( IfStatement const & _ifStatement )
{
2018-09-04 10:14:04 +00:00
checkSingleStatementVariableDeclaration ( _ifStatement . trueStatement ( ) ) ;
2018-09-03 16:17:59 +00:00
if ( Statement const * _statement = _ifStatement . falseStatement ( ) )
2018-09-04 10:14:04 +00:00
checkSingleStatementVariableDeclaration ( * _statement ) ;
2018-09-03 16:17:59 +00:00
return true ;
}
bool SyntaxChecker : : visit ( WhileStatement const & _whileStatement )
2016-01-14 01:58:09 +00:00
{
2016-01-19 02:16:13 +00:00
m_inLoopDepth + + ;
2018-09-04 10:14:04 +00:00
checkSingleStatementVariableDeclaration ( _whileStatement . body ( ) ) ;
2016-01-14 01:58:09 +00:00
return true ;
}
2017-08-15 12:22:50 +00:00
void SyntaxChecker : : endVisit ( WhileStatement const & )
2016-01-14 01:58:09 +00:00
{
2016-01-19 02:16:13 +00:00
m_inLoopDepth - - ;
2016-01-14 01:58:09 +00:00
}
2018-09-03 16:17:59 +00:00
bool SyntaxChecker : : visit ( ForStatement const & _forStatement )
2016-01-14 01:58:09 +00:00
{
2016-01-19 02:16:13 +00:00
m_inLoopDepth + + ;
2018-09-04 10:14:04 +00:00
checkSingleStatementVariableDeclaration ( _forStatement . body ( ) ) ;
2016-01-14 01:58:09 +00:00
return true ;
}
2016-01-19 02:18:01 +00:00
void SyntaxChecker : : endVisit ( ForStatement const & )
2016-01-14 01:58:09 +00:00
{
2016-01-19 02:16:13 +00:00
m_inLoopDepth - - ;
2016-01-14 01:58:09 +00:00
}
bool SyntaxChecker : : visit ( Continue const & _continueStatement )
{
2016-01-19 02:16:13 +00:00
if ( m_inLoopDepth < = 0 )
2016-01-14 01:58:09 +00:00
// we're not in a for/while loop, report syntax error
2017-05-11 13:26:35 +00:00
m_errorReporter . syntaxError ( _continueStatement . location ( ) , " \" continue \" has to be in a \" for \" or \" while \" loop. " ) ;
2016-01-14 01:58:09 +00:00
return true ;
}
bool SyntaxChecker : : visit ( Break const & _breakStatement )
{
2016-01-19 02:16:13 +00:00
if ( m_inLoopDepth < = 0 )
2016-01-14 01:58:09 +00:00
// we're not in a for/while loop, report syntax error
2017-05-11 13:26:35 +00:00
m_errorReporter . syntaxError ( _breakStatement . location ( ) , " \" break \" has to be in a \" for \" or \" while \" loop. " ) ;
2016-01-14 01:58:09 +00:00
return true ;
}
2017-07-05 17:45:12 +00:00
bool SyntaxChecker : : visit ( Throw const & _throwStatement )
{
2018-07-11 14:06:31 +00:00
m_errorReporter . syntaxError (
_throwStatement . location ( ) ,
" \" throw \" is deprecated in favour of \" revert() \" , \" require() \" and \" assert() \" . "
) ;
2017-07-05 17:45:12 +00:00
return true ;
}
2017-10-25 08:12:07 +00:00
bool SyntaxChecker : : visit ( Literal const & _literal )
{
2018-08-03 14:13:52 +00:00
if ( _literal . token ( ) ! = Token : : Number )
2017-10-25 08:12:07 +00:00
return true ;
2018-08-03 14:13:52 +00:00
ASTString const & value = _literal . value ( ) ;
solAssert ( ! value . empty ( ) , " " ) ;
2017-10-25 08:12:07 +00:00
2018-08-03 14:13:52 +00:00
// Generic checks no matter what base this number literal is of:
if ( value . back ( ) = = ' _ ' )
{
m_errorReporter . syntaxError ( _literal . location ( ) , " Invalid use of underscores in number literal. No trailing underscores allowed. " ) ;
2017-10-25 08:12:07 +00:00
return true ;
2018-08-03 14:13:52 +00:00
}
2017-10-25 08:12:07 +00:00
2018-08-03 14:13:52 +00:00
if ( value . find ( " __ " ) ! = ASTString : : npos )
2017-10-25 08:12:07 +00:00
{
2018-08-03 14:13:52 +00:00
m_errorReporter . syntaxError ( _literal . location ( ) , " Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed. " ) ;
return true ;
2017-10-25 08:12:07 +00:00
}
2018-08-03 14:13:52 +00:00
if ( ! _literal . isHexNumber ( ) ) // decimal literal
2017-10-25 08:12:07 +00:00
{
2018-08-03 14:13:52 +00:00
if ( value . find ( " ._ " ) ! = ASTString : : npos )
m_errorReporter . syntaxError ( _literal . location ( ) , " Invalid use of underscores in number literal. No underscores in front of the fraction part allowed. " ) ;
if ( value . find ( " _. " ) ! = ASTString : : npos )
m_errorReporter . syntaxError ( _literal . location ( ) , " Invalid use of underscores in number literal. No underscores in front of the fraction part allowed. " ) ;
if ( value . find ( " _e " ) ! = ASTString : : npos )
m_errorReporter . syntaxError ( _literal . location ( ) , " Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed. " ) ;
if ( value . find ( " e_ " ) ! = ASTString : : npos )
m_errorReporter . syntaxError ( _literal . location ( ) , " Invalid use of underscores in number literal. No underscore in front of exponent allowed. " ) ;
2017-10-25 08:12:07 +00:00
}
return true ;
}
2017-04-29 00:43:19 +00:00
bool SyntaxChecker : : visit ( UnaryOperation const & _operation )
{
if ( _operation . getOperator ( ) = = Token : : Add )
2018-07-03 09:10:14 +00:00
m_errorReporter . syntaxError ( _operation . location ( ) , " Use of unary + is disallowed. " ) ;
2017-04-29 00:43:19 +00:00
return true ;
}
2019-05-27 12:01:53 +00:00
bool SyntaxChecker : : visit ( InlineAssembly const & _inlineAssembly )
{
if ( ! m_useYulOptimizer )
return false ;
if ( yul : : SideEffectsCollector (
_inlineAssembly . dialect ( ) ,
_inlineAssembly . operations ( )
) . containsMSize ( ) )
m_errorReporter . syntaxError (
_inlineAssembly . location ( ) ,
" The msize instruction cannot be used when the Yul optimizer is activated because "
" it can change its semantics. Either disable the Yul optimizer or do not use the instruction. "
) ;
return false ;
}
2016-08-19 17:57:21 +00:00
bool SyntaxChecker : : visit ( PlaceholderStatement const & )
2016-08-06 13:08:06 +00:00
{
m_placeholderFound = true ;
return true ;
}
2018-07-10 20:43:02 +00:00
bool SyntaxChecker : : visit ( ContractDefinition const & _contract )
{
2019-01-17 11:59:11 +00:00
m_isInterface = _contract . isInterface ( ) ;
2018-06-27 10:29:03 +00:00
ASTString const & contractName = _contract . name ( ) ;
for ( FunctionDefinition const * function : _contract . definedFunctions ( ) )
if ( function - > name ( ) = = contractName )
m_errorReporter . syntaxError ( function - > location ( ) ,
" Functions are not allowed to have the same name as the contract. "
" If you intend this to be a constructor, use \" constructor(...) { ... } \" to define it. "
) ;
2018-07-10 20:43:02 +00:00
return true ;
}
2017-08-15 12:22:50 +00:00
bool SyntaxChecker : : visit ( FunctionDefinition const & _function )
{
2018-07-04 16:04:44 +00:00
if ( _function . noVisibilitySpecified ( ) )
2018-07-10 20:43:02 +00:00
{
string suggestedVisibility = _function . isFallback ( ) | | m_isInterface ? " external " : " public " ;
m_errorReporter . syntaxError (
_function . location ( ) ,
" No visibility specified. Did you intend to add \" " + suggestedVisibility + " \" ? "
) ;
}
2018-03-01 17:39:01 +00:00
2018-03-16 11:39:30 +00:00
if ( ! _function . isImplemented ( ) & & ! _function . modifiers ( ) . empty ( ) )
2018-07-03 09:28:57 +00:00
m_errorReporter . syntaxError ( _function . location ( ) , " Functions without implementation cannot have modifiers. " ) ;
2017-08-15 12:22:50 +00:00
return true ;
}
2017-06-24 17:09:19 +00:00
bool SyntaxChecker : : visit ( FunctionTypeName const & _node )
{
for ( auto const & decl : _node . parameterTypeList ( ) - > parameters ( ) )
if ( ! decl - > name ( ) . empty ( ) )
m_errorReporter . warning ( decl - > location ( ) , " Naming function type parameters is deprecated. " ) ;
for ( auto const & decl : _node . returnParameterTypeList ( ) - > parameters ( ) )
if ( ! decl - > name ( ) . empty ( ) )
2018-06-14 15:32:09 +00:00
m_errorReporter . syntaxError ( decl - > location ( ) , " Return parameters in function types may not be named. " ) ;
2017-06-24 17:09:19 +00:00
return true ;
}
2018-01-22 21:32:47 +00:00
2018-07-05 10:31:46 +00:00
bool SyntaxChecker : : visit ( VariableDeclarationStatement const & _statement )
{
// Report if none of the variable components in the tuple have a name (only possible via deprecated "var")
if ( boost : : algorithm : : all_of_equal ( _statement . declarations ( ) , nullptr ) )
m_errorReporter . syntaxError (
_statement . location ( ) ,
" The use of the \" var \" keyword is disallowed. The declaration part of the statement can be removed, since it is empty. "
) ;
return true ;
}
2018-03-27 13:38:28 +00:00
bool SyntaxChecker : : visit ( StructDefinition const & _struct )
{
if ( _struct . members ( ) . empty ( ) )
2018-06-06 09:15:22 +00:00
m_errorReporter . syntaxError ( _struct . location ( ) , " Defining empty structs is disallowed. " ) ;
2018-03-27 13:38:28 +00:00
return true ;
}