2017-08-28 17:48:34 +00:00
/*
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/>.
*/
2020-07-17 14:54:12 +00:00
// SPDX-License-Identifier: GPL-3.0
2017-08-28 17:48:34 +00:00
# include <libsolidity/analysis/ViewPureChecker.h>
2017-09-14 15:53:43 +00:00
# include <libsolidity/ast/ExperimentalFeatures.h>
2020-10-29 14:00:27 +00:00
# include <libyul/AST.h>
2019-05-22 11:57:48 +00:00
# include <libyul/backends/evm/EVMDialect.h>
2018-11-14 16:11:55 +00:00
# include <liblangutil/ErrorReporter.h>
2018-12-17 11:30:08 +00:00
# include <libevmasm/SemanticInformation.h>
2019-11-19 15:42:49 +00:00
2017-08-31 11:13:35 +00:00
# include <functional>
2020-04-01 03:04:29 +00:00
# include <utility>
2019-11-19 15:42:49 +00:00
# include <variant>
2017-08-31 11:13:35 +00:00
2017-08-28 17:48:34 +00:00
using namespace std ;
2019-12-11 16:31:36 +00:00
using namespace solidity ;
using namespace solidity : : langutil ;
using namespace solidity : : frontend ;
2017-08-28 17:48:34 +00:00
2017-09-01 17:58:38 +00:00
namespace
{
2017-08-31 11:13:35 +00:00
2019-11-19 15:42:49 +00:00
class AssemblyViewPureChecker
2017-08-31 11:13:35 +00:00
{
public :
2019-05-22 11:57:48 +00:00
explicit AssemblyViewPureChecker (
yul : : Dialect const & _dialect ,
std : : function < void ( StateMutability , SourceLocation const & ) > _reportMutability
) :
m_dialect ( _dialect ) ,
2020-04-01 03:04:29 +00:00
m_reportMutability ( std : : move ( _reportMutability ) ) { }
2017-08-31 11:13:35 +00:00
2018-11-21 11:42:34 +00:00
void operator ( ) ( yul : : Literal const & ) { }
void operator ( ) ( yul : : Identifier const & ) { }
void operator ( ) ( yul : : ExpressionStatement const & _expr )
2017-12-08 13:01:22 +00:00
{
2019-11-19 15:42:49 +00:00
std : : visit ( * this , _expr . expression ) ;
2017-12-08 13:01:22 +00:00
}
2018-11-21 11:42:34 +00:00
void operator ( ) ( yul : : Assignment const & _assignment )
2017-08-31 11:13:35 +00:00
{
2019-11-19 15:42:49 +00:00
std : : visit ( * this , * _assignment . value ) ;
2017-08-31 11:13:35 +00:00
}
2018-11-21 11:42:34 +00:00
void operator ( ) ( yul : : VariableDeclaration const & _varDecl )
2017-08-31 11:13:35 +00:00
{
if ( _varDecl . value )
2019-11-19 15:42:49 +00:00
std : : visit ( * this , * _varDecl . value ) ;
2017-08-31 11:13:35 +00:00
}
2018-11-21 11:42:34 +00:00
void operator ( ) ( yul : : FunctionDefinition const & _funDef )
2017-08-31 11:13:35 +00:00
{
( * this ) ( _funDef . body ) ;
}
2018-11-21 11:42:34 +00:00
void operator ( ) ( yul : : FunctionCall const & _funCall )
2017-08-31 11:13:35 +00:00
{
2019-05-22 11:57:48 +00:00
if ( yul : : EVMDialect const * dialect = dynamic_cast < decltype ( dialect ) > ( & m_dialect ) )
if ( yul : : BuiltinFunctionForEVM const * fun = dialect - > builtin ( _funCall . functionName . name ) )
if ( fun - > instruction )
checkInstruction ( _funCall . location , * fun - > instruction ) ;
2017-08-31 11:13:35 +00:00
for ( auto const & arg : _funCall . arguments )
2019-11-19 15:42:49 +00:00
std : : visit ( * this , arg ) ;
2017-08-31 11:13:35 +00:00
}
2018-11-21 11:42:34 +00:00
void operator ( ) ( yul : : If const & _if )
2017-11-21 12:36:41 +00:00
{
2019-11-19 15:42:49 +00:00
std : : visit ( * this , * _if . condition ) ;
2017-11-21 12:36:41 +00:00
( * this ) ( _if . body ) ;
}
2018-11-21 11:42:34 +00:00
void operator ( ) ( yul : : Switch const & _switch )
2017-08-31 11:13:35 +00:00
{
2019-11-19 15:42:49 +00:00
std : : visit ( * this , * _switch . expression ) ;
2017-08-31 11:13:35 +00:00
for ( auto const & _case : _switch . cases )
{
if ( _case . value )
( * this ) ( * _case . value ) ;
( * this ) ( _case . body ) ;
}
}
2018-11-21 11:42:34 +00:00
void operator ( ) ( yul : : ForLoop const & _for )
2017-08-31 11:13:35 +00:00
{
( * this ) ( _for . pre ) ;
2019-11-19 15:42:49 +00:00
std : : visit ( * this , * _for . condition ) ;
2017-08-31 11:13:35 +00:00
( * this ) ( _for . body ) ;
( * this ) ( _for . post ) ;
}
2019-03-04 14:38:05 +00:00
void operator ( ) ( yul : : Break const & )
{
}
void operator ( ) ( yul : : Continue const & )
{
}
2019-10-28 14:25:02 +00:00
void operator ( ) ( yul : : Leave const & )
{
}
2018-11-21 11:42:34 +00:00
void operator ( ) ( yul : : Block const & _block )
2017-08-31 11:13:35 +00:00
{
for ( auto const & s : _block . statements )
2019-11-19 15:42:49 +00:00
std : : visit ( * this , s ) ;
2017-08-31 11:13:35 +00:00
}
private :
2019-12-11 16:31:36 +00:00
void checkInstruction ( SourceLocation _location , evmasm : : Instruction _instruction )
2017-12-05 13:44:20 +00:00
{
2019-12-11 16:31:36 +00:00
if ( evmasm : : SemanticInformation : : invalidInViewFunctions ( _instruction ) )
2017-12-05 13:44:20 +00:00
m_reportMutability ( StateMutability : : NonPayable , _location ) ;
2019-12-11 16:31:36 +00:00
else if ( evmasm : : SemanticInformation : : invalidInPureFunctions ( _instruction ) )
2017-12-05 13:44:20 +00:00
m_reportMutability ( StateMutability : : View , _location ) ;
}
2019-05-22 11:57:48 +00:00
yul : : Dialect const & m_dialect ;
std : : function < void ( StateMutability , SourceLocation const & ) > m_reportMutability ;
2017-08-31 11:13:35 +00:00
} ;
2017-09-01 17:58:38 +00:00
}
2017-08-31 11:13:35 +00:00
2017-08-28 17:48:34 +00:00
bool ViewPureChecker : : check ( )
{
2020-05-04 16:38:00 +00:00
for ( auto const & source : m_ast )
source - > accept ( * this ) ;
2017-08-28 17:48:34 +00:00
return ! m_errors ;
}
bool ViewPureChecker : : visit ( FunctionDefinition const & _funDef )
{
solAssert ( ! m_currentFunction , " " ) ;
m_currentFunction = & _funDef ;
2018-07-26 19:45:24 +00:00
m_bestMutabilityAndLocation = { StateMutability : : Pure , _funDef . location ( ) } ;
2017-08-28 17:48:34 +00:00
return true ;
}
void ViewPureChecker : : endVisit ( FunctionDefinition const & _funDef )
{
solAssert ( m_currentFunction = = & _funDef , " " ) ;
if (
2018-07-26 19:45:24 +00:00
m_bestMutabilityAndLocation . mutability < _funDef . stateMutability ( ) & &
2017-08-28 17:48:34 +00:00
_funDef . stateMutability ( ) ! = StateMutability : : Payable & &
_funDef . isImplemented ( ) & &
2018-12-12 11:03:59 +00:00
! _funDef . body ( ) . statements ( ) . empty ( ) & &
2017-08-28 17:48:34 +00:00
! _funDef . isConstructor ( ) & &
! _funDef . isFallback ( ) & &
2019-09-09 16:22:02 +00:00
! _funDef . isReceive ( ) & &
2020-07-15 09:52:56 +00:00
! _funDef . virtualSemantics ( )
2017-08-28 17:48:34 +00:00
)
m_errorReporter . warning (
2020-05-05 22:38:28 +00:00
2018 _error ,
2017-08-28 17:48:34 +00:00
_funDef . location ( ) ,
2018-07-26 19:45:24 +00:00
" Function state mutability can be restricted to " + stateMutabilityToString ( m_bestMutabilityAndLocation . mutability )
2017-08-28 17:48:34 +00:00
) ;
m_currentFunction = nullptr ;
}
2018-07-26 19:45:24 +00:00
bool ViewPureChecker : : visit ( ModifierDefinition const & _modifier )
2017-08-28 17:48:34 +00:00
{
solAssert ( m_currentFunction = = nullptr , " " ) ;
2018-07-26 19:45:24 +00:00
m_bestMutabilityAndLocation = { StateMutability : : Pure , _modifier . location ( ) } ;
2017-08-28 17:48:34 +00:00
return true ;
}
void ViewPureChecker : : endVisit ( ModifierDefinition const & _modifierDef )
{
solAssert ( m_currentFunction = = nullptr , " " ) ;
2018-07-26 19:45:24 +00:00
m_inferredMutability [ & _modifierDef ] = std : : move ( m_bestMutabilityAndLocation ) ;
2017-08-28 17:48:34 +00:00
}
void ViewPureChecker : : endVisit ( Identifier const & _identifier )
{
Declaration const * declaration = _identifier . annotation ( ) . referencedDeclaration ;
solAssert ( declaration , " " ) ;
StateMutability mutability = StateMutability : : Pure ;
2020-04-09 10:48:57 +00:00
bool writes = _identifier . annotation ( ) . willBeWrittenTo ;
2017-08-28 17:48:34 +00:00
if ( VariableDeclaration const * varDecl = dynamic_cast < VariableDeclaration const * > ( declaration ) )
{
2020-11-09 16:12:29 +00:00
if ( varDecl - > immutable ( ) )
{
// Immutables that are assigned literals are pure.
if ( ! ( varDecl - > value ( ) & & varDecl - > value ( ) - > annotation ( ) . type - > category ( ) = = Type : : Category : : RationalNumber ) )
mutability = StateMutability : : View ;
}
else if ( varDecl - > isStateVariable ( ) & & ! varDecl - > isConstant ( ) )
2017-08-28 17:48:34 +00:00
mutability = writes ? StateMutability : : NonPayable : StateMutability : : View ;
}
else if ( MagicVariableDeclaration const * magicVar = dynamic_cast < MagicVariableDeclaration const * > ( declaration ) )
{
switch ( magicVar - > type ( ) - > category ( ) )
{
case Type : : Category : : Contract :
2020-10-14 09:17:53 +00:00
solAssert ( _identifier . name ( ) = = " this " , " " ) ;
if ( dynamic_cast < ContractType const * > ( magicVar - > type ( ) ) )
2017-08-28 17:48:34 +00:00
// reads the address
mutability = StateMutability : : View ;
break ;
case Type : : Category : : Integer :
solAssert ( _identifier . name ( ) = = " now " , " " ) ;
mutability = StateMutability : : View ;
break ;
default :
break ;
}
}
2017-08-31 11:13:35 +00:00
reportMutability ( mutability , _identifier . location ( ) ) ;
2017-08-28 17:48:34 +00:00
}
void ViewPureChecker : : endVisit ( InlineAssembly const & _inlineAssembly )
{
2017-08-31 11:13:35 +00:00
AssemblyViewPureChecker {
2019-05-22 11:57:48 +00:00
_inlineAssembly . dialect ( ) ,
2020-05-11 14:53:45 +00:00
[ & ] ( StateMutability _mutability , SourceLocation const & _location ) { reportMutability ( _mutability , _location ) ; }
2017-08-31 11:13:35 +00:00
} ( _inlineAssembly . operations ( ) ) ;
2017-08-28 17:48:34 +00:00
}
2018-07-26 19:45:24 +00:00
void ViewPureChecker : : reportMutability (
StateMutability _mutability ,
SourceLocation const & _location ,
2019-10-28 10:39:30 +00:00
std : : optional < SourceLocation > const & _nestedLocation
2018-07-26 19:45:24 +00:00
)
2017-08-28 17:48:34 +00:00
{
2018-07-26 19:45:24 +00:00
if ( _mutability > m_bestMutabilityAndLocation . mutability )
m_bestMutabilityAndLocation = MutabilityAndLocation { _mutability , _location } ;
if ( ! m_currentFunction | | _mutability < = m_currentFunction - > stateMutability ( ) )
return ;
// Check for payable here, because any occurrence of `msg.value`
// will set mutability to payable.
if ( _mutability = = StateMutability : : View | | (
_mutability = = StateMutability : : Payable & &
m_currentFunction - > stateMutability ( ) = = StateMutability : : Pure
) )
2017-08-28 17:48:34 +00:00
{
2018-07-26 19:45:24 +00:00
m_errorReporter . typeError (
2020-05-05 22:38:28 +00:00
2527 _error ,
2018-07-26 19:45:24 +00:00
_location ,
" Function declared as pure, but this expression (potentially) reads from the "
" environment or state and thus requires \" view \" . "
2017-09-14 15:53:43 +00:00
) ;
2018-05-11 08:40:32 +00:00
m_errors = true ;
2017-08-28 17:48:34 +00:00
}
2018-07-26 19:45:24 +00:00
else if ( _mutability = = StateMutability : : NonPayable )
{
m_errorReporter . typeError (
2020-05-05 22:38:28 +00:00
8961 _error ,
2018-07-26 19:45:24 +00:00
_location ,
" Function declared as " +
stateMutabilityToString ( m_currentFunction - > stateMutability ( ) ) +
" , but this expression (potentially) modifies the state and thus "
" requires non-payable (the default) or payable. "
) ;
m_errors = true ;
}
else if ( _mutability = = StateMutability : : Payable )
{
// We do not warn for library functions because they cannot be payable anyway.
// Also internal functions should be allowed to use `msg.value`.
2020-09-02 18:35:18 +00:00
if ( ( m_currentFunction - > isConstructor ( ) | | m_currentFunction - > isPublic ( ) ) & & ! m_currentFunction - > libraryFunction ( ) )
2018-07-26 19:45:24 +00:00
{
if ( _nestedLocation )
2018-08-16 15:28:49 +00:00
m_errorReporter . typeError (
2020-05-05 22:38:28 +00:00
4006 _error ,
2018-07-26 19:45:24 +00:00
_location ,
2019-03-19 15:51:33 +00:00
SecondarySourceLocation ( ) . append ( " \" msg.value \" or \" callvalue() \" appear here inside the modifier. " , * _nestedLocation ) ,
2020-09-02 18:35:18 +00:00
m_currentFunction - > isConstructor ( ) ?
" This modifier uses \" msg.value \" or \" callvalue() \" and thus the constructor has to be payable. "
: " This modifier uses \" msg.value \" or \" callvalue() \" and thus the function has to be payable or internal. "
2018-07-26 19:45:24 +00:00
) ;
else
2018-08-16 15:28:49 +00:00
m_errorReporter . typeError (
2020-05-05 22:38:28 +00:00
5887 _error ,
2018-07-26 19:45:24 +00:00
_location ,
2020-09-02 18:35:18 +00:00
m_currentFunction - > isConstructor ( ) ?
" \" msg.value \" and \" callvalue() \" can only be used in payable constructors. Make the constructor \" payable \" to avoid this error. "
: " \" msg.value \" and \" callvalue() \" can only be used in payable public functions. Make the function \" payable \" or use an internal function to avoid this error. "
2018-07-26 19:45:24 +00:00
) ;
2018-08-16 15:28:49 +00:00
m_errors = true ;
2018-07-26 19:45:24 +00:00
}
}
else
solAssert ( false , " " ) ;
solAssert (
m_currentFunction - > stateMutability ( ) = = StateMutability : : View | |
m_currentFunction - > stateMutability ( ) = = StateMutability : : Pure | |
m_currentFunction - > stateMutability ( ) = = StateMutability : : NonPayable ,
" "
2020-11-09 16:12:29 +00:00
) ;
2020-08-04 08:38:31 +00:00
}
ViewPureChecker : : MutabilityAndLocation const & ViewPureChecker : : modifierMutability (
ModifierDefinition const & _modifier
)
{
if ( ! m_inferredMutability . count ( & _modifier ) )
{
MutabilityAndLocation bestMutabilityAndLocation { } ;
FunctionDefinition const * currentFunction = nullptr ;
swap ( bestMutabilityAndLocation , m_bestMutabilityAndLocation ) ;
swap ( currentFunction , m_currentFunction ) ;
_modifier . accept ( * this ) ;
swap ( bestMutabilityAndLocation , m_bestMutabilityAndLocation ) ;
swap ( currentFunction , m_currentFunction ) ;
}
return m_inferredMutability . at ( & _modifier ) ;
2017-08-28 17:48:34 +00:00
}
void ViewPureChecker : : endVisit ( FunctionCall const & _functionCall )
{
2020-04-08 17:38:30 +00:00
if ( * _functionCall . annotation ( ) . kind ! = FunctionCallKind : : FunctionCall )
2017-08-28 17:48:34 +00:00
return ;
2018-10-04 11:03:55 +00:00
StateMutability mutability = dynamic_cast < FunctionType const & > ( * _functionCall . expression ( ) . annotation ( ) . type ) . stateMutability ( ) ;
2017-08-28 17:48:34 +00:00
// We only require "nonpayable" to call a payble function.
2018-10-04 11:03:55 +00:00
if ( mutability = = StateMutability : : Payable )
mutability = StateMutability : : NonPayable ;
reportMutability ( mutability , _functionCall . location ( ) ) ;
2017-08-28 17:48:34 +00:00
}
2018-02-13 08:49:50 +00:00
bool ViewPureChecker : : visit ( MemberAccess const & _memberAccess )
{
// Catch the special case of `this.f.selector` which is a pure expression.
ASTString const & member = _memberAccess . memberName ( ) ;
if (
_memberAccess . expression ( ) . annotation ( ) . type - > category ( ) = = Type : : Category : : Function & &
member = = " selector "
)
if ( auto const * expr = dynamic_cast < MemberAccess const * > ( & _memberAccess . expression ( ) ) )
if ( auto const * exprInt = dynamic_cast < Identifier const * > ( & expr - > expression ( ) ) )
if ( exprInt - > name ( ) = = " this " )
// Do not continue visiting.
return false ;
return true ;
}
2017-08-28 17:48:34 +00:00
void ViewPureChecker : : endVisit ( MemberAccess const & _memberAccess )
{
StateMutability mutability = StateMutability : : Pure ;
2020-04-09 10:48:57 +00:00
bool writes = _memberAccess . annotation ( ) . willBeWrittenTo ;
2017-08-28 17:48:34 +00:00
ASTString const & member = _memberAccess . memberName ( ) ;
switch ( _memberAccess . expression ( ) . annotation ( ) . type - > category ( ) )
{
2018-09-03 15:45:58 +00:00
case Type : : Category : : Address :
2020-09-24 16:28:14 +00:00
if ( member = = " balance " | | member = = " code " | | member = = " codehash " )
2017-08-28 17:48:34 +00:00
mutability = StateMutability : : View ;
break ;
case Type : : Category : : Magic :
2017-07-31 23:55:13 +00:00
{
2018-07-26 19:45:24 +00:00
using MagicMember = pair < MagicType : : Kind , string > ;
set < MagicMember > static const pureMembers {
{ MagicType : : Kind : : ABI , " decode " } ,
{ MagicType : : Kind : : ABI , " encode " } ,
{ MagicType : : Kind : : ABI , " encodePacked " } ,
{ MagicType : : Kind : : ABI , " encodeWithSelector " } ,
{ MagicType : : Kind : : ABI , " encodeWithSignature " } ,
{ MagicType : : Kind : : Message , " data " } ,
2019-01-10 15:28:39 +00:00
{ MagicType : : Kind : : Message , " sig " } ,
{ MagicType : : Kind : : MetaType , " creationCode " } ,
2019-01-22 16:15:55 +00:00
{ MagicType : : Kind : : MetaType , " runtimeCode " } ,
{ MagicType : : Kind : : MetaType , " name " } ,
2020-04-08 22:08:49 +00:00
{ MagicType : : Kind : : MetaType , " interfaceId " } ,
2020-05-07 16:24:37 +00:00
{ MagicType : : Kind : : MetaType , " min " } ,
{ MagicType : : Kind : : MetaType , " max " } ,
2017-07-31 23:55:13 +00:00
} ;
2018-07-26 19:45:24 +00:00
set < MagicMember > static const payableMembers {
{ MagicType : : Kind : : Message , " value " }
} ;
auto const & type = dynamic_cast < MagicType const & > ( * _memberAccess . expression ( ) . annotation ( ) . type ) ;
MagicMember magicMember ( type . kind ( ) , member ) ;
if ( ! pureMembers . count ( magicMember ) )
2017-08-28 17:48:34 +00:00
mutability = StateMutability : : View ;
2018-07-26 19:45:24 +00:00
if ( payableMembers . count ( magicMember ) )
mutability = StateMutability : : Payable ;
2017-08-28 17:48:34 +00:00
break ;
2017-07-31 23:55:13 +00:00
}
2017-08-28 17:48:34 +00:00
case Type : : Category : : Struct :
{
if ( _memberAccess . expression ( ) . annotation ( ) . type - > dataStoredIn ( DataLocation : : Storage ) )
mutability = writes ? StateMutability : : NonPayable : StateMutability : : View ;
break ;
}
case Type : : Category : : Array :
{
auto const & type = dynamic_cast < ArrayType const & > ( * _memberAccess . expression ( ) . annotation ( ) . type ) ;
if ( member = = " length " & & type . isDynamicallySized ( ) & & type . dataStoredIn ( DataLocation : : Storage ) )
2019-09-18 12:58:20 +00:00
mutability = StateMutability : : View ;
2017-08-28 17:48:34 +00:00
break ;
}
default :
2019-07-03 09:18:42 +00:00
{
if ( VariableDeclaration const * varDecl = dynamic_cast < VariableDeclaration const * > (
_memberAccess . annotation ( ) . referencedDeclaration
) )
if ( varDecl - > isStateVariable ( ) & & ! varDecl - > isConstant ( ) )
mutability = writes ? StateMutability : : NonPayable : StateMutability : : View ;
2017-08-28 17:48:34 +00:00
break ;
}
2019-07-03 09:18:42 +00:00
}
2017-08-31 11:13:35 +00:00
reportMutability ( mutability , _memberAccess . location ( ) ) ;
2017-08-28 17:48:34 +00:00
}
void ViewPureChecker : : endVisit ( IndexAccess const & _indexAccess )
{
2017-09-01 10:31:24 +00:00
if ( ! _indexAccess . indexExpression ( ) )
solAssert ( _indexAccess . annotation ( ) . type - > category ( ) = = Type : : Category : : TypeType , " " ) ;
else
{
2020-04-09 10:48:57 +00:00
bool writes = _indexAccess . annotation ( ) . willBeWrittenTo ;
2017-09-01 10:31:24 +00:00
if ( _indexAccess . baseExpression ( ) . annotation ( ) . type - > dataStoredIn ( DataLocation : : Storage ) )
reportMutability ( writes ? StateMutability : : NonPayable : StateMutability : : View , _indexAccess . location ( ) ) ;
}
2017-08-28 17:48:34 +00:00
}
2019-09-03 16:30:00 +00:00
void ViewPureChecker : : endVisit ( IndexRangeAccess const & _indexRangeAccess )
{
2020-04-09 10:48:57 +00:00
bool writes = _indexRangeAccess . annotation ( ) . willBeWrittenTo ;
2019-09-03 16:30:00 +00:00
if ( _indexRangeAccess . baseExpression ( ) . annotation ( ) . type - > dataStoredIn ( DataLocation : : Storage ) )
reportMutability ( writes ? StateMutability : : NonPayable : StateMutability : : View , _indexRangeAccess . location ( ) ) ;
}
2017-08-28 17:48:34 +00:00
void ViewPureChecker : : endVisit ( ModifierInvocation const & _modifier )
{
2020-08-11 09:18:22 +00:00
if ( ModifierDefinition const * mod = dynamic_cast < decltype ( mod ) > ( _modifier . name ( ) . annotation ( ) . referencedDeclaration ) )
2017-08-31 14:03:50 +00:00
{
2020-08-04 08:38:31 +00:00
MutabilityAndLocation const & mutAndLocation = modifierMutability ( * mod ) ;
2018-07-26 19:45:24 +00:00
reportMutability ( mutAndLocation . mutability , _modifier . location ( ) , mutAndLocation . location ) ;
2017-08-31 14:03:50 +00:00
}
else
2020-08-11 09:18:22 +00:00
solAssert ( dynamic_cast < ContractDefinition const * > ( _modifier . name ( ) . annotation ( ) . referencedDeclaration ) , " " ) ;
2017-08-28 17:48:34 +00:00
}