| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "tools/gn/operators.h" | 5 #include "tools/gn/operators.h" |
| 6 | 6 |
| 7 #include "base/strings/string_number_conversions.h" | 7 #include "base/strings/string_number_conversions.h" |
| 8 #include "tools/gn/err.h" | 8 #include "tools/gn/err.h" |
| 9 #include "tools/gn/parse_tree.h" | 9 #include "tools/gn/parse_tree.h" |
| 10 #include "tools/gn/scope.h" | 10 #include "tools/gn/scope.h" |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 89 } | 89 } |
| 90 break; | 90 break; |
| 91 | 91 |
| 92 default: | 92 default: |
| 93 break; | 93 break; |
| 94 } | 94 } |
| 95 } | 95 } |
| 96 | 96 |
| 97 // Assignment ----------------------------------------------------------------- | 97 // Assignment ----------------------------------------------------------------- |
| 98 | 98 |
| 99 // We return a null value from this rather than the result of doing the append. |
| 100 // See ValuePlusEquals for rationale. |
| 99 Value ExecuteEquals(Scope* scope, | 101 Value ExecuteEquals(Scope* scope, |
| 100 const BinaryOpNode* op_node, | 102 const BinaryOpNode* op_node, |
| 101 const Token& left, | 103 const Token& left, |
| 102 const Value& right, | 104 const Value& right, |
| 103 Err* err) { | 105 Err* err) { |
| 104 const Value* old_value = scope->GetValue(left.value(), false); | 106 const Value* old_value = scope->GetValue(left.value(), false); |
| 105 if (old_value) { | 107 if (old_value) { |
| 106 if (scope->IsSetButUnused(left.value())) { | 108 // Throw an error when overwriting a nonempty list with another nonempty |
| 107 // Throw an error for re-assigning without using the value first. The | 109 // list item. This is to detect the case where you write |
| 108 // exception is that you can overwrite an empty list with another list | 110 // defines = ["FOO"] |
| 109 // since this is the way to get around the "can't overwrite a nonempty | 111 // and you overwrote inherited ones, when instead you mean to append: |
| 110 // list with another nonempty list" restriction. | 112 // defines += ["FOO"] |
| 111 if (old_value->type() != Value::LIST || | 113 if (old_value->type() == Value::LIST && |
| 112 !old_value->list_value().empty()) { | 114 !old_value->list_value().empty() && |
| 113 *err = Err(op_node->left()->GetRange(), "Overwriting unused variable.", | 115 right.type() == Value::LIST && |
| 114 "This overwrites a previous assignment to \"" + | 116 !right.list_value().empty()) { |
| 115 left.value().as_string() + "\" that had no effect."); | 117 *err = Err(op_node->left()->GetRange(), "Replacing nonempty list.", |
| 116 err->AppendSubErr(Err(*scope->GetValue(left.value()), | 118 std::string("This overwrites a previously-defined nonempty list ") + |
| 117 "Previously set here.", | 119 "(length " + |
| 118 "Maybe you wanted \"+=\" to append instead?")); | 120 base::IntToString(static_cast<int>(old_value->list_value().size())) |
| 119 return Value(); | 121 + ")."); |
| 120 } | 122 err->AppendSubErr(Err(*old_value, "for previous definition", |
| 121 } else { | 123 "with another one (length " + |
| 122 // Throw an error when overwriting a nonempty list with another nonempty | 124 base::IntToString(static_cast<int>(right.list_value().size())) + |
| 123 // list item. This is to detect the case where you write | 125 "). Did you mean " + |
| 124 // defines = ["FOO"] | 126 "\"+=\" to append instead? If you\nreally want to do this, do\n " + |
| 125 // and you overwrote inherited ones, when instead you mean to append: | 127 left.value().as_string() + " = []\nbefore reassigning.")); |
| 126 // defines += ["FOO"] | 128 return Value(); |
| 127 if (old_value->type() == Value::LIST && | |
| 128 !old_value->list_value().empty() && | |
| 129 right.type() == Value::LIST && | |
| 130 !right.list_value().empty()) { | |
| 131 *err = Err(op_node->left()->GetRange(), "Replacing nonempty list.", | |
| 132 std::string("This overwrites a previously-defined nonempty list ") + | |
| 133 "(length " + | |
| 134 base::IntToString(static_cast<int>(old_value->list_value().size())) | |
| 135 + ")."); | |
| 136 err->AppendSubErr(Err(*old_value, "for previous definition", | |
| 137 "with another one (length " + | |
| 138 base::IntToString(static_cast<int>(right.list_value().size())) + | |
| 139 "). Did you mean " + | |
| 140 "\"+=\" to append instead? If you\nreally want to do this, do\n " + | |
| 141 left.value().as_string() + " = []\nbefore reassigning.")); | |
| 142 return Value(); | |
| 143 } | |
| 144 } | 129 } |
| 145 } | 130 } |
| 146 if (err->has_error()) | 131 if (err->has_error()) |
| 147 return Value(); | 132 return Value(); |
| 148 | 133 |
| 149 if (right.type() == Value::LIST && left.value() == kSourcesName) { | 134 if (right.type() == Value::LIST && left.value() == kSourcesName) { |
| 150 // Assigning to sources, filter the list. Here we do the filtering and | 135 // Assigning to sources, filter the list. Here we do the filtering and |
| 151 // copying in one step to save an extra list copy (the lists may be | 136 // copying in one step to save an extra list copy (the lists may be |
| 152 // long). | 137 // long). |
| 153 Value* set_value = scope->SetValue(left.value(), | 138 Value* set_value = scope->SetValue(left.value(), |
| 154 Value(op_node, Value::LIST), op_node); | 139 Value(op_node, Value::LIST), op_node); |
| 155 set_value->list_value().reserve(right.list_value().size()); | 140 set_value->list_value().reserve(right.list_value().size()); |
| 156 AppendFilteredSourcesToValue(scope, right, set_value); | 141 AppendFilteredSourcesToValue(scope, right, set_value); |
| 157 } else { | 142 } else { |
| 158 // Normal value set, just copy it. | 143 // Normal value set, just copy it. |
| 159 scope->SetValue(left.value(), right, op_node->right()); | 144 scope->SetValue(left.value(), right, op_node->right()); |
| 160 } | 145 } |
| 161 return Value(); | 146 return Value(); |
| 162 } | 147 } |
| 163 | 148 |
| 164 // allow_type_conversion indicates if we're allowed to change the type of the | 149 // allow_type_conversion indicates if we're allowed to change the type of the |
| 165 // left value. This is set to true when doing +, and false when doing +=. | 150 // left value. This is set to true when doing +, and false when doing +=. |
| 151 // |
| 152 // Note that we return Value() from here, which is different than C++. This |
| 153 // means you can't do clever things like foo = [ bar += baz ] to simultaneously |
| 154 // append to and use a value. This is basically never needed in out build |
| 155 // scripts and is just as likely an error as the intended behavior, and it also |
| 156 // involves a copy of the value when it's returned. Many times we're appending |
| 157 // to large lists, and copying the value to discard it for the next statement |
| 158 // is very wasteful. |
| 166 void ValuePlusEquals(const Scope* scope, | 159 void ValuePlusEquals(const Scope* scope, |
| 167 const BinaryOpNode* op_node, | 160 const BinaryOpNode* op_node, |
| 168 const Token& left_token, | 161 const Token& left_token, |
| 169 Value* left, | 162 Value* left, |
| 170 const Value& right, | 163 const Value& right, |
| 171 bool allow_type_conversion, | 164 bool allow_type_conversion, |
| 172 Err* err) { | 165 Err* err) { |
| 173 switch (left->type()) { | 166 switch (left->type()) { |
| 174 // Left-hand-side int. | 167 // Left-hand-side int. |
| 175 case Value::INTEGER: | 168 case Value::INTEGER: |
| (...skipping 27 matching lines...) Expand all Loading... |
| 203 return; | 196 return; |
| 204 | 197 |
| 205 default: | 198 default: |
| 206 break; | 199 break; |
| 207 } | 200 } |
| 208 break; | 201 break; |
| 209 | 202 |
| 210 // Left-hand-side list. | 203 // Left-hand-side list. |
| 211 case Value::LIST: | 204 case Value::LIST: |
| 212 switch (right.type()) { | 205 switch (right.type()) { |
| 213 case Value::INTEGER: // list + integer -> list append. | |
| 214 case Value::STRING: // list + string -> list append. | |
| 215 if (left_token.value() == kSourcesName) | |
| 216 AppendFilteredSourcesToValue(scope, right, left); | |
| 217 else | |
| 218 left->list_value().push_back(right); | |
| 219 return; | |
| 220 | |
| 221 case Value::LIST: // list + list -> list concat. | 206 case Value::LIST: // list + list -> list concat. |
| 222 if (left_token.value() == kSourcesName) { | 207 if (left_token.value() == kSourcesName) { |
| 223 // Filter additions through the assignment filter. | 208 // Filter additions through the assignment filter. |
| 224 AppendFilteredSourcesToValue(scope, right, left); | 209 AppendFilteredSourcesToValue(scope, right, left); |
| 225 } else { | 210 } else { |
| 226 // Normal list concat. | 211 // Normal list concat. |
| 227 for (size_t i = 0; i < right.list_value().size(); i++) | 212 for (size_t i = 0; i < right.list_value().size(); i++) |
| 228 left->list_value().push_back(right.list_value()[i]); | 213 left->list_value().push_back(right.list_value()[i]); |
| 229 } | 214 } |
| 230 return; | 215 return; |
| 231 | 216 |
| 232 default: | 217 default: |
| 233 break; | 218 *err = Err(op_node->op(), "Incompatible types to add.", |
| 219 "To append a single item to a list do \"foo += [ bar ]\"."); |
| 220 return; |
| 234 } | 221 } |
| 235 | 222 |
| 236 default: | 223 default: |
| 237 break; | 224 break; |
| 238 } | 225 } |
| 239 | 226 |
| 240 *err = Err(op_node->op(), "Incompatible types to add.", | 227 *err = Err(op_node->op(), "Incompatible types to add.", |
| 241 std::string("I see a ") + Value::DescribeType(left->type()) + " and a " + | 228 std::string("I see a ") + Value::DescribeType(left->type()) + " and a " + |
| 242 Value::DescribeType(right.type()) + "."); | 229 Value::DescribeType(right.type()) + "."); |
| 243 } | 230 } |
| (...skipping 11 matching lines...) Expand all Loading... |
| 255 *err = Err(left, "Undefined variable for +=.", | 242 *err = Err(left, "Undefined variable for +=.", |
| 256 "I don't have something with this name in scope now."); | 243 "I don't have something with this name in scope now."); |
| 257 return Value(); | 244 return Value(); |
| 258 } | 245 } |
| 259 ValuePlusEquals(scope, op_node, left, left_value, right, false, err); | 246 ValuePlusEquals(scope, op_node, left, left_value, right, false, err); |
| 260 left_value->set_origin(op_node); | 247 left_value->set_origin(op_node); |
| 261 scope->MarkUnused(left.value()); | 248 scope->MarkUnused(left.value()); |
| 262 return Value(); | 249 return Value(); |
| 263 } | 250 } |
| 264 | 251 |
| 252 // We return a null value from this rather than the result of doing the append. |
| 253 // See ValuePlusEquals for rationale. |
| 265 void ValueMinusEquals(const BinaryOpNode* op_node, | 254 void ValueMinusEquals(const BinaryOpNode* op_node, |
| 266 Value* left, | 255 Value* left, |
| 267 const Value& right, | 256 const Value& right, |
| 268 bool allow_type_conversion, | 257 bool allow_type_conversion, |
| 269 Err* err) { | 258 Err* err) { |
| 270 switch (left->type()) { | 259 switch (left->type()) { |
| 271 // Left-hand-side int. | 260 // Left-hand-side int. |
| 272 case Value::INTEGER: | 261 case Value::INTEGER: |
| 273 switch (right.type()) { | 262 switch (right.type()) { |
| 274 case Value::INTEGER: // int - int -> subtraction. | 263 case Value::INTEGER: // int - int -> subtraction. |
| 275 left->int_value() -= right.int_value(); | 264 left->int_value() -= right.int_value(); |
| 276 return; | 265 return; |
| 277 | 266 |
| 278 default: | 267 default: |
| 279 break; | 268 break; |
| 280 } | 269 } |
| 281 break; | 270 break; |
| 282 | 271 |
| 283 // Left-hand-side string. | 272 // Left-hand-side string. |
| 284 case Value::STRING: | 273 case Value::STRING: |
| 285 break; // All are errors. | 274 break; // All are errors. |
| 286 | 275 |
| 287 // Left-hand-side list. | 276 // Left-hand-side list. |
| 288 case Value::LIST: | 277 case Value::LIST: |
| 289 RemoveMatchesFromList(op_node, left, right, err); | 278 if (right.type() != Value::LIST) { |
| 279 *err = Err(op_node->op(), "Incompatible types to subtract.", |
| 280 "To remove a single item from a list do \"foo -= [ bar ]\"."); |
| 281 } else { |
| 282 RemoveMatchesFromList(op_node, left, right, err); |
| 283 } |
| 290 return; | 284 return; |
| 291 | 285 |
| 292 default: | 286 default: |
| 293 break; | 287 break; |
| 294 } | 288 } |
| 295 | 289 |
| 296 *err = Err(op_node->op(), "Incompatible types to subtract.", | 290 *err = Err(op_node->op(), "Incompatible types to subtract.", |
| 297 std::string("I see a ") + Value::DescribeType(left->type()) + " and a " + | 291 std::string("I see a ") + Value::DescribeType(left->type()) + " and a " + |
| 298 Value::DescribeType(right.type()) + "."); | 292 Value::DescribeType(right.type()) + "."); |
| 299 } | 293 } |
| (...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 585 return ExecuteLess(scope, op_node, left_value, right_value, err); | 579 return ExecuteLess(scope, op_node, left_value, right_value, err); |
| 586 | 580 |
| 587 // ||, &&. | 581 // ||, &&. |
| 588 if (op.type() == Token::BOOLEAN_OR) | 582 if (op.type() == Token::BOOLEAN_OR) |
| 589 return ExecuteOr(scope, op_node, left_value, right_value, err); | 583 return ExecuteOr(scope, op_node, left_value, right_value, err); |
| 590 if (op.type() == Token::BOOLEAN_AND) | 584 if (op.type() == Token::BOOLEAN_AND) |
| 591 return ExecuteAnd(scope, op_node, left_value, right_value, err); | 585 return ExecuteAnd(scope, op_node, left_value, right_value, err); |
| 592 | 586 |
| 593 return Value(); | 587 return Value(); |
| 594 } | 588 } |
| OLD | NEW |