• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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