From a6db624aada29e0e11e7e0297d6310413dd1f8f9 Mon Sep 17 00:00:00 2001
From: Xavier Claessens <xavier.claessens@collabora.com>
Date: Fri, 28 Oct 2022 13:38:46 -0400
Subject: [PATCH] Implement `in` operator on string

---
 docs/markdown/snippets/str_in.md            | 11 +++++++++++
 mesonbuild/interpreter/primitives/string.py | 12 ++++++++++++
 test cases/common/16 comparison/meson.build |  3 +++
 3 files changed, 26 insertions(+)
 create mode 100644 docs/markdown/snippets/str_in.md

diff --git a/docs/markdown/snippets/str_in.md b/docs/markdown/snippets/str_in.md
new file mode 100644
index 000000000000..abcb8bdc0c78
--- /dev/null
+++ b/docs/markdown/snippets/str_in.md
@@ -0,0 +1,11 @@
+## `in` operator for strings
+
+`in` and `not in` operators now works on strings, in addition to arrays and
+dictionaries.
+
+```
+fs = import('fs')
+if 'something' in fs.read('somefile')
+  # True
+endif
+```
diff --git a/mesonbuild/interpreter/primitives/string.py b/mesonbuild/interpreter/primitives/string.py
index 0c635a20a6fe..d8133c10be07 100644
--- a/mesonbuild/interpreter/primitives/string.py
+++ b/mesonbuild/interpreter/primitives/string.py
@@ -64,6 +64,8 @@ def __init__(self, obj: str, interpreter: 'Interpreter') -> None:
         self.operators.update({
             MesonOperator.DIV: self.op_div,
             MesonOperator.INDEX: self.op_index,
+            MesonOperator.IN: self.op_in,
+            MesonOperator.NOT_IN: self.op_notin,
         })
 
     def display_name(self) -> str:
@@ -173,6 +175,16 @@ def op_index(self, other: int) -> str:
         except IndexError:
             raise InvalidArguments(f'Index {other} out of bounds of string of size {len(self.held_object)}.')
 
+    @FeatureNew('"in" string operator', '0.64.0')
+    @typed_operator(MesonOperator.IN, str)
+    def op_in(self, other: str) -> bool:
+        return other in self.held_object
+
+    @FeatureNew('"not in" string operator', '0.64.0')
+    @typed_operator(MesonOperator.NOT_IN, str)
+    def op_notin(self, other: str) -> bool:
+        return other not in self.held_object
+
 
 class MesonVersionString(str):
     pass
diff --git a/test cases/common/16 comparison/meson.build b/test cases/common/16 comparison/meson.build
index 1d250f9ca2a5..97cf2cac279a 100644
--- a/test cases/common/16 comparison/meson.build	
+++ b/test cases/common/16 comparison/meson.build	
@@ -141,3 +141,6 @@ assert(exe3 not in [exe1, exe2], ''''exe3 shouldn't be in [exe1, exe2]''')
 
 assert('a' in {'a': 'b'}, '''1 should be in {'a': 'b'}''')
 assert('b' not in {'a': 'b'}, '''1 should be in {'a': 'b'}''')
+
+assert('a' in 'abc')
+assert('b' not in 'def')