1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtXmlPatterns module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qargumentconverter_p.h" |
41 | #include "qatomictype_p.h" |
42 | #include "qatomizer_p.h" |
43 | #include "qbuiltintypes_p.h" |
44 | #include "qcardinalityverifier_p.h" |
45 | #include "qcommonsequencetypes_p.h" |
46 | #include "qfunctionfactory_p.h" |
47 | #include "qitemverifier_p.h" |
48 | #include "qpatternistlocale_p.h" |
49 | #include "quntypedatomicconverter_p.h" |
50 | |
51 | #include "qtypechecker_p.h" |
52 | |
53 | QT_BEGIN_NAMESPACE |
54 | |
55 | using namespace QPatternist; |
56 | |
57 | QString TypeChecker::wrongType(const NamePool::Ptr &np, |
58 | const ItemType::Ptr &reqType, |
59 | const ItemType::Ptr &opType) |
60 | { |
61 | return QtXmlPatterns::tr(sourceText: "Required type is %1, but %2 was found." ) |
62 | .arg(args: formatType(np, type: reqType), args: formatType(np, type: opType)); |
63 | } |
64 | |
65 | Expression::Ptr TypeChecker::applyFunctionConversion(const Expression::Ptr &operand, |
66 | const SequenceType::Ptr &reqType, |
67 | const StaticContext::Ptr &context, |
68 | const ReportContext::ErrorCode code, |
69 | const Options options) |
70 | { |
71 | Q_ASSERT_X(!ReportContext::codeToString(code).isEmpty(), Q_FUNC_INFO, |
72 | "This test ensures 'code' exists, otherwise codeToString() would assert." ); |
73 | Q_ASSERT(operand); |
74 | Q_ASSERT(reqType); |
75 | Q_ASSERT(context); |
76 | |
77 | /* Do it in two steps: verify type, and then cardinality. */ |
78 | const Expression::Ptr cardVerified(CardinalityVerifier::verifyCardinality(operand, card: reqType->cardinality(), context, code)); |
79 | return verifyType(operand: cardVerified, reqSeqType: reqType, context, code, options); |
80 | } |
81 | |
82 | bool TypeChecker::promotionPossible(const ItemType::Ptr &fromType, |
83 | const ItemType::Ptr &toType, |
84 | const StaticContext::Ptr &context) |
85 | { |
86 | /* These types can be promoted to xs:string. xs:untypedAtomic should be |
87 | * cast when interpreting it formally, but implementing it as a promotion |
88 | * gives the same result(and is faster). */ |
89 | if(*toType == *BuiltinTypes::xsString && |
90 | (BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(other: fromType) || |
91 | BuiltinTypes::xsAnyURI->xdtTypeMatches(other: fromType))) |
92 | return true; |
93 | |
94 | if(*toType == *BuiltinTypes::xsDouble && |
95 | BuiltinTypes::numeric->xdtTypeMatches(other: fromType)) |
96 | { |
97 | /* Any numeric can be promoted to xs:double. */ |
98 | return true; |
99 | } |
100 | |
101 | /* xs:decimal/xs:integer can be promoted to xs:float. */ |
102 | if(*toType == *BuiltinTypes::xsFloat && BuiltinTypes::xsDecimal->xdtTypeMatches(other: fromType)) |
103 | |
104 | { |
105 | context->warning(message: QtXmlPatterns::tr(sourceText: "Promoting %1 to %2 may cause loss of precision." ) |
106 | .arg(a: formatType(np: context->namePool(), type: fromType)) |
107 | .arg(a: formatType(np: context->namePool(), type: BuiltinTypes::xsFloat))); |
108 | return true; |
109 | } |
110 | |
111 | return false; |
112 | } |
113 | |
114 | Expression::Ptr TypeChecker::typeCheck(Expression *const op, |
115 | const StaticContext::Ptr &context, |
116 | const SequenceType::Ptr &reqType) |
117 | { |
118 | return Expression::Ptr(op->typeCheck(context, reqType)); |
119 | } |
120 | |
121 | Expression::Ptr TypeChecker::verifyType(const Expression::Ptr &operand, |
122 | const SequenceType::Ptr &reqSeqType, |
123 | const StaticContext::Ptr &context, |
124 | const ReportContext::ErrorCode code, |
125 | const Options options) |
126 | { |
127 | const ItemType::Ptr reqType(reqSeqType->itemType()); |
128 | const Expression::Properties props(operand->properties()); |
129 | |
130 | /* If operand requires a focus, do the necessary type checking for that. */ |
131 | if(props.testFlag(flag: Expression::RequiresFocus) && options.testFlag(flag: CheckFocus)) |
132 | { |
133 | const ItemType::Ptr contextType(context->contextItemType()); |
134 | if(contextType) |
135 | { |
136 | if(props.testFlag(flag: Expression::RequiresContextItem)) |
137 | { |
138 | Q_ASSERT_X(operand->expectedContextItemType(), Q_FUNC_INFO, |
139 | "When the Expression sets the RequiresContextItem property, it must " |
140 | "return a type in expectedContextItemType()" ); |
141 | const ItemType::Ptr expectedContextType(operand->expectedContextItemType()); |
142 | |
143 | /* Allow the empty sequence. We don't want to trigger XPTY0020 on ()/... . */ |
144 | if(!expectedContextType->xdtTypeMatches(other: contextType) && contextType != CommonSequenceTypes::Empty) |
145 | { |
146 | context->error(message: wrongType(np: context->namePool(), reqType: operand->expectedContextItemType(), opType: contextType), |
147 | errorCode: ReportContext::XPTY0020, reflection: operand.data()); |
148 | return operand; |
149 | } |
150 | } |
151 | } |
152 | else |
153 | { |
154 | context->error(message: QtXmlPatterns::tr(sourceText: "The focus is undefined." ), errorCode: ReportContext::XPDY0002, reflection: operand.data()); |
155 | return operand; |
156 | } |
157 | } |
158 | |
159 | SequenceType::Ptr operandSeqType(operand->staticType()); |
160 | ItemType::Ptr operandType(operandSeqType->itemType()); |
161 | |
162 | /* This returns the operand if the types are identical or if operandType |
163 | * is a subtype of reqType. */ |
164 | if(reqType->xdtTypeMatches(other: operandType) || *operandType == *CommonSequenceTypes::Empty) |
165 | return operand; |
166 | |
167 | /* Since we haven't exited yet, it means that the operandType is a super type |
168 | * of reqType, and that there hence is a path down to it through the |
169 | * type hierachy -- but that doesn't necessarily mean that a up-cast(down the |
170 | * hierarchy) would succeed. */ |
171 | |
172 | Expression::Ptr result(operand); |
173 | |
174 | if(reqType->isAtomicType()) |
175 | { |
176 | const Expression::ID opID = operand->id(); |
177 | if((opID == Expression::IDArgumentReference || |
178 | (opID == Expression::IDCardinalityVerifier && operand->operands().first()->is(i: Expression::IDArgumentReference))) |
179 | && *BuiltinTypes::item == *operandType) |
180 | return Expression::Ptr(new ArgumentConverter(result, reqType)); |
181 | |
182 | if(!operandType->isAtomicType()) |
183 | { |
184 | result = Expression::Ptr(new Atomizer(result)); |
185 | /* The atomizer might know more about the type. */ |
186 | operandType = result->staticType()->itemType(); |
187 | } |
188 | |
189 | if(reqType->xdtTypeMatches(other: operandType)) |
190 | { |
191 | /* Atomization was sufficient. Either the expected type is xs:anyAtomicType |
192 | * or the type the Atomizer knows it returns, matches the required type. */ |
193 | return result; |
194 | } |
195 | |
196 | const bool compatModeEnabled = context->compatModeEnabled(); |
197 | |
198 | if((options.testFlag(flag: AutomaticallyConvert) && BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(other: operandType)) || |
199 | (compatModeEnabled && BuiltinTypes::xsString->xdtTypeMatches(other: reqType))) |
200 | { |
201 | if(*reqType == *BuiltinTypes::numeric) |
202 | { |
203 | result = typeCheck(op: new UntypedAtomicConverter(result, BuiltinTypes::xsDouble, code), |
204 | context, reqType: reqSeqType); |
205 | } |
206 | else |
207 | result = typeCheck(op: new UntypedAtomicConverter(result, reqType, code), context, reqType: reqSeqType); |
208 | |
209 | /* The UntypedAtomicConverter might know more about the type, so reload. */ |
210 | operandType = result->staticType()->itemType(); |
211 | } |
212 | else if(compatModeEnabled && *reqType == *BuiltinTypes::xsDouble) |
213 | { |
214 | const FunctionFactory::Ptr functions(context->functionSignatures()); |
215 | Expression::List numberArgs; |
216 | numberArgs.append(t: operand); |
217 | |
218 | result = functions->createFunctionCall(name: QXmlName(StandardNamespaces::fn, StandardLocalNames::number), |
219 | arguments: numberArgs, |
220 | context, |
221 | r: operand.data())->typeCheck(context, reqType: reqSeqType); |
222 | operandType = result->staticType()->itemType(); |
223 | context->wrapExpressionWith(existingNode: operand.data(), newNode: result); |
224 | } |
225 | |
226 | if(reqType->xdtTypeMatches(other: operandType)) |
227 | return result; |
228 | |
229 | /* Test if promotion will solve it; the xdtTypeMatches didn't |
230 | * do that. */ |
231 | if(options.testFlag(flag: AutomaticallyConvert) && promotionPossible(fromType: operandType, toType: reqType, context)) |
232 | { |
233 | if(options.testFlag(flag: GeneratePromotion)) |
234 | return Expression::Ptr(new UntypedAtomicConverter(result, reqType)); |
235 | else |
236 | return result; |
237 | } |
238 | |
239 | if(operandType->xdtTypeMatches(other: reqType)) |
240 | { |
241 | /* For example, operandType is numeric, and reqType is xs:integer. */ |
242 | return Expression::Ptr(new ItemVerifier(result, reqType, code)); |
243 | } |
244 | else |
245 | { |
246 | context->error(message: wrongType(np: context->namePool(), reqType, opType: operandType), errorCode: code, reflection: operand.data()); |
247 | return result; |
248 | } |
249 | } |
250 | else if(reqType->isNodeType()) |
251 | { |
252 | |
253 | ReportContext::ErrorCode myCode; |
254 | |
255 | if(*reqType == *CommonSequenceTypes::EBV->itemType()) |
256 | myCode = ReportContext::FORG0006; |
257 | else |
258 | myCode = code; |
259 | |
260 | /* empty-sequence() is considered valid because it's ok to do |
261 | * for example nilled( () ). That is, to pass an empty sequence to a |
262 | * function requiring for example node()?. */ |
263 | if(*operandType == *CommonSequenceTypes::Empty) |
264 | return result; |
265 | else if(!operandType->xdtTypeMatches(other: reqType)) |
266 | { |
267 | context->error(message: wrongType(np: context->namePool(), reqType, opType: operandType), errorCode: myCode, reflection: operand.data()); |
268 | return result; |
269 | } |
270 | |
271 | /* Operand must be an item. Thus, the sequence can contain both |
272 | * nodes and atomic values: we have to verify. */ |
273 | return Expression::Ptr(new ItemVerifier(result, reqType, myCode)); |
274 | } |
275 | else |
276 | { |
277 | Q_ASSERT(*reqType == *CommonSequenceTypes::Empty); |
278 | |
279 | /* element() doesn't match empty-sequence(), but element()* does. */ |
280 | if(!reqType->xdtTypeMatches(other: operandType) && |
281 | !operandSeqType->cardinality().allowsEmpty()) |
282 | { |
283 | context->error(message: wrongType(np: context->namePool(), reqType, opType: operandType), |
284 | errorCode: code, reflection: operand.data()); |
285 | return result; |
286 | } |
287 | } |
288 | |
289 | /* This line should be reached if required type is |
290 | * EBVType, and the operand is compatible. */ |
291 | return result; |
292 | } |
293 | |
294 | QT_END_NAMESPACE |
295 | |