1 //===--- NarrowingConversionsCheck.cpp - clang-tidy------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "NarrowingConversionsCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/Type.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "llvm/ADT/APSInt.h"
14 #include "llvm/ADT/SmallString.h"
15 #include "llvm/ADT/SmallVector.h"
16
17 #include <cstdint>
18
19 using namespace clang::ast_matchers;
20
21 namespace clang {
22 namespace tidy {
23 namespace cppcoreguidelines {
24
NarrowingConversionsCheck(StringRef Name,ClangTidyContext * Context)25 NarrowingConversionsCheck::NarrowingConversionsCheck(StringRef Name,
26 ClangTidyContext *Context)
27 : ClangTidyCheck(Name, Context),
28 WarnOnFloatingPointNarrowingConversion(
29 Options.get("WarnOnFloatingPointNarrowingConversion", true)),
30 PedanticMode(Options.get("PedanticMode", false)) {}
31
storeOptions(ClangTidyOptions::OptionMap & Opts)32 void NarrowingConversionsCheck::storeOptions(
33 ClangTidyOptions::OptionMap &Opts) {
34 Options.store(Opts, "WarnOnFloatingPointNarrowingConversion",
35 WarnOnFloatingPointNarrowingConversion);
36 Options.store(Opts, "PedanticMode", PedanticMode);
37 }
38
registerMatchers(MatchFinder * Finder)39 void NarrowingConversionsCheck::registerMatchers(MatchFinder *Finder) {
40 // ceil() and floor() are guaranteed to return integers, even though the type
41 // is not integral.
42 const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl(
43 hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor")))));
44
45 // Casts:
46 // i = 0.5;
47 // void f(int); f(0.5);
48 Finder->addMatcher(
49 traverse(
50 ast_type_traits::TK_AsIs,
51 implicitCastExpr(hasImplicitDestinationType(
52 hasUnqualifiedDesugaredType(builtinType())),
53 hasSourceExpression(hasType(
54 hasUnqualifiedDesugaredType(builtinType()))),
55 unless(hasSourceExpression(IsCeilFloorCallExpr)),
56 unless(hasParent(castExpr())),
57 unless(isInTemplateInstantiation()))
58 .bind("cast")),
59 this);
60
61 // Binary operators:
62 // i += 0.5;
63 Finder->addMatcher(
64 binaryOperator(
65 isAssignmentOperator(),
66 hasLHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))),
67 hasRHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))),
68 unless(hasRHS(IsCeilFloorCallExpr)),
69 unless(isInTemplateInstantiation()),
70 // The `=` case generates an implicit cast
71 // which is covered by the previous matcher.
72 unless(hasOperatorName("=")))
73 .bind("binary_op"),
74 this);
75 }
76
getBuiltinType(const Expr & E)77 static const BuiltinType *getBuiltinType(const Expr &E) {
78 return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>();
79 }
80
getUnqualifiedType(const Expr & E)81 static QualType getUnqualifiedType(const Expr &E) {
82 return E.getType().getUnqualifiedType();
83 }
84
getConstantExprValue(const ASTContext & Ctx,const Expr & E)85 static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E) {
86 if (auto IntegerConstant = E.getIntegerConstantExpr(Ctx))
87 return APValue(*IntegerConstant);
88 APValue Constant;
89 if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant))
90 return Constant;
91 return {};
92 }
93
getIntegerConstantExprValue(const ASTContext & Context,const Expr & E,llvm::APSInt & Value)94 static bool getIntegerConstantExprValue(const ASTContext &Context,
95 const Expr &E, llvm::APSInt &Value) {
96 APValue Constant = getConstantExprValue(Context, E);
97 if (!Constant.isInt())
98 return false;
99 Value = Constant.getInt();
100 return true;
101 }
102
getFloatingConstantExprValue(const ASTContext & Context,const Expr & E,llvm::APFloat & Value)103 static bool getFloatingConstantExprValue(const ASTContext &Context,
104 const Expr &E, llvm::APFloat &Value) {
105 APValue Constant = getConstantExprValue(Context, E);
106 if (!Constant.isFloat())
107 return false;
108 Value = Constant.getFloat();
109 return true;
110 }
111
112 namespace {
113
114 struct IntegerRange {
Containsclang::tidy::cppcoreguidelines::__anon58a210e70111::IntegerRange115 bool Contains(const IntegerRange &From) const {
116 return llvm::APSInt::compareValues(Lower, From.Lower) <= 0 &&
117 llvm::APSInt::compareValues(Upper, From.Upper) >= 0;
118 }
119
Containsclang::tidy::cppcoreguidelines::__anon58a210e70111::IntegerRange120 bool Contains(const llvm::APSInt &Value) const {
121 return llvm::APSInt::compareValues(Lower, Value) <= 0 &&
122 llvm::APSInt::compareValues(Upper, Value) >= 0;
123 }
124
125 llvm::APSInt Lower;
126 llvm::APSInt Upper;
127 };
128
129 } // namespace
130
createFromType(const ASTContext & Context,const BuiltinType & T)131 static IntegerRange createFromType(const ASTContext &Context,
132 const BuiltinType &T) {
133 if (T.isFloatingPoint()) {
134 unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision(
135 Context.getFloatTypeSemantics(T.desugar()));
136 // Contrary to two's complement integer, floating point values are
137 // symmetric and have the same number of positive and negative values.
138 // The range of valid integers for a floating point value is:
139 // [-2^PrecisionBits, 2^PrecisionBits]
140
141 // Values are created with PrecisionBits plus two bits:
142 // - One to express the missing negative value of 2's complement
143 // representation.
144 // - One for the sign.
145 llvm::APSInt UpperValue(PrecisionBits + 2, /*isUnsigned*/ false);
146 UpperValue.setBit(PrecisionBits);
147 llvm::APSInt LowerValue(PrecisionBits + 2, /*isUnsigned*/ false);
148 LowerValue.setBit(PrecisionBits);
149 LowerValue.setSignBit();
150 return {LowerValue, UpperValue};
151 }
152 assert(T.isInteger() && "Unexpected builtin type");
153 uint64_t TypeSize = Context.getTypeSize(&T);
154 bool IsUnsignedInteger = T.isUnsignedInteger();
155 return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger),
156 llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)};
157 }
158
isWideEnoughToHold(const ASTContext & Context,const BuiltinType & FromType,const BuiltinType & ToType)159 static bool isWideEnoughToHold(const ASTContext &Context,
160 const BuiltinType &FromType,
161 const BuiltinType &ToType) {
162 IntegerRange FromIntegerRange = createFromType(Context, FromType);
163 IntegerRange ToIntegerRange = createFromType(Context, ToType);
164 return ToIntegerRange.Contains(FromIntegerRange);
165 }
166
isWideEnoughToHold(const ASTContext & Context,const llvm::APSInt & IntegerConstant,const BuiltinType & ToType)167 static bool isWideEnoughToHold(const ASTContext &Context,
168 const llvm::APSInt &IntegerConstant,
169 const BuiltinType &ToType) {
170 IntegerRange ToIntegerRange = createFromType(Context, ToType);
171 return ToIntegerRange.Contains(IntegerConstant);
172 }
173
getValueAsString(const llvm::APSInt & Value,uint64_t HexBits)174 static llvm::SmallString<64> getValueAsString(const llvm::APSInt &Value,
175 uint64_t HexBits) {
176 llvm::SmallString<64> Str;
177 Value.toString(Str, 10);
178 if (HexBits > 0) {
179 Str.append(" (0x");
180 llvm::SmallString<32> HexValue;
181 Value.toStringUnsigned(HexValue, 16);
182 for (size_t I = HexValue.size(); I < (HexBits / 4); ++I)
183 Str.append("0");
184 Str.append(HexValue);
185 Str.append(")");
186 }
187 return Str;
188 }
189
diagNarrowType(SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs)190 void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,
191 const Expr &Lhs,
192 const Expr &Rhs) {
193 diag(SourceLoc, "narrowing conversion from %0 to %1")
194 << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
195 }
196
diagNarrowTypeToSignedInt(SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs)197 void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(
198 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs) {
199 diag(SourceLoc, "narrowing conversion from %0 to signed type %1 is "
200 "implementation-defined")
201 << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
202 }
203
diagNarrowIntegerConstant(SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs,const llvm::APSInt & Value)204 void NarrowingConversionsCheck::diagNarrowIntegerConstant(
205 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
206 const llvm::APSInt &Value) {
207 diag(SourceLoc,
208 "narrowing conversion from constant value %0 of type %1 to %2")
209 << getValueAsString(Value, /*NoHex*/ 0) << getUnqualifiedType(Rhs)
210 << getUnqualifiedType(Lhs);
211 }
212
diagNarrowIntegerConstantToSignedInt(SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs,const llvm::APSInt & Value,const uint64_t HexBits)213 void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt(
214 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
215 const llvm::APSInt &Value, const uint64_t HexBits) {
216 diag(SourceLoc, "narrowing conversion from constant value %0 of type %1 "
217 "to signed type %2 is implementation-defined")
218 << getValueAsString(Value, HexBits) << getUnqualifiedType(Rhs)
219 << getUnqualifiedType(Lhs);
220 }
221
diagNarrowConstant(SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs)222 void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
223 const Expr &Lhs,
224 const Expr &Rhs) {
225 diag(SourceLoc, "narrowing conversion from constant %0 to %1")
226 << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
227 }
228
diagConstantCast(SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs)229 void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
230 const Expr &Lhs,
231 const Expr &Rhs) {
232 diag(SourceLoc, "constant value should be of type of type %0 instead of %1")
233 << getUnqualifiedType(Lhs) << getUnqualifiedType(Rhs);
234 }
235
diagNarrowTypeOrConstant(const ASTContext & Context,SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs)236 void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
237 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
238 const Expr &Rhs) {
239 APValue Constant = getConstantExprValue(Context, Rhs);
240 if (Constant.isInt())
241 return diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Constant.getInt());
242 if (Constant.isFloat())
243 return diagNarrowConstant(SourceLoc, Lhs, Rhs);
244 return diagNarrowType(SourceLoc, Lhs, Rhs);
245 }
246
handleIntegralCast(const ASTContext & Context,SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs)247 void NarrowingConversionsCheck::handleIntegralCast(const ASTContext &Context,
248 SourceLocation SourceLoc,
249 const Expr &Lhs,
250 const Expr &Rhs) {
251 const BuiltinType *ToType = getBuiltinType(Lhs);
252 // From [conv.integral]p7.3.8:
253 // Conversions to unsigned integer is well defined so no warning is issued.
254 // "The resulting value is the smallest unsigned value equal to the source
255 // value modulo 2^n where n is the number of bits used to represent the
256 // destination type."
257 if (ToType->isUnsignedInteger())
258 return;
259 const BuiltinType *FromType = getBuiltinType(Rhs);
260 llvm::APSInt IntegerConstant;
261 if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
262 if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
263 diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs, IntegerConstant,
264 Context.getTypeSize(FromType));
265 return;
266 }
267 if (!isWideEnoughToHold(Context, *FromType, *ToType))
268 diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs);
269 }
270
handleIntegralToBoolean(const ASTContext & Context,SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs)271 void NarrowingConversionsCheck::handleIntegralToBoolean(
272 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
273 const Expr &Rhs) {
274 // Conversion from Integral to Bool value is well defined.
275
276 // We keep this function (even if it is empty) to make sure that
277 // handleImplicitCast and handleBinaryOperator are symmetric in their behavior
278 // and handle the same cases.
279 }
280
handleIntegralToFloating(const ASTContext & Context,SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs)281 void NarrowingConversionsCheck::handleIntegralToFloating(
282 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
283 const Expr &Rhs) {
284 const BuiltinType *ToType = getBuiltinType(Lhs);
285 llvm::APSInt IntegerConstant;
286 if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
287 if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
288 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);
289 return;
290 }
291 const BuiltinType *FromType = getBuiltinType(Rhs);
292 if (!isWideEnoughToHold(Context, *FromType, *ToType))
293 diagNarrowType(SourceLoc, Lhs, Rhs);
294 }
295
handleFloatingToIntegral(const ASTContext & Context,SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs)296 void NarrowingConversionsCheck::handleFloatingToIntegral(
297 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
298 const Expr &Rhs) {
299 llvm::APFloat FloatConstant(0.0);
300
301 // We always warn when Rhs is non-constexpr.
302 if (!getFloatingConstantExprValue(Context, Rhs, FloatConstant))
303 return diagNarrowType(SourceLoc, Lhs, Rhs);
304
305 QualType DestType = Lhs.getType();
306 unsigned DestWidth = Context.getIntWidth(DestType);
307 bool DestSigned = DestType->isSignedIntegerOrEnumerationType();
308 llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned);
309 bool IsExact = false;
310 bool Overflows = FloatConstant.convertToInteger(
311 Result, llvm::APFloat::rmTowardZero, &IsExact) &
312 llvm::APFloat::opInvalidOp;
313 // We warn iff the constant floating point value is not exactly representable.
314 if (Overflows || !IsExact)
315 return diagNarrowConstant(SourceLoc, Lhs, Rhs);
316
317 if (PedanticMode)
318 return diagConstantCast(SourceLoc, Lhs, Rhs);
319 }
320
handleFloatingToBoolean(const ASTContext & Context,SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs)321 void NarrowingConversionsCheck::handleFloatingToBoolean(
322 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
323 const Expr &Rhs) {
324 return diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs);
325 }
326
handleBooleanToSignedIntegral(const ASTContext & Context,SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs)327 void NarrowingConversionsCheck::handleBooleanToSignedIntegral(
328 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
329 const Expr &Rhs) {
330 // Conversion from Bool to SignedIntegral value is well defined.
331
332 // We keep this function (even if it is empty) to make sure that
333 // handleImplicitCast and handleBinaryOperator are symmetric in their behavior
334 // and handle the same cases.
335 }
336
handleFloatingCast(const ASTContext & Context,SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs)337 void NarrowingConversionsCheck::handleFloatingCast(const ASTContext &Context,
338 SourceLocation SourceLoc,
339 const Expr &Lhs,
340 const Expr &Rhs) {
341 if (WarnOnFloatingPointNarrowingConversion) {
342 const BuiltinType *ToType = getBuiltinType(Lhs);
343 APValue Constant = getConstantExprValue(Context, Rhs);
344 if (Constant.isFloat()) {
345 // From [dcl.init.list]p7.2:
346 // Floating point constant narrowing only takes place when the value is
347 // not within destination range. We convert the value to the destination
348 // type and check if the resulting value is infinity.
349 llvm::APFloat Tmp = Constant.getFloat();
350 bool UnusedLosesInfo;
351 Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()),
352 llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo);
353 if (Tmp.isInfinity())
354 diagNarrowConstant(SourceLoc, Lhs, Rhs);
355 return;
356 }
357 const BuiltinType *FromType = getBuiltinType(Rhs);
358 if (ToType->getKind() < FromType->getKind())
359 diagNarrowType(SourceLoc, Lhs, Rhs);
360 }
361 }
362
handleBinaryOperator(const ASTContext & Context,SourceLocation SourceLoc,const Expr & Lhs,const Expr & Rhs)363 void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
364 SourceLocation SourceLoc,
365 const Expr &Lhs,
366 const Expr &Rhs) {
367 assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&
368 "Dependent types must be check before calling this function");
369 const BuiltinType *LhsType = getBuiltinType(Lhs);
370 const BuiltinType *RhsType = getBuiltinType(Rhs);
371 if (RhsType == nullptr || LhsType == nullptr)
372 return;
373 if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())
374 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
375 if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)
376 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
377 if (RhsType->isInteger() && LhsType->isFloatingPoint())
378 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
379 if (RhsType->isInteger() && LhsType->isInteger())
380 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
381 if (RhsType->isFloatingPoint() && LhsType->getKind() == BuiltinType::Bool)
382 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
383 if (RhsType->isFloatingPoint() && LhsType->isInteger())
384 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
385 if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())
386 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
387 }
388
handleConditionalOperator(const ASTContext & Context,const Expr & Lhs,const Expr & Rhs)389 bool NarrowingConversionsCheck::handleConditionalOperator(
390 const ASTContext &Context, const Expr &Lhs, const Expr &Rhs) {
391 if (const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
392 // We have an expression like so: `output = cond ? lhs : rhs`
393 // From the point of view of narrowing conversion we treat it as two
394 // expressions `output = lhs` and `output = rhs`.
395 handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs,
396 *CO->getLHS());
397 handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs,
398 *CO->getRHS());
399 return true;
400 }
401 return false;
402 }
403
handleImplicitCast(const ASTContext & Context,const ImplicitCastExpr & Cast)404 void NarrowingConversionsCheck::handleImplicitCast(
405 const ASTContext &Context, const ImplicitCastExpr &Cast) {
406 if (Cast.getExprLoc().isMacroID())
407 return;
408 const Expr &Lhs = Cast;
409 const Expr &Rhs = *Cast.getSubExpr();
410 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
411 return;
412 if (handleConditionalOperator(Context, Lhs, Rhs))
413 return;
414 SourceLocation SourceLoc = Lhs.getExprLoc();
415 switch (Cast.getCastKind()) {
416 case CK_BooleanToSignedIntegral:
417 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
418 case CK_IntegralToBoolean:
419 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
420 case CK_IntegralToFloating:
421 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
422 case CK_IntegralCast:
423 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
424 case CK_FloatingToBoolean:
425 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
426 case CK_FloatingToIntegral:
427 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
428 case CK_FloatingCast:
429 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
430 default:
431 break;
432 }
433 }
434
handleBinaryOperator(const ASTContext & Context,const BinaryOperator & Op)435 void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
436 const BinaryOperator &Op) {
437 if (Op.getBeginLoc().isMacroID())
438 return;
439 const Expr &Lhs = *Op.getLHS();
440 const Expr &Rhs = *Op.getRHS();
441 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
442 return;
443 if (handleConditionalOperator(Context, Lhs, Rhs))
444 return;
445 handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
446 }
447
check(const MatchFinder::MatchResult & Result)448 void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) {
449 if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("binary_op"))
450 return handleBinaryOperator(*Result.Context, *Op);
451 if (const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast"))
452 return handleImplicitCast(*Result.Context, *Cast);
453 llvm_unreachable("must be binary operator or cast expression");
454 }
455 } // namespace cppcoreguidelines
456 } // namespace tidy
457 } // namespace clang
458