1 // © 2024 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3
4 #include "unicode/utypes.h"
5
6 #if !UCONFIG_NO_FORMATTING
7
8 #if !UCONFIG_NO_MF2
9
10 #include "messageformat2_allocation.h"
11 #include "messageformat2_checker.h"
12 #include "messageformat2_macros.h"
13 #include "uvector.h" // U_ASSERT
14
15 U_NAMESPACE_BEGIN
16
17 namespace message2 {
18
19 /*
20 Checks data model errors
21 (see https://github.com/unicode-org/message-format-wg/blob/main/spec/formatting.md#error-handling )
22
23 The following are checked here:
24 Variant Key Mismatch
25 Duplicate Variant
26 Missing Fallback Variant (called NonexhaustivePattern here)
27 Missing Selector Annotation
28 Duplicate Declaration
29 - Most duplicate declaration errors are checked by the parser,
30 but the checker checks for declarations of input variables
31 that were previously implicitly declared
32 (Duplicate option names and duplicate declarations are checked by the parser)
33 */
34
35 // Type environments
36 // -----------------
37
TypeEnvironment(UErrorCode & status)38 TypeEnvironment::TypeEnvironment(UErrorCode& status) {
39 CHECK_ERROR(status);
40
41 UVector* temp;
42 temp = createStringVectorNoAdopt(status);
43 CHECK_ERROR(status);
44 annotated.adoptInstead(temp);
45 temp = createStringVectorNoAdopt(status);
46 CHECK_ERROR(status);
47 unannotated.adoptInstead(temp);
48 temp = createStringVectorNoAdopt(status);
49 CHECK_ERROR(status);
50 freeVars.adoptInstead(temp);
51 }
52
has(const UVector & v,const VariableName & var)53 static bool has(const UVector& v, const VariableName& var) {
54 return v.contains(const_cast<void*>(static_cast<const void*>(&var)));
55 }
56
57 // Returns true if `var` was either previously used (implicit declaration),
58 // or is in scope by an explicit declaration
known(const VariableName & var) const59 bool TypeEnvironment::known(const VariableName& var) const {
60 return has(*annotated, var) || has(*unannotated, var) || has(*freeVars, var);
61 }
62
get(const VariableName & var) const63 TypeEnvironment::Type TypeEnvironment::get(const VariableName& var) const {
64 U_ASSERT(annotated.isValid());
65 if (has(*annotated, var)) {
66 return Annotated;
67 }
68 U_ASSERT(unannotated.isValid());
69 if (has(*unannotated, var)) {
70 return Unannotated;
71 }
72 U_ASSERT(freeVars.isValid());
73 if (has(*freeVars, var)) {
74 return FreeVariable;
75 }
76 // This case is a "free variable without an implicit declaration",
77 // i.e. one used only in a selector expression and not in a declaration RHS
78 return Unannotated;
79 }
80
extend(const VariableName & var,TypeEnvironment::Type t,UErrorCode & status)81 void TypeEnvironment::extend(const VariableName& var, TypeEnvironment::Type t, UErrorCode& status) {
82 if (t == Unannotated) {
83 U_ASSERT(unannotated.isValid());
84 // See comment below
85 unannotated->addElement(const_cast<void*>(static_cast<const void*>(&var)), status);
86 return;
87 }
88
89 if (t == FreeVariable) {
90 U_ASSERT(freeVars.isValid());
91 // See comment below
92 freeVars->addElement(const_cast<void*>(static_cast<const void*>(&var)), status);
93 return;
94 }
95
96 U_ASSERT(annotated.isValid());
97 // This is safe because elements of `annotated` are never written
98 // and the lifetime of `var` is guaranteed to include the lifetime of
99 // `annotated`
100 annotated->addElement(const_cast<void*>(static_cast<const void*>(&var)), status);
101 }
102
~TypeEnvironment()103 TypeEnvironment::~TypeEnvironment() {}
104
105 // ---------------------
106
areDefaultKeys(const Key * keys,int32_t len)107 static bool areDefaultKeys(const Key* keys, int32_t len) {
108 U_ASSERT(len > 0);
109 for (int32_t i = 0; i < len; i++) {
110 if (!keys[i].isWildcard()) {
111 return false;
112 }
113 }
114 return true;
115 }
116
addFreeVars(TypeEnvironment & t,const Operand & rand,UErrorCode & status)117 void Checker::addFreeVars(TypeEnvironment& t, const Operand& rand, UErrorCode& status) {
118 CHECK_ERROR(status);
119
120 if (rand.isVariable()) {
121 const VariableName& v = rand.asVariable();
122 if (!t.known(v)) {
123 t.extend(v, TypeEnvironment::Type::FreeVariable, status);
124 }
125 }
126 }
127
addFreeVars(TypeEnvironment & t,const OptionMap & opts,UErrorCode & status)128 void Checker::addFreeVars(TypeEnvironment& t, const OptionMap& opts, UErrorCode& status) {
129 for (int32_t i = 0; i < opts.size(); i++) {
130 const Option& o = opts.getOption(i, status);
131 CHECK_ERROR(status);
132 addFreeVars(t, o.getValue(), status);
133 }
134 }
135
addFreeVars(TypeEnvironment & t,const Operator & rator,UErrorCode & status)136 void Checker::addFreeVars(TypeEnvironment& t, const Operator& rator, UErrorCode& status) {
137 CHECK_ERROR(status);
138
139 addFreeVars(t, rator.getOptionsInternal(), status);
140 }
141
addFreeVars(TypeEnvironment & t,const Expression & rhs,UErrorCode & status)142 void Checker::addFreeVars(TypeEnvironment& t, const Expression& rhs, UErrorCode& status) {
143 CHECK_ERROR(status);
144
145 if (rhs.isFunctionCall()) {
146 const Operator* rator = rhs.getOperator(status);
147 U_ASSERT(U_SUCCESS(status));
148 addFreeVars(t, *rator, status);
149 }
150 addFreeVars(t, rhs.getOperand(), status);
151 }
152
checkVariants(UErrorCode & status)153 void Checker::checkVariants(UErrorCode& status) {
154 CHECK_ERROR(status);
155
156 U_ASSERT(!dataModel.hasPattern());
157
158 // Check that each variant has a key list with size
159 // equal to the number of selectors
160 const Variant* variants = dataModel.getVariantsInternal();
161
162 // Check that one variant includes only wildcards
163 bool defaultExists = false;
164 bool duplicatesExist = false;
165
166 for (int32_t i = 0; i < dataModel.numVariants(); i++) {
167 const SelectorKeys& k = variants[i].getKeys();
168 const Key* keys = k.getKeysInternal();
169 int32_t len = k.len;
170 if (len != dataModel.numSelectors()) {
171 // Variant key mismatch
172 errors.addError(StaticErrorType::VariantKeyMismatchError, status);
173 return;
174 }
175 defaultExists |= areDefaultKeys(keys, len);
176
177 // Check if this variant's keys are duplicated by any other variant's keys
178 if (!duplicatesExist) {
179 // This check takes quadratic time, but it can be optimized if checking
180 // this property turns out to be a bottleneck.
181 for (int32_t j = 0; j < i; j++) {
182 const SelectorKeys& k1 = variants[j].getKeys();
183 const Key* keys1 = k1.getKeysInternal();
184 bool allEqual = true;
185 // This variant was already checked,
186 // so we know keys1.len == len
187 for (int32_t kk = 0; kk < len; kk++) {
188 if (!(keys[kk] == keys1[kk])) {
189 allEqual = false;
190 break;
191 }
192 }
193 if (allEqual) {
194 duplicatesExist = true;
195 }
196 }
197 }
198 }
199
200 if (duplicatesExist) {
201 errors.addError(StaticErrorType::DuplicateVariant, status);
202 }
203 if (!defaultExists) {
204 errors.addError(StaticErrorType::NonexhaustivePattern, status);
205 }
206 }
207
requireAnnotated(const TypeEnvironment & t,const Expression & selectorExpr,UErrorCode & status)208 void Checker::requireAnnotated(const TypeEnvironment& t, const Expression& selectorExpr, UErrorCode& status) {
209 CHECK_ERROR(status);
210
211 if (selectorExpr.isFunctionCall()) {
212 return; // No error
213 }
214 const Operand& rand = selectorExpr.getOperand();
215 if (rand.isVariable()) {
216 if (t.get(rand.asVariable()) == TypeEnvironment::Type::Annotated) {
217 return; // No error
218 }
219 }
220 // If this code is reached, an error was detected
221 errors.addError(StaticErrorType::MissingSelectorAnnotation, status);
222 }
223
checkSelectors(const TypeEnvironment & t,UErrorCode & status)224 void Checker::checkSelectors(const TypeEnvironment& t, UErrorCode& status) {
225 U_ASSERT(!dataModel.hasPattern());
226
227 // Check each selector; if it's not annotated, emit a
228 // "missing selector annotation" error
229 const Expression* selectors = dataModel.getSelectorsInternal();
230 for (int32_t i = 0; i < dataModel.numSelectors(); i++) {
231 requireAnnotated(t, selectors[i], status);
232 }
233 }
234
typeOf(TypeEnvironment & t,const Expression & expr)235 TypeEnvironment::Type typeOf(TypeEnvironment& t, const Expression& expr) {
236 if (expr.isFunctionCall()) {
237 return TypeEnvironment::Type::Annotated;
238 }
239 const Operand& rand = expr.getOperand();
240 U_ASSERT(!rand.isNull());
241 if (rand.isLiteral()) {
242 return TypeEnvironment::Type::Unannotated;
243 }
244 U_ASSERT(rand.isVariable());
245 return t.get(rand.asVariable());
246 }
247
checkDeclarations(TypeEnvironment & t,UErrorCode & status)248 void Checker::checkDeclarations(TypeEnvironment& t, UErrorCode& status) {
249 CHECK_ERROR(status);
250
251 // For each declaration, extend the type environment with its type
252 // Only a very simple type system is necessary: variables
253 // have the type "annotated", "unannotated", or "free".
254 // For "missing selector annotation" checking, free variables
255 // (message arguments) are treated as unannotated.
256 // Free variables are also used for checking duplicate declarations.
257 const Binding* env = dataModel.getLocalVariablesInternal();
258 for (int32_t i = 0; i < dataModel.bindingsLen; i++) {
259 const Binding& b = env[i];
260 const VariableName& lhs = b.getVariable();
261 const Expression& rhs = b.getValue();
262
263 // First, add free variables from the RHS of b
264 // This must be done first so we can catch:
265 // .local $foo = {$foo}
266 // (where the RHS is the first use of $foo)
267 if (b.isLocal()) {
268 addFreeVars(t, rhs, status);
269
270 // Next, check if the LHS equals any free variables
271 // whose implicit declarations are in scope
272 if (t.known(lhs) && t.get(lhs) == TypeEnvironment::Type::FreeVariable) {
273 errors.addError(StaticErrorType::DuplicateDeclarationError, status);
274 }
275 } else {
276 // Input declaration; if b has no annotation, there's nothing to check
277 if (!b.isLocal() && b.hasAnnotation()) {
278 const OptionMap& opts = b.getOptionsInternal();
279 // For .input declarations, we just need to add any variables
280 // referenced in the options
281 addFreeVars(t, opts, status);
282 }
283 // Next, check if the LHS equals any free variables
284 // whose implicit declarations are in scope
285 if (t.known(lhs) && t.get(lhs) == TypeEnvironment::Type::FreeVariable) {
286 errors.addError(StaticErrorType::DuplicateDeclarationError, status);
287 }
288 }
289 // Next, extend the type environment with a binding from lhs to its type
290 t.extend(lhs, typeOf(t, rhs), status);
291 }
292 }
293
check(UErrorCode & status)294 void Checker::check(UErrorCode& status) {
295 CHECK_ERROR(status);
296
297 TypeEnvironment typeEnv(status);
298 checkDeclarations(typeEnv, status);
299 // Pattern message
300 if (dataModel.hasPattern()) {
301 return;
302 } else {
303 // Selectors message
304 checkSelectors(typeEnv, status);
305 checkVariants(status);
306 }
307 }
308
309 } // namespace message2
310 U_NAMESPACE_END
311
312 #endif /* #if !UCONFIG_NO_MF2 */
313
314 #endif /* #if !UCONFIG_NO_FORMATTING */
315