@@ -80,6 +80,13 @@ struct Context {
80
80
if_stmt_single_line : Option < bool > ,
81
81
}
82
82
83
+ impl Context {
84
+ /// Returns true if the current function context is the constructor
85
+ pub ( crate ) fn is_constructor_function ( & self ) -> bool {
86
+ self . function . as_ref ( ) . map_or ( false , |f| matches ! ( f. ty, FunctionTy :: Constructor ) )
87
+ }
88
+ }
89
+
83
90
/// A Solidity formatter
84
91
#[ derive( Debug ) ]
85
92
pub struct Formatter < ' a , W > {
@@ -3047,6 +3054,18 @@ impl<'a, W: Write> Visitor for Formatter<'a, W> {
3047
3054
self . visit_list ( "" , args, None , Some ( loc. end ( ) ) , false ) ?
3048
3055
}
3049
3056
FunctionAttribute :: BaseOrModifier ( loc, base) => {
3057
+ // here we need to find out if this attribute belongs to the constructor because the
3058
+ // modifier need to include the trailing parenthesis
3059
+ // This is very ambiguous because the modifier can either by an inherited contract
3060
+ // or a modifier here: e.g.: This is valid constructor:
3061
+ // `constructor() public Ownable() OnlyOwner {}`
3062
+ let is_constructor = self . context . is_constructor_function ( ) ;
3063
+ // we can't make any decisions here regarding trailing `()` because we'd need to
3064
+ // find out if the `base` is a solidity modifier or an
3065
+ // interface/contract therefor we we its raw content.
3066
+
3067
+ // we can however check if the contract `is` the `base`, this however also does
3068
+ // not cover all cases
3050
3069
let is_contract_base = self . context . contract . as_ref ( ) . map_or ( false , |contract| {
3051
3070
contract. base . iter ( ) . any ( |contract_base| {
3052
3071
contract_base
@@ -3060,6 +3079,20 @@ impl<'a, W: Write> Visitor for Formatter<'a, W> {
3060
3079
3061
3080
if is_contract_base {
3062
3081
base. visit ( self ) ?;
3082
+ } else if is_constructor {
3083
+ // This is ambiguous because the modifier can either by an inherited
3084
+ // contract modifiers with empty parenthesis are
3085
+ // valid, but not required so we make the assumption
3086
+ // here that modifiers are lowercase
3087
+ let mut base_or_modifier =
3088
+ self . visit_to_chunk ( loc. start ( ) , Some ( loc. end ( ) ) , base) ?;
3089
+ let is_lowercase =
3090
+ base_or_modifier. content . chars ( ) . next ( ) . map_or ( false , |c| c. is_lowercase ( ) ) ;
3091
+ if is_lowercase && base_or_modifier. content . ends_with ( "()" ) {
3092
+ base_or_modifier. content . truncate ( base_or_modifier. content . len ( ) - 2 ) ;
3093
+ }
3094
+
3095
+ self . write_chunk ( & base_or_modifier) ?;
3063
3096
} else {
3064
3097
let mut base_or_modifier =
3065
3098
self . visit_to_chunk ( loc. start ( ) , Some ( loc. end ( ) ) , base) ?;
@@ -3110,6 +3143,8 @@ impl<'a, W: Write> Visitor for Formatter<'a, W> {
3110
3143
} ) ?;
3111
3144
3112
3145
if base. args . is_none ( ) || base. args . as_ref ( ) . unwrap ( ) . is_empty ( ) {
3146
+ // This is ambiguous because the modifier can either by an inherited contract or a
3147
+ // modifier
3113
3148
if self . context . function . is_some ( ) {
3114
3149
name. content . push_str ( "()" ) ;
3115
3150
}
0 commit comments