Repository: FHIR/sushi (ts)

Issue:
Ghost issue in data types when adding several extensions makes sushi give up
When we declare an extension that has a valueExpression, if there are other extensions of other types, sushi complains that the other extensions are missing the valueExtension.language. 
The conversion works, and goFSH seems to convert it back ok. If we try the roundtrip conversion everything is well. 
But sushi complains about errors - it seems these are ghost errors.
And Sushi reports the errors upon exit, which blocks the IG build.

To reproduce: 
Try to convert this from fsh to json. Conversion goes ok (and goFSH works which confirms the code is ok), but there are 2 errors:
Removing lines 21 and 22 and/or 23-25 remove the errors, even if those lines are ok.
https://fshschool.org/FSHOnline/#/share/3sU6cfX


Pull request description:
Select correct choice slice when validating properties of Instances
This PR fixes #911.

When we validate the required child elements of instances, we have special logic to handle the case when a child is a choice slice. We find all possible choice slices that the current property could match and then checks each choice slice to see if it is the slice we are currently validating. When we do that, we weren't updating which element we were using to validate cardinality properties. Therefore, when there were two or more choice slices assigned, the wrong child element could potentially be used to compare cardinality requirements.

In the example provided in the issue, the following FSH snippet is present:

```
* item[=].extension[0].url = "http://hl7.org/fhir/StructureDefinition/variable"
* item[=].extension[=].valueExpression.name = "scoreExt"
* item[=].extension[=].valueExpression.language = #text/fhirpath
* item[=].extension[=].valueExpression.expression = "'http://hl7.org/fhir/StructureDefinition/ordinalValue'"
* item[=].extension[+].url = "http://hl7.org/fhir/StructureDefinition/questionnaire-unit"
* item[=].extension[=].valueCoding.display = "{score}"
```

When checking the required properties of the `valueCoding` slice, we were mistakenly using the `valueExpression` element on the structure definition to compare against. We correctly identified that `valueCoding` is the slice we care about, but didn't update the element definition we use. This PR updates the element definition whenever we find the relevant slice. A test was added that uses the above snippet from the issue.

