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
53QT_BEGIN_NAMESPACE
54
55using namespace QPatternist;
56
57QString 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
65Expression::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
82bool 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
114Expression::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
121Expression::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
294QT_END_NAMESPACE
295

source code of qtxmlpatterns/src/xmlpatterns/type/qtypechecker.cpp