diff --git a/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/AssignmentParser.scala b/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/AssignmentParser.scala
index e67bd3db..fe8d602e 100644
--- a/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/AssignmentParser.scala
+++ b/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/AssignmentParser.scala
@@ -5,12 +5,12 @@ import fastparse.NoWhitespace.noWhitespaceImplicit
 import org.alephium.ralph.lsp.access.compiler.message.SourceIndexExtra.range
 import org.alephium.ralph.lsp.access.compiler.parser.soft.ast.{SoftAST, Token}
 
-private object AssignmentParser {
+private case object AssignmentParser {
 
   def parseOrFail[Unknown: P]: P[SoftAST.Assignment] =
     P {
       Index ~
-        IdentifierParser.parseOrFail ~
+        ExpressionParser.parseOrFailSelective(parseInfix = true, parseMethodCall = true, parseAssignment = false) ~
         SpaceParser.parseOrFail.? ~
         TokenParser.parseOrFail(Token.Equal) ~
         SpaceParser.parseOrFail.? ~
@@ -20,11 +20,11 @@ private object AssignmentParser {
       case (from, identifier, postIdentifierSpace, equalToken, postEqualSpace, expression, to) =>
         SoftAST.Assignment(
           index = range(from, to),
-          identifier = identifier,
+          expressionLeft = identifier,
           postIdentifierSpace = postIdentifierSpace,
           equalToken = equalToken,
           postEqualSpace = postEqualSpace,
-          expression = expression
+          expressionRight = expression
         )
     }
 
diff --git a/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/Demo.scala b/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/Demo.scala
index 613b0f93..3ee3f5c0 100644
--- a/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/Demo.scala
+++ b/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/Demo.scala
@@ -33,6 +33,14 @@ object Demo extends App {
         |       // infix assignment
         |       let sum = 1 + 2
         |     }
+        |
+        |     // complex assignment
+        |     object.function(1).value = cache.getValue()
+        |
+        |     // complex equality check
+        |     while(cache.getValue() == objectB.read().value) {
+        |        // do something
+        |     }
         |  }
         |
         |  🚀
diff --git a/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/ExpressionParser.scala b/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/ExpressionParser.scala
index 2f1719dc..a80b7836 100644
--- a/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/ExpressionParser.scala
+++ b/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/ExpressionParser.scala
@@ -26,21 +26,28 @@ private object ExpressionParser {
   def parse[Unknown: P]: P[SoftAST.ExpressionAST] =
     parseSelective(
       parseInfix = true,
-      parseMethodCall = true
+      parseMethodCall = true,
+      parseAssignment = true
     )
 
   def parseOrFail[Unknown: P]: P[SoftAST.ExpressionAST] =
     parseOrFailSelective(
       parseInfix = true,
-      parseMethodCall = true
+      parseMethodCall = true,
+      parseAssignment = true
     )
 
   def parseSelective[Unknown: P](
       parseInfix: Boolean,
-      parseMethodCall: Boolean): P[SoftAST.ExpressionAST] =
+      parseMethodCall: Boolean,
+      parseAssignment: Boolean): P[SoftAST.ExpressionAST] =
     P {
       Index ~
-        parseOrFailSelective(parseInfix = parseInfix, parseMethodCall = parseMethodCall).? ~
+        parseOrFailSelective(
+          parseInfix = parseInfix,
+          parseMethodCall = parseMethodCall,
+          parseAssignment = parseAssignment
+        ).? ~
         Index
     } map {
       case (_, Some(expression), _) =>
@@ -52,7 +59,8 @@ private object ExpressionParser {
 
   def parseOrFailSelective[Unknown: P](
       parseInfix: Boolean,
-      parseMethodCall: Boolean): P[SoftAST.ExpressionAST] = {
+      parseMethodCall: Boolean,
+      parseAssignment: Boolean): P[SoftAST.ExpressionAST] = {
     def infixOrFail() =
       if (parseInfix)
         InfixCallParser.parseOrFail
@@ -65,8 +73,15 @@ private object ExpressionParser {
       else
         Fail(s"${MethodCallParser.productPrefix} ignored")
 
+    def assignmentOrFail() =
+      if (parseAssignment)
+        AssignmentParser.parseOrFail
+      else
+        Fail(s"${AssignmentParser.productPrefix} ignored")
+
     P {
-      infixOrFail() |
+      assignmentOrFail() |
+        infixOrFail() |
         methodCallOrFail() |
         common
     }
@@ -78,7 +93,6 @@ private object ExpressionParser {
         ForLoopParser.parseOrFail |
         WhileLoopParser.parseOrFail |
         VariableDeclarationParser.parseOrFail |
-        AssignmentParser.parseOrFail |
         TypeAssignmentParser.parseOrFail |
         BlockParser.clause(required = false) |
         ReferenceCallParser.parseOrFail |
diff --git a/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/InfixCallParser.scala b/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/InfixCallParser.scala
index 8942fef7..57fdfaaa 100644
--- a/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/InfixCallParser.scala
+++ b/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/InfixCallParser.scala
@@ -10,7 +10,7 @@ case object InfixCallParser {
   def parseOrFail[Unknown: P]: P[SoftAST.InfixExpression] =
     P {
       Index ~
-        ExpressionParser.parseOrFailSelective(parseInfix = false, parseMethodCall = true) ~
+        ExpressionParser.parseOrFailSelective(parseInfix = false, parseMethodCall = true, parseAssignment = false) ~
         SpaceParser.parseOrFail.? ~
         TokenParser.InfixOperatorOrFail ~
         SpaceParser.parseOrFail.? ~
diff --git a/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/MethodCallParser.scala b/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/MethodCallParser.scala
index 0b43fbcf..fd72082e 100644
--- a/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/MethodCallParser.scala
+++ b/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/MethodCallParser.scala
@@ -10,7 +10,7 @@ case object MethodCallParser {
   def parseOrFail[Unknown: P]: P[SoftAST.MethodCall] =
     P {
       Index ~
-        ExpressionParser.parseOrFailSelective(parseInfix = false, parseMethodCall = false) ~
+        ExpressionParser.parseOrFailSelective(parseInfix = false, parseMethodCall = false, parseAssignment = false) ~
         SpaceParser.parseOrFail.? ~
         dotCall.rep(1) ~
         Index
@@ -29,7 +29,7 @@ case object MethodCallParser {
       Index ~
         TokenParser.parseOrFail(Token.Dot) ~
         SpaceParser.parseOrFail.? ~
-        ReferenceCallParser.parse ~
+        (ReferenceCallParser.parseOrFail | IdentifierParser.parse) ~
         Index
     } map {
       case (from, dot, postDotSpace, rightExpression, to) =>
diff --git a/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/ast/SoftAST.scala b/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/ast/SoftAST.scala
index cb371e3e..7e7cb6f7 100644
--- a/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/ast/SoftAST.scala
+++ b/compiler-access/src/main/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/ast/SoftAST.scala
@@ -322,7 +322,7 @@ object SoftAST {
       index: SourceIndex,
       dot: TokenDocumented[Token.Dot.type],
       postDotSpace: Option[Space],
-      rightExpression: ReferenceCall)
+      rightExpression: ReferenceCallOrIdentifier)
     extends SoftAST
 
   case class ReturnStatement(
@@ -368,11 +368,11 @@ object SoftAST {
 
   case class Assignment(
       index: SourceIndex,
-      identifier: Identifier,
+      expressionLeft: ExpressionAST,
       postIdentifierSpace: Option[Space],
       equalToken: TokenDocumented[Token.Equal.type],
       postEqualSpace: Option[Space],
-      expression: ExpressionAST)
+      expressionRight: ExpressionAST)
     extends ExpressionAST
 
   case class TypeAssignment(
diff --git a/compiler-access/src/test/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/AssignmentSpec.scala b/compiler-access/src/test/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/AssignmentSpec.scala
index d36a02a9..a8ee41fc 100644
--- a/compiler-access/src/test/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/AssignmentSpec.scala
+++ b/compiler-access/src/test/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/AssignmentSpec.scala
@@ -25,59 +25,118 @@ import org.scalatest.wordspec.AnyWordSpec
 
 class AssignmentSpec extends AnyWordSpec with Matchers {
 
-  "report ExpressionExpected" when {
-    "a variable is assigned without initialisation" in {
-      val annotation =
-        parseAssignment("variable =")
-
-      annotation shouldBe
-        SoftAST.Assignment(
-          index = indexOf(">>variable =<<"),
-          identifier = Identifier(indexOf(">>variable<<="), "variable"),
-          postIdentifierSpace = Some(SpaceOne(indexOf("variable>> <<="))),
-          equalToken = Equal(indexOf("variable >>=<<")),
-          postEqualSpace = None,
-          expression = SoftAST.ExpressionExpected(indexOf("variable =>><<"))
-        )
+  "assignments to an identifier" should {
+    "report ExpressionExpected" when {
+      "a variable is assigned without initialisation" in {
+        val assignment =
+          parseAssignment("variable =")
+
+        assignment shouldBe
+          SoftAST.Assignment(
+            index = indexOf(">>variable =<<"),
+            expressionLeft = Identifier(indexOf(">>variable<<="), "variable"),
+            postIdentifierSpace = Some(SpaceOne(indexOf("variable>> <<="))),
+            equalToken = Equal(indexOf("variable >>=<<")),
+            postEqualSpace = None,
+            expressionRight = SoftAST.ExpressionExpected(indexOf("variable =>><<"))
+          )
+      }
     }
-  }
 
-  "succeed" when {
-    "full assignment syntax is defined" in {
-      val assigment =
-        parseAssignment("variable = 1")
-
-      assigment shouldBe
-        SoftAST.Assignment(
-          index = indexOf(">>variable = 1<<"),
-          identifier = Identifier(indexOf(">>variable<< = 1"), "variable"),
-          postIdentifierSpace = Some(SpaceOne(indexOf("variable>> <<= 1"))),
-          equalToken = Equal(indexOf("variable >>=<< 1")),
-          postEqualSpace = Some(SpaceOne(indexOf("variable =>> <<1"))),
-          expression = Number(indexOf("variable = >>1<<"), "1")
-        )
+    "succeed" when {
+      "full assignment syntax is defined" in {
+        val assigment =
+          parseAssignment("variable = 1")
+
+        assigment shouldBe
+          SoftAST.Assignment(
+            index = indexOf(">>variable = 1<<"),
+            expressionLeft = Identifier(indexOf(">>variable<< = 1"), "variable"),
+            postIdentifierSpace = Some(SpaceOne(indexOf("variable>> <<= 1"))),
+            equalToken = Equal(indexOf("variable >>=<< 1")),
+            postEqualSpace = Some(SpaceOne(indexOf("variable =>> <<1"))),
+            expressionRight = Number(indexOf("variable = >>1<<"), "1")
+          )
+      }
+
+      "expression is another expression" in {
+        val assigment =
+          parseAssignment("variable = variable + 1")
+
+        assigment shouldBe
+          SoftAST.Assignment(
+            index = indexOf(">>variable = variable + 1<<"),
+            expressionLeft = Identifier(indexOf(">>variable<< = variable + 1"), "variable"),
+            postIdentifierSpace = Some(SpaceOne(indexOf("variable>> <<= variable + 1"))),
+            equalToken = Equal(indexOf("variable >>=<< variable + 1")),
+            postEqualSpace = Some(SpaceOne(indexOf("variable =>> << variable + 1"))),
+            expressionRight = SoftAST.InfixExpression(
+              index = indexOf("variable = >>variable + 1<<"),
+              leftExpression = Identifier(indexOf("variable = >>variable<< + 1"), "variable"),
+              preOperatorSpace = Some(SpaceOne(indexOf("variable = variable>> <<+ 1"))),
+              operator = Plus(indexOf("variable = variable >>+<< 1")),
+              postOperatorSpace = Some(SpaceOne(indexOf("variable = variable +>> <<1"))),
+              rightExpression = Number(indexOf("variable = variable + >>1<<"), "1")
+            )
+          )
+      }
     }
+  }
 
-    "expression is another expression" in {
-      val assigment =
-        parseAssignment("variable = variable + 1")
-
-      assigment shouldBe
-        SoftAST.Assignment(
-          index = indexOf(">>variable = variable + 1<<"),
-          identifier = Identifier(indexOf(">>variable<< = variable + 1"), "variable"),
-          postIdentifierSpace = Some(SpaceOne(indexOf("variable>> <<= variable + 1"))),
-          equalToken = Equal(indexOf("variable >>=<< variable + 1")),
-          postEqualSpace = Some(SpaceOne(indexOf("variable =>> << variable + 1"))),
-          expression = SoftAST.InfixExpression(
-            index = indexOf("variable = >>variable + 1<<"),
-            leftExpression = Identifier(indexOf("variable = >>variable<< + 1"), "variable"),
-            preOperatorSpace = Some(SpaceOne(indexOf("variable = variable>> <<+ 1"))),
-            operator = Plus(indexOf("variable = variable >>+<< 1")),
-            postOperatorSpace = Some(SpaceOne(indexOf("variable = variable +>> <<1"))),
-            rightExpression = Number(indexOf("variable = variable + >>1<<"), "1")
+  "assignments to an expression" should {
+    "succeed" when {
+      "left expression is a method call" in {
+        val assignment =
+          parseAssignment("obj.func(param).counter = 0")
+
+        // left expression is a method call
+        val methodCall = assignment.expressionLeft.asInstanceOf[SoftAST.MethodCall]
+        methodCall.index shouldBe indexOf(">>obj.func(param).counter<< = 0")
+        val objectName = methodCall.leftExpression.asInstanceOf[SoftAST.Identifier]
+        objectName.code.text shouldBe "obj"
+
+        // right expression is a number
+        val number = assignment.expressionRight.asInstanceOf[SoftAST.Number]
+        number shouldBe
+          Number(
+            index = indexOf("obj.func(param).counter = >>0<<"),
+            text = "0"
           )
-        )
+      }
+
+      "left & right expressions both are method call" in {
+        val assignment =
+          parseAssignment("obj.func(param).counter = cache.getNumber()")
+
+        // left expression is a method call
+        val left = assignment.expressionLeft.asInstanceOf[SoftAST.MethodCall]
+        left.index shouldBe indexOf(">>obj.func(param).counter<< = cache.getNumber()")
+        val objectName = left.leftExpression.asInstanceOf[SoftAST.Identifier]
+        objectName.code.text shouldBe "obj"
+
+        // right expression is also a method call
+        val right = assignment.expressionRight.asInstanceOf[SoftAST.MethodCall]
+        right.index shouldBe indexOf("obj.func(param).counter = >>cache.getNumber()<<")
+        val cacheObject = right.leftExpression.asInstanceOf[SoftAST.Identifier]
+        cacheObject.code.text shouldBe "cache"
+      }
+    }
+
+    "report missing expression" when {
+      "left expression is a method call and right expression is missing" in {
+        val assignment =
+          parseAssignment("obj.func(param).counter =")
+
+        // left expression is a method call
+        val methodCall = assignment.expressionLeft.asInstanceOf[SoftAST.MethodCall]
+        methodCall.index shouldBe indexOf(">>obj.func(param).counter<< = 0")
+        val objectName = methodCall.leftExpression.asInstanceOf[SoftAST.Identifier]
+        objectName.code.text shouldBe "obj"
+
+        // right expression is a number
+        assignment.expressionRight shouldBe
+          SoftAST.ExpressionExpected(indexOf("obj.func(param).counter =>><<"))
+      }
     }
   }
 
diff --git a/compiler-access/src/test/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/VariableDeclarationSpec.scala b/compiler-access/src/test/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/VariableDeclarationSpec.scala
index 7572f6f9..1d136b8c 100644
--- a/compiler-access/src/test/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/VariableDeclarationSpec.scala
+++ b/compiler-access/src/test/scala/org/alephium/ralph/lsp/access/compiler/parser/soft/VariableDeclarationSpec.scala
@@ -51,11 +51,11 @@ class VariableDeclarationSpec extends AnyWordSpec with Matchers {
           ),
           assignment = SoftAST.Assignment(
             index = indexOf("let mut >>variable = 1<<"),
-            identifier = Identifier(indexOf("let mut >>variable<< = 1"), "variable"),
+            expressionLeft = Identifier(indexOf("let mut >>variable<< = 1"), "variable"),
             postIdentifierSpace = Some(SpaceOne(indexOf("let mut variable>> <<= 1"))),
             equalToken = Equal(indexOf("let mut variable >>=<< 1")),
             postEqualSpace = Some(SpaceOne(indexOf("let mut variable =>> <<1"))),
-            expression = Number(indexOf("let mut variable = >>1<<"), "1")
+            expressionRight = Number(indexOf("let mut variable = >>1<<"), "1")
           )
         )
     }
@@ -83,11 +83,11 @@ class VariableDeclarationSpec extends AnyWordSpec with Matchers {
           ),
           assignment = SoftAST.Assignment(
             index = indexOf("let mut >>variable = <<"),
-            identifier = Identifier(indexOf("let mut >>variable<< = "), "variable"),
+            expressionLeft = Identifier(indexOf("let mut >>variable<< = "), "variable"),
             postIdentifierSpace = Some(SpaceOne(indexOf("let mut variable>> <<= "))),
             equalToken = Equal(indexOf("let mut variable >>=<< ")),
             postEqualSpace = Some(SpaceOne(indexOf("let mut variable =>> << "))),
-            expression = SoftAST.ExpressionExpected(indexOf("let mut variable = >><<"))
+            expressionRight = SoftAST.ExpressionExpected(indexOf("let mut variable = >><<"))
           )
         )
     }
@@ -105,7 +105,33 @@ class VariableDeclarationSpec extends AnyWordSpec with Matchers {
       val varDec =
         parseVariableDeclaration("let letter = 1")
 
-      varDec.assignment.identifier.code.text shouldBe "letter"
+      varDec.assignment.expressionLeft.asInstanceOf[SoftAST.Identifier].code.text shouldBe "letter"
+    }
+  }
+
+  "allow expressions as assignment identifiers" when {
+    "the identifier is a tuple" in {
+      val tupleDecl =
+        parseVariableDeclaration("let (a, b, c) = blah")
+
+      tupleDecl.modifiers should contain only
+        SoftAST.AssignmentAccessModifier(
+          indexOf(">>let <<(a, b, c) = blah"),
+          Let(indexOf(">>let<< (a, b, c) = blah")),
+          SpaceOne(indexOf("let>> <<(a, b, c) = blah"))
+        )
+
+      // left is a tuple
+      val left = tupleDecl.assignment.expressionLeft.asInstanceOf[SoftAST.Tuple]
+      left.index shouldBe indexOf("let >>(a, b, c)<< = blah")
+      left.toCode() shouldBe "(a, b, c)"
+
+      // right is an assignment
+      tupleDecl.assignment.expressionRight shouldBe
+        Identifier(
+          index = indexOf("let (a, b, c) = >>blah<<"),
+          text = "blah"
+        )
     }
   }