Skip to content

Commit dac543c

Browse files
committed
Merge branch '1024-fix-unit-normalization-for-quantity-units-such-as-ucum-and-calendar-date-units' of https://github.com/FirelyTeam/firely-cql-sdk into 1024-fix-unit-normalization-for-quantity-units-such-as-ucum-and-calendar-date-units
2 parents 6ca7360 + c31459c commit dac543c

10 files changed

+12819
-15
lines changed

Cql/Cql.Compiler/Preprocessing/MissingResultTypeSpecifierCorrector.cs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
using Hl7.Cql.Elm;
10+
using Tuple = Hl7.Cql.Elm.Tuple;
1011

1112
namespace Hl7.Cql.Compiler.Preprocessing;
1213

@@ -18,17 +19,73 @@ internal class MissingResultTypeSpecifierCorrector(ILogger<MissingResultTypeSpec
1819
{
1920
private readonly ElmTreeWalker _walker = ElmTreeWalker.Create((self, node) =>
2021
{
21-
switch (node)
22+
// DO NOT turn this into a switch expression.
23+
// It is possible to match multiple patterns in one node, e.g.
24+
// Element with resultTypeName then Query with ListTypeSpecifier
25+
// The order of these blocks matters too, starting with the most general ones first.
26+
2227
{
23-
case Element { resultTypeSpecifier: null, resultTypeName: {} resultTypeName } element:
28+
// If an Element has a resultTypeName but no resultTypeSpecifier, set resultTypeSpecifier to a NamedTypeSpecifier with the name from resultTypeName
29+
if (node is Element { resultTypeSpecifier: null, resultTypeName: { } resultTypeName } element)
30+
{
2431
logger.LogDebug(
2532
"Setting missing resultTypeSpecifier to resultTypeName on {elementType} to '{resultType}' @ {locator}.\n{expressionKey}",
2633
element.GetType().ToString(),
2734
resultTypeName,
2835
element.locator,
2936
self.ContextStackString);
3037
element.resultTypeSpecifier = new NamedTypeSpecifier { name = resultTypeName };
31-
break;
38+
}
39+
}
40+
41+
{
42+
// If a Query has a ListTypeSpecifier as resultTypeSpecifier but its ReturnClause has no resultTypeSpecifier, set the ReturnClause's resultTypeSpecifier to the elementType of the ListTypeSpecifier
43+
if (node is Query
44+
{
45+
resultTypeSpecifier: ListTypeSpecifier { elementType: { } elementType },
46+
@return: { resultTypeSpecifier: null } returnClause,
47+
} query)
48+
{
49+
logger.LogDebug(
50+
"Setting missing ReturnClause.resultTypeSpecifier to ListTypeSpecifier.elementType to '{resultType}' on Query @ {locator}.\n{expressionKey}",
51+
elementType,
52+
query.locator,
53+
self.ContextStackString);
54+
returnClause.resultTypeSpecifier = elementType;
55+
}
56+
}
57+
58+
{
59+
// If a ReturnClause has a TupleTypeSpecifier as resultTypeSpecifier but its Tuple has no resultTypeSpecifier, set the Tuple's resultTypeSpecifier to the ReturnClause's TupleTypeSpecifier
60+
if (node is ReturnClause
61+
{
62+
resultTypeSpecifier: TupleTypeSpecifier tupleTypeSpecifier,
63+
expression: Tuple { resultTypeSpecifier: null } tuple,
64+
} clause)
65+
{
66+
logger.LogDebug(
67+
"Setting missing Tuple.resultTypeSpecifier to TupleTypeSpecifier to '{resultType}' on ReturnClause @ {locator}.\n{expressionKey}",
68+
tupleTypeSpecifier,
69+
clause.locator,
70+
self.ContextStackString);
71+
tuple.resultTypeSpecifier = tupleTypeSpecifier;
72+
}
73+
}
74+
75+
{
76+
// If a Tuple has elements with missing resultTypeSpecifier but the Tuple itself has a TupleTypeSpecifier as resultTypeSpecifier, set the missing element resultTypeSpecifiers from the TupleTypeSpecifier
77+
if (node is Tuple { element: { Length: > 0 } elements, resultTypeSpecifier: TupleTypeSpecifier tupleTypeSpecifier } tuple
78+
&& elements.Any(e => e.value.resultTypeSpecifier is null))
79+
{
80+
logger.LogDebug(
81+
"Setting missing Tuple.element.value.resultTypeSpecifier from TupleTypeSpecifier on Tuple @ {locator}.\n{expressionKey}",
82+
tuple.locator,
83+
self.ContextStackString);
84+
foreach (var (index, tupleElement) in elements.Select((v, i) => (i, v)))
85+
{
86+
tupleElement.value.resultTypeSpecifier ??= tupleTypeSpecifier.element[index].elementType;
87+
}
88+
}
3289
}
3390

3491
return false; // Continue walking children

Cql/PackagerCLI/Hl7.Cql.Packager.ecqm-content-cms-2025.appsettings.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
{
22
"Elm": {
33
"SkipFiles": [
4-
// Tuple element value does not have a resultTypeSpecifier
5-
"CMS2FHIRPCSDepressionScreenAndFollowUp.json",
6-
74
// Exception: cannot convert from 'Hl7.Fhir.Model.Id' to 'Hl7.Fhir.Model.Code<Hl7.Fhir.Model.Account.AccountStatus>'
85
"NHSNHelpers.json",
96

Cql/PackagerCLI/Hl7.Cql.Packager.ecqm-content-qicore-2024.appsettings.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@
1919
"CMS506FHIRSafeUseofOpioids.json",
2020
"PCSBMIScreenAndFollowUpFHIR.json",
2121

22-
// Tuple element value does not have a resultTypeSpecifier
23-
"CADBetaBlockerTherapyPriorMIorLVSDFHIR.json",
24-
"PCSDepressionScreenAndFollowUpFHIR.json",
25-
2622
// Multiple sort fields not supported yet
2723
"CMS986FHIRMalnutritionScore.json",
2824
"HospitalHarmAcuteKidneyInjuryFHIR.json",

Cql/PackagerCLI/Hl7.Cql.Packager.ecqm-content-qicore-2025.appsettings.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
{
22
"Elm": {
33
"SkipFiles": [
4-
// Tuple element value does not have a resultTypeSpecifier
5-
"CMS2FHIRPCSDepressionScreenAndFollowUp.json",
6-
"CMS145FHIRCADBetaBlockerTherapyPriorMIorLVSD.json",
7-
"CMS832HHAKIFHIR.json",
8-
94
// Cannot resolve type {http://hl7.org/fhir}DoNotPerformReason} for expression
105
"CMS190VTEProphylaxisICUFHIR.json",
116
"CMS108FHIRVTEProphylaxis.json"

0 commit comments

Comments
 (0)