@@ -75,9 +75,12 @@ pub struct VisitorContext<'s> {
75
75
/// e.g. true for `a.b.c` if either a,b or c is declared in a constant block
76
76
constant : bool ,
77
77
78
- /// true the visitor entered a body (so no declarations)
78
+ /// true if the visitor entered a body (so no declarations)
79
79
in_body : bool ,
80
80
81
+ /// true if the visitor entered a control statement
82
+ in_control : bool ,
83
+
81
84
pub id_provider : IdProvider ,
82
85
83
86
// what's the current strategy for resolving
@@ -87,80 +90,54 @@ pub struct VisitorContext<'s> {
87
90
impl < ' s > VisitorContext < ' s > {
88
91
/// returns a copy of the current context and changes the `current_qualifier` to the given qualifier
89
92
fn with_qualifier ( & self , qualifier : String ) -> VisitorContext < ' s > {
90
- VisitorContext {
91
- pou : self . pou ,
92
- qualifier : Some ( qualifier) ,
93
- lhs : self . lhs ,
94
- constant : false ,
95
- in_body : self . in_body ,
96
- id_provider : self . id_provider . clone ( ) ,
97
- resolve_strategy : self . resolve_strategy . clone ( ) ,
98
- }
93
+ let mut ctx = self . clone ( ) ;
94
+ ctx. qualifier = Some ( qualifier) ;
95
+ ctx. constant = false ;
96
+ ctx
99
97
}
100
98
101
99
/// returns a copy of the current context and changes the `current_pou` to the given pou
102
100
fn with_pou ( & self , pou : & ' s str ) -> VisitorContext < ' s > {
103
- VisitorContext {
104
- pou : Some ( pou) ,
105
- qualifier : self . qualifier . clone ( ) ,
106
- lhs : self . lhs ,
107
- constant : false ,
108
- in_body : self . in_body ,
109
- id_provider : self . id_provider . clone ( ) ,
110
- resolve_strategy : self . resolve_strategy . clone ( ) ,
111
- }
101
+ let mut ctx = self . clone ( ) ;
102
+ ctx. pou = Some ( pou) ;
103
+ ctx. constant = false ;
104
+ ctx
112
105
}
113
106
114
107
/// returns a copy of the current context and changes the `lhs_pou` to the given pou
115
108
fn with_lhs ( & self , lhs_pou : & ' s str ) -> VisitorContext < ' s > {
116
- VisitorContext {
117
- pou : self . pou ,
118
- qualifier : self . qualifier . clone ( ) ,
119
- lhs : Some ( lhs_pou) ,
120
- constant : false ,
121
- in_body : self . in_body ,
122
- id_provider : self . id_provider . clone ( ) ,
123
- resolve_strategy : self . resolve_strategy . clone ( ) ,
124
- }
109
+ let mut ctx = self . clone ( ) ;
110
+ ctx. lhs = Some ( lhs_pou) ;
111
+ ctx. constant = false ;
112
+ ctx
125
113
}
126
114
127
115
/// returns a copy of the current context and changes the `is_call` to true
128
116
fn with_const ( & self , const_state : bool ) -> VisitorContext < ' s > {
129
- VisitorContext {
130
- pou : self . pou ,
131
- qualifier : self . qualifier . clone ( ) ,
132
- lhs : self . lhs ,
133
- constant : const_state,
134
- in_body : self . in_body ,
135
- id_provider : self . id_provider . clone ( ) ,
136
- resolve_strategy : self . resolve_strategy . clone ( ) ,
137
- }
117
+ let mut ctx = self . clone ( ) ;
118
+ ctx. constant = const_state;
119
+ ctx
138
120
}
139
121
140
122
// returns a copy of the current context and sets the in_body field to true
141
123
fn enter_body ( & self ) -> Self {
142
- VisitorContext {
143
- pou : self . pou ,
144
- qualifier : self . qualifier . clone ( ) ,
145
- lhs : self . lhs ,
146
- constant : self . constant ,
147
- in_body : true ,
148
- id_provider : self . id_provider . clone ( ) ,
149
- resolve_strategy : self . resolve_strategy . clone ( ) ,
150
- }
124
+ let mut ctx = self . clone ( ) ;
125
+ ctx . in_body = true ;
126
+ ctx
127
+ }
128
+
129
+ fn enter_control ( & self ) -> Self {
130
+ let mut ctx = self . clone ( ) ;
131
+ ctx . in_control = true ;
132
+ ctx
151
133
}
152
134
153
135
// returns a copy of the current context and sets the resolve_strategy field to the given strategies
154
136
fn with_resolving_strategy ( & self , resolve_strategy : Vec < ResolvingScope > ) -> Self {
155
- VisitorContext {
156
- pou : self . pou ,
157
- qualifier : self . qualifier . clone ( ) ,
158
- lhs : self . lhs ,
159
- constant : self . constant ,
160
- in_body : true ,
161
- id_provider : self . id_provider . clone ( ) ,
162
- resolve_strategy,
163
- }
137
+ let mut ctx = self . clone ( ) ;
138
+ ctx. in_body = true ;
139
+ ctx. resolve_strategy = resolve_strategy;
140
+ ctx
164
141
}
165
142
166
143
fn is_in_a_body ( & self ) -> bool {
@@ -762,6 +739,7 @@ impl<'i> TypeAnnotator<'i> {
762
739
in_body : false ,
763
740
id_provider,
764
741
resolve_strategy : ResolvingScope :: default_scopes ( ) ,
742
+ in_control : false ,
765
743
} ;
766
744
767
745
for global_variable in unit. global_vars . iter ( ) . flat_map ( |it| it. variables . iter ( ) ) {
@@ -1244,54 +1222,56 @@ impl<'i> TypeAnnotator<'i> {
1244
1222
self . visit_statement ( ctx, expr) ;
1245
1223
self . inherit_annotations ( statement, expr) ;
1246
1224
}
1247
- AstStatement :: ControlStatement ( AstControlStatement :: If ( stmt) , ..) => {
1248
- stmt. blocks . iter ( ) . for_each ( |b| {
1249
- self . visit_statement ( ctx, b. condition . as_ref ( ) ) ;
1250
- b. body . iter ( ) . for_each ( |s| self . visit_statement ( ctx, s) ) ;
1251
- } ) ;
1252
- stmt. else_block . iter ( ) . for_each ( |e| self . visit_statement ( ctx, e) ) ;
1253
- }
1254
- AstStatement :: ControlStatement ( AstControlStatement :: ForLoop ( stmt) , ..) => {
1255
- visit_all_statements ! ( self , ctx, & stmt. counter, & stmt. start, & stmt. end) ;
1256
- if let Some ( by_step) = & stmt. by_step {
1257
- self . visit_statement ( ctx, by_step) ;
1258
- }
1259
- //Hint annotate start, end and step with the counter's real type
1260
- if let Some ( type_name) = self
1261
- . annotation_map
1262
- . get_type ( & stmt. counter , self . index )
1263
- . map ( typesystem:: DataType :: get_name)
1264
- {
1265
- let annotation = StatementAnnotation :: value ( type_name) ;
1266
- self . annotation_map . annotate_type_hint ( & stmt. start , annotation. clone ( ) ) ;
1267
- self . annotation_map . annotate_type_hint ( & stmt. end , annotation. clone ( ) ) ;
1268
- if let Some ( by_step) = & stmt. by_step {
1269
- self . annotation_map . annotate_type_hint ( by_step, annotation) ;
1225
+ AstStatement :: ControlStatement ( control) => {
1226
+ match control {
1227
+ AstControlStatement :: If ( stmt) => {
1228
+ stmt. blocks . iter ( ) . for_each ( |b| {
1229
+ self . visit_statement ( & ctx. enter_control ( ) , b. condition . as_ref ( ) ) ;
1230
+ b. body . iter ( ) . for_each ( |s| self . visit_statement ( ctx, s) ) ;
1231
+ } ) ;
1232
+ stmt. else_block . iter ( ) . for_each ( |e| self . visit_statement ( ctx, e) ) ;
1270
1233
}
1271
- }
1272
- stmt. body . iter ( ) . for_each ( |s| self . visit_statement ( ctx, s) ) ;
1273
- }
1274
- AstStatement :: ControlStatement ( AstControlStatement :: WhileLoop ( stmt) , ..)
1275
- | AstStatement :: ControlStatement ( AstControlStatement :: RepeatLoop ( stmt) , ..) => {
1276
- self . visit_statement ( ctx, & stmt. condition ) ;
1277
- stmt. body . iter ( ) . for_each ( |s| self . visit_statement ( ctx, s) ) ;
1278
- }
1279
- AstStatement :: ControlStatement ( AstControlStatement :: Case ( stmt) , ..) => {
1280
- self . visit_statement ( ctx, & stmt. selector ) ;
1281
- let selector_type = self . annotation_map . get_type ( & stmt. selector , self . index ) . cloned ( ) ;
1282
- stmt. case_blocks . iter ( ) . for_each ( |b| {
1283
- self . visit_statement ( ctx, b. condition . as_ref ( ) ) ;
1284
- if let Some ( selector_type) = & selector_type {
1285
- self . update_expected_types ( selector_type, b. condition . as_ref ( ) ) ;
1234
+ AstControlStatement :: ForLoop ( stmt) => {
1235
+ visit_all_statements ! ( self , ctx, & stmt. counter, & stmt. start, & stmt. end) ;
1236
+ if let Some ( by_step) = & stmt. by_step {
1237
+ self . visit_statement ( ctx, by_step) ;
1238
+ }
1239
+ //Hint annotate start, end and step with the counter's real type
1240
+ if let Some ( type_name) = self
1241
+ . annotation_map
1242
+ . get_type ( & stmt. counter , self . index )
1243
+ . map ( typesystem:: DataType :: get_name)
1244
+ {
1245
+ let annotation = StatementAnnotation :: value ( type_name) ;
1246
+ self . annotation_map . annotate_type_hint ( & stmt. start , annotation. clone ( ) ) ;
1247
+ self . annotation_map . annotate_type_hint ( & stmt. end , annotation. clone ( ) ) ;
1248
+ if let Some ( by_step) = & stmt. by_step {
1249
+ self . annotation_map . annotate_type_hint ( by_step, annotation) ;
1250
+ }
1251
+ }
1252
+ stmt. body . iter ( ) . for_each ( |s| self . visit_statement ( ctx, s) ) ;
1253
+ }
1254
+ AstControlStatement :: WhileLoop ( stmt) | AstControlStatement :: RepeatLoop ( stmt) => {
1255
+ self . visit_statement ( & ctx. enter_control ( ) , & stmt. condition ) ;
1256
+ stmt. body . iter ( ) . for_each ( |s| self . visit_statement ( ctx, s) ) ;
1257
+ }
1258
+ AstControlStatement :: Case ( stmt) => {
1259
+ self . visit_statement ( ctx, & stmt. selector ) ;
1260
+ let selector_type = self . annotation_map . get_type ( & stmt. selector , self . index ) . cloned ( ) ;
1261
+ stmt. case_blocks . iter ( ) . for_each ( |b| {
1262
+ self . visit_statement ( ctx, b. condition . as_ref ( ) ) ;
1263
+ if let Some ( selector_type) = & selector_type {
1264
+ self . update_expected_types ( selector_type, b. condition . as_ref ( ) ) ;
1265
+ }
1266
+ b. body . iter ( ) . for_each ( |s| self . visit_statement ( ctx, s) ) ;
1267
+ } ) ;
1268
+ stmt. else_block . iter ( ) . for_each ( |s| self . visit_statement ( ctx, s) ) ;
1286
1269
}
1287
- b. body . iter ( ) . for_each ( |s| self . visit_statement ( ctx, s) ) ;
1288
- } ) ;
1289
- stmt. else_block . iter ( ) . for_each ( |s| self . visit_statement ( ctx, s) ) ;
1270
+ }
1290
1271
}
1272
+
1291
1273
AstStatement :: CaseCondition ( condition, ..) => self . visit_statement ( ctx, condition) ,
1292
- _ => {
1293
- self . visit_statement_expression ( ctx, statement) ;
1294
- }
1274
+ _ => self . visit_statement_expression ( ctx, statement) ,
1295
1275
}
1296
1276
}
1297
1277
@@ -1399,7 +1379,15 @@ impl<'i> TypeAnnotator<'i> {
1399
1379
} ;
1400
1380
1401
1381
if let Some ( statement_type) = statement_type {
1402
- self . annotate ( statement, StatementAnnotation :: value ( statement_type) ) ;
1382
+ self . annotate ( statement, StatementAnnotation :: value ( statement_type. clone ( ) ) ) ;
1383
+
1384
+ // https://github.com/PLC-lang/rusty/issues/939: We rely on type-hints in order
1385
+ // to identify `=` operations that have no effect (e.g. `foo = bar;`) hence
1386
+ // type-hint the conditions of control statements to eliminate false-positives.
1387
+ if ctx. in_control {
1388
+ self . annotation_map
1389
+ . annotate_type_hint ( statement, StatementAnnotation :: value ( statement_type) )
1390
+ }
1403
1391
}
1404
1392
}
1405
1393
AstStatement :: UnaryExpression ( data, ..) => {
@@ -1435,7 +1423,7 @@ impl<'i> TypeAnnotator<'i> {
1435
1423
visit_all_statements ! ( self , ctx, & data. start, & data. end) ;
1436
1424
}
1437
1425
AstStatement :: Assignment ( data, ..) => {
1438
- self . visit_statement ( ctx, & data. right ) ;
1426
+ self . visit_statement ( & ctx. enter_control ( ) , & data. right ) ;
1439
1427
if let Some ( lhs) = ctx. lhs {
1440
1428
//special context for left hand side
1441
1429
self . visit_statement ( & ctx. with_pou ( lhs) . with_lhs ( lhs) , & data. left ) ;
0 commit comments