Patch:
diff --git a/src/export/InstanceExporter.ts b/src/export/InstanceExporter.ts
index 523d422..93a3b2f 100644
--- a/src/export/InstanceExporter.ts
+++ b/src/export/InstanceExporter.ts
@@ -144,7 +144,8 @@ export class InstanceExporter implements Fishable {
   ): void {
     // Get only direct children of the element
     const children = element.children(true);
-    children.forEach(child => {
+    children.forEach(c => {
+      let child = c;
       // Get the last part of the path, A.B.C => C
       const childPathEnd = child.path.split('.').slice(-1)[0];
       // Note that in most cases the _ prefixed element will not exist. But if it does exist, we validate
@@ -160,6 +161,8 @@ export class InstanceExporter implements Fishable {
         for (const choiceSlice of choiceSlices) {
           instanceChild = instance[choiceSlice.sliceName];
           if (instanceChild != null) {
+            // Once we find the the choiceSlice that matches, use it as the child
+            child = choiceSlice;
             break;
           }
         }


Test patch:
diff --git a/test/export/InstanceExporter.test.ts b/test/export/InstanceExporter.test.ts
index cb3c2f0..c4eda64 100644
--- a/test/export/InstanceExporter.test.ts
+++ b/test/export/InstanceExporter.test.ts
@@ -1050,6 +1050,91 @@ describe('InstanceExporter', () => {
       });
     });
 
+    it('should assign fixed value[x] correctly and log no errors when multiple choice slices are assigned', () => {
+      // Example from SUSHI 911: https://github.com/FHIR/sushi/issues/911
+
+      // Instance: MyQuestionnaire
+      // InstanceOf: Questionnaire
+      const questionnaireInstance = new Instance('MyQuestionnaire');
+      questionnaireInstance.instanceOf = 'Questionnaire';
+
+      // Add required properties
+      // status = #draft
+      const statusDraft = new AssignmentRule('status');
+      statusDraft.value = new FshCode('draft');
+      questionnaireInstance.rules.push(statusDraft);
+      // * item[+].linkId = "findrisc-score"
+      // * item[=].type = #decimal
+      const itemLinkId = new AssignmentRule('item[+].linkId');
+      itemLinkId.value = 'findrisc-score';
+      questionnaireInstance.rules.push(itemLinkId);
+      const itemType = new AssignmentRule('item[=].type');
+      itemType.value = new FshCode('decimal');
+      questionnaireInstance.rules.push(itemType);
+
+      // Add choiceSlices and ensure values are set and no warnings are logged
+      // * item[=].extension[0].url = "http://example.org"
+      // * item[=].extension[=].valueExpression.name = "scoreExt"
+      // * item[=].extension[=].valueExpression.language = #text/fhirpath
+      // * item[=].extension[=].valueExpression.expression = "'http://hl7.org/fhir/StructureDefinition/ordinalValue'"
+      // * item[=].extension[+].url = "http://example.org"
+      // * item[=].extension[=].valueCoding.display = "{score}"
+      const firstExtensionUrl = new AssignmentRule('item[=].extension[0].url');
+      firstExtensionUrl.value = 'http://example.org';
+      questionnaireInstance.rules.push(firstExtensionUrl);
+      const valueExpressionName = new AssignmentRule('item[=].extension[=].valueExpression.name');
+      valueExpressionName.value = 'scoreExt';
+      questionnaireInstance.rules.push(valueExpressionName);
+      const valueExpressionLanguage = new AssignmentRule(
+        'item[=].extension[=].valueExpression.language'
+      );
+      valueExpressionLanguage.value = new FshCode('#text/fhirpath');
+      questionnaireInstance.rules.push(valueExpressionLanguage);
+      const valueExpressionExpression = new AssignmentRule(
+        'item[=].extension[=].valueExpression.expression'
+      );
+      valueExpressionExpression.value = "'http://hl7.org/fhir/StructureDefinition/ordinalValue'";
+      questionnaireInstance.rules.push(valueExpressionExpression);
+      const secondExtensionUrl = new AssignmentRule('item[=].extension[+].url');
+      secondExtensionUrl.value = 'http://example.org';
+      questionnaireInstance.rules.push(secondExtensionUrl);
+      const valueCodingDisplay = new AssignmentRule('item[=].extension[=].valueCoding.display');
+      valueCodingDisplay.value = '{score}';
+      questionnaireInstance.rules.push(valueCodingDisplay);
+      doc.instances.set(questionnaireInstance.name, questionnaireInstance);
+
+      const exported = exportInstance(questionnaireInstance);
+      // There should be no errors saying that valueExpression.language has min cardinality 1 but occurs 0 times
+      expect(loggerSpy.getAllMessages('error')).toHaveLength(0);
+      expect(exported.toJSON()).toEqual({
+        resourceType: 'Questionnaire',
+        id: 'MyQuestionnaire',
+        status: 'draft',
+        item: [
+          {
+            linkId: 'findrisc-score',
+            type: 'decimal',
+            extension: [
+              {
+                url: 'http://example.org',
+                valueExpression: {
+                  name: 'scoreExt',
+                  language: '#text/fhirpath',
+                  expression: "'http://hl7.org/fhir/StructureDefinition/ordinalValue'"
+                }
+              },
+              {
+                url: 'http://example.org',
+                valueCoding: {
+                  display: '{score}'
+                }
+              }
+            ]
+          }
+        ]
+      });
+    });
+
     it('should assign fixed value[x] correctly even in weird situations (SUSHI #760)', () => {
       // See https://github.com/FHIR/sushi/issues/760
 
diff --git a/test/testhelpers/testdefs/package/StructureDefinition-Expression.json b/test/testhelpers/testdefs/package/StructureDefinition-Expression.json
new file mode 100644
index 0000000..542497c
--- /dev/null
+++ b/test/testhelpers/testdefs/package/StructureDefinition-Expression.json
@@ -0,0 +1,466 @@
+{
+  "resourceType": "StructureDefinition",
+  "id": "Expression",
+  "meta": {
+    "lastUpdated": "2019-11-01T09:29:23.356+11:00"
+  },
+  "extension": [
+    {
+      "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status",
+      "valueCode": "trial-use"
+    }
+  ],
+  "url": "http://hl7.org/fhir/StructureDefinition/Expression",
+  "version": "4.0.1",
+  "name": "Expression",
+  "status": "draft",
+  "date": "2019-11-01T09:29:23+11:00",
+  "publisher": "HL7 FHIR Standard",
+  "contact": [
+    {
+      "telecom": [
+        {
+          "system": "url",
+          "value": "http://hl7.org/fhir"
+        }
+      ]
+    }
+  ],
+  "description": "Base StructureDefinition for Expression Type: A expression that is evaluated in a specified context and returns a value. The context of use of the expression must specify the context in which the expression is evaluated, and how the result of the expression is used.",
+  "fhirVersion": "4.0.1",
+  "mapping": [
+    {
+      "identity": "rim",
+      "uri": "http://hl7.org/v3",
+      "name": "RIM Mapping"
+    }
+  ],
+  "kind": "complex-type",
+  "abstract": false,
+  "type": "Expression",
+  "baseDefinition": "http://hl7.org/fhir/StructureDefinition/Element",
+  "derivation": "specialization",
+  "snapshot": {
+    "element": [
+      {
+        "id": "Expression",
+        "extension": [
+          {
+            "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status",
+            "valueCode": "trial-use"
+          }
+        ],
+        "path": "Expression",
+        "short": "An expression that can be used to generate a value",
+        "definition": "A expression that is evaluated in a specified context and returns a value. The context of use of the expression must specify the context in which the expression is evaluated, and how the result of the expression is used.",
+        "min": 0,
+        "max": "*",
+        "base": {
+          "path": "Expression",
+          "min": 0,
+          "max": "*"
+        },
+        "condition": [
+          "ele-1"
+        ],
+        "constraint": [
+          {
+            "key": "ele-1",
+            "severity": "error",
+            "human": "All FHIR elements must have a @value or children",
+            "expression": "hasValue() or (children().count() > id.count())",
+            "xpath": "@value|f:*|h:div",
+            "source": "http://hl7.org/fhir/StructureDefinition/Element"
+          },
+          {
+            "key": "exp-1",
+            "severity": "error",
+            "human": "An expression or a reference must be provided",
+            "expression": "expression.exists() or reference.exists()",
+            "xpath": "exists(f:expression) or exists(f:reference)"
+          }
+        ],
+        "isModifier": false,
+        "mapping": [
+          {
+            "identity": "rim",
+            "map": "n/a"
+          }
+        ]
+      },
+      {
+        "id": "Expression.id",
+        "path": "Expression.id",
+        "representation": [
+          "xmlAttr"
+        ],
+        "short": "Unique id for inter-element referencing",
+        "definition": "Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.",
+        "min": 0,
+        "max": "1",
+        "base": {
+          "path": "Element.id",
+          "min": 0,
+          "max": "1"
+        },
+        "type": [
+          {
+            "extension": [
+              {
+                "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type",
+                "valueUrl": "string"
+              }
+            ],
+            "code": "http://hl7.org/fhirpath/System.String"
+          }
+        ],
+        "isModifier": false,
+        "isSummary": false,
+        "mapping": [
+          {
+            "identity": "rim",
+            "map": "n/a"
+          }
+        ]
+      },
+      {
+        "id": "Expression.extension",
+        "path": "Expression.extension",
+        "slicing": {
+          "discriminator": [
+            {
+              "type": "value",
+              "path": "url"
+            }
+          ],
+          "description": "Extensions are always sliced by (at least) url",
+          "rules": "open"
+        },
+        "short": "Additional content defined by implementations",
+        "definition": "May be used to represent additional information that is not part of the basic definition of the element. To make the use of extensions safe and manageable, there is a strict set of governance  applied to the definition and use of extensions. Though any implementer can define an extension, there is a set of requirements that SHALL be met as part of the definition of the extension.",
+        "comment": "There can be no stigma associated with the use of extensions by any application, project, or standard - regardless of the institution or jurisdiction that uses or defines the extensions.  The use of extensions is what allows the FHIR specification to retain a core level of simplicity for everyone.",
+        "alias": [
+          "extensions",
+          "user content"
+        ],
+        "min": 0,
+        "max": "*",
+        "base": {
+          "path": "Element.extension",
+          "min": 0,
+          "max": "*"
+        },
+        "type": [
+          {
+            "code": "Extension"
+          }
+        ],
+        "constraint": [
+          {
+            "key": "ele-1",
+            "severity": "error",
+            "human": "All FHIR elements must have a @value or children",
+            "expression": "hasValue() or (children().count() > id.count())",
+            "xpath": "@value|f:*|h:div",
+            "source": "http://hl7.org/fhir/StructureDefinition/Element"
+          },
+          {
+            "key": "ext-1",
+            "severity": "error",
+            "human": "Must have either extensions or value[x], not both",
+            "expression": "extension.exists() != value.exists()",
+            "xpath": "exists(f:extension)!=exists(f:*[starts-with(local-name(.), \"value\")])",
+            "source": "http://hl7.org/fhir/StructureDefinition/Extension"
+          }
+        ],
+        "isModifier": false,
+        "isSummary": false,
+        "mapping": [
+          {
+            "identity": "rim",
+            "map": "n/a"
+          }
+        ]
+      },
+      {
+        "id": "Expression.description",
+        "path": "Expression.description",
+        "short": "Natural language description of the condition",
+        "definition": "A brief, natural language description of the condition that effectively communicates the intended semantics.",
+        "min": 0,
+        "max": "1",
+        "base": {
+          "path": "Expression.description",
+          "min": 0,
+          "max": "1"
+        },
+        "type": [
+          {
+            "code": "string"
+          }
+        ],
+        "constraint": [
+          {
+            "key": "ele-1",
+            "severity": "error",
+            "human": "All FHIR elements must have a @value or children",
+            "expression": "hasValue() or (children().count() > id.count())",
+            "xpath": "@value|f:*|h:div",
+            "source": "http://hl7.org/fhir/StructureDefinition/Element"
+          }
+        ],
+        "isModifier": false,
+        "isSummary": true
+      },
+      {
+        "id": "Expression.name",
+        "path": "Expression.name",
+        "short": "Short name assigned to expression for reuse",
+        "definition": "A short name assigned to the expression to allow for multiple reuse of the expression in the context where it is defined.",
+        "min": 0,
+        "max": "1",
+        "base": {
+          "path": "Expression.name",
+          "min": 0,
+          "max": "1"
+        },
+        "type": [
+          {
+            "code": "id"
+          }
+        ],
+        "constraint": [
+          {
+            "key": "ele-1",
+            "severity": "error",
+            "human": "All FHIR elements must have a @value or children",
+            "expression": "hasValue() or (children().count() > id.count())",
+            "xpath": "@value|f:*|h:div",
+            "source": "http://hl7.org/fhir/StructureDefinition/Element"
+          }
+        ],
+        "isModifier": false,
+        "isSummary": true
+      },
+      {
+        "id": "Expression.language",
+        "path": "Expression.language",
+        "short": "text/cql | text/fhirpath | application/x-fhir-query | etc.",
+        "definition": "The media type of the language for the expression.",
+        "min": 1,
+        "max": "1",
+        "base": {
+          "path": "Expression.language",
+          "min": 1,
+          "max": "1"
+        },
+        "type": [
+          {
+            "code": "code"
+          }
+        ],
+        "constraint": [
+          {
+            "key": "ele-1",
+            "severity": "error",
+            "human": "All FHIR elements must have a @value or children",
+            "expression": "hasValue() or (children().count() > id.count())",
+            "xpath": "@value|f:*|h:div",
+            "source": "http://hl7.org/fhir/StructureDefinition/Element"
+          }
+        ],
+        "isModifier": false,
+        "isSummary": true,
+        "binding": {
+          "extension": [
+            {
+              "url": "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet",
+              "valueCanonical": "http://www.rfc-editor.org/bcp/bcp13.txt"
+            },
+            {
+              "url": "http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName",
+              "valueString": "ExpressionLanguage"
+            }
+          ],
+          "strength": "extensible",
+          "description": "The media type of the expression language.",
+          "valueSet": "http://hl7.org/fhir/ValueSet/expression-language"
+        }
+      },
+      {
+        "id": "Expression.expression",
+        "path": "Expression.expression",
+        "short": "Expression in specified language",
+        "definition": "An expression in the specified language that returns a value.",
+        "min": 0,
+        "max": "1",
+        "base": {
+          "path": "Expression.expression",
+          "min": 0,
+          "max": "1"
+        },
+        "type": [
+          {
+            "code": "string"
+          }
+        ],
+        "constraint": [
+          {
+            "key": "ele-1",
+            "severity": "error",
+            "human": "All FHIR elements must have a @value or children",
+            "expression": "hasValue() or (children().count() > id.count())",
+            "xpath": "@value|f:*|h:div",
+            "source": "http://hl7.org/fhir/StructureDefinition/Element"
+          }
+        ],
+        "isModifier": false,
+        "isSummary": true
+      },
+      {
+        "id": "Expression.reference",
+        "path": "Expression.reference",
+        "short": "Where the expression is found",
+        "definition": "A URI that defines where the expression is found.",
+        "comment": "If both a reference and an expression is found, the reference SHALL point to the same expression.",
+        "min": 0,
+        "max": "1",
+        "base": {
+          "path": "Expression.reference",
+          "min": 0,
+          "max": "1"
+        },
+        "type": [
+          {
+            "code": "uri"
+          }
+        ],
+        "constraint": [
+          {
+            "key": "ele-1",
+            "severity": "error",
+            "human": "All FHIR elements must have a @value or children",
+            "expression": "hasValue() or (children().count() > id.count())",
+            "xpath": "@value|f:*|h:div",
+            "source": "http://hl7.org/fhir/StructureDefinition/Element"
+          }
+        ],
+        "isModifier": false,
+        "isSummary": true
+      }
+    ]
+  },
+  "differential": {
+    "element": [
+      {
+        "id": "Expression",
+        "extension": [
+          {
+            "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status",
+            "valueCode": "trial-use"
+          }
+        ],
+        "path": "Expression",
+        "short": "An expression that can be used to generate a value",
+        "definition": "A expression that is evaluated in a specified context and returns a value. The context of use of the expression must specify the context in which the expression is evaluated, and how the result of the expression is used.",
+        "min": 0,
+        "max": "*",
+        "constraint": [
+          {
+            "key": "exp-1",
+            "severity": "error",
+            "human": "An expression or a reference must be provided",
+            "expression": "expression.exists() or reference.exists()",
+            "xpath": "exists(f:expression) or exists(f:reference)"
+          }
+        ]
+      },
+      {
+        "id": "Expression.description",
+        "path": "Expression.description",
+        "short": "Natural language description of the condition",
+        "definition": "A brief, natural language description of the condition that effectively communicates the intended semantics.",
+        "min": 0,
+        "max": "1",
+        "type": [
+          {
+            "code": "string"
+          }
+        ],
+        "isSummary": true
+      },
+      {
+        "id": "Expression.name",
+        "path": "Expression.name",
+        "short": "Short name assigned to expression for reuse",
+        "definition": "A short name assigned to the expression to allow for multiple reuse of the expression in the context where it is defined.",
+        "min": 0,
+        "max": "1",
+        "type": [
+          {
+            "code": "id"
+          }
+        ],
+        "isSummary": true
+      },
+      {
+        "id": "Expression.language",
+        "path": "Expression.language",
+        "short": "text/cql | text/fhirpath | application/x-fhir-query | etc.",
+        "definition": "The media type of the language for the expression.",
+        "min": 1,
+        "max": "1",
+        "type": [
+          {
+            "code": "code"
+          }
+        ],
+        "isSummary": true,
+        "binding": {
+          "extension": [
+            {
+              "url": "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet",
+              "valueCanonical": "http://www.rfc-editor.org/bcp/bcp13.txt"
+            },
+            {
+              "url": "http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName",
+              "valueString": "ExpressionLanguage"
+            }
+          ],
+          "strength": "extensible",
+          "description": "The media type of the expression language.",
+          "valueSet": "http://hl7.org/fhir/ValueSet/expression-language"
+        }
+      },
+      {
+        "id": "Expression.expression",
+        "path": "Expression.expression",
+        "short": "Expression in specified language",
+        "definition": "An expression in the specified language that returns a value.",
+        "min": 0,
+        "max": "1",
+        "type": [
+          {
+            "code": "string"
+          }
+        ],
+        "isSummary": true
+      },
+      {
+        "id": "Expression.reference",
+        "path": "Expression.reference",
+        "short": "Where the expression is found",
+        "definition": "A URI that defines where the expression is found.",
+        "comment": "If both a reference and an expression is found, the reference SHALL point to the same expression.",
+        "min": 0,
+        "max": "1",
+        "type": [
+          {
+            "code": "uri"
+          }
+        ],
+        "isSummary": true
+      }
+    ]
+  }
+}
\ No newline at end of file