blob: a7e4f0ccc54bd8320357cd65c1eec8f3247bc2c2 [file] [log] [blame]
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +00001//===--- ImplicitBoolCastCheck.cpp - clang-tidy----------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "ImplicitBoolCastCheck.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
Alexander Kornienkocbe8d162017-05-04 15:34:23 +000014#include "clang/Tooling/FixIt.h"
Alexander Kornienko4e001b52017-04-29 12:06:45 +000015#include <queue>
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +000016
17using namespace clang::ast_matchers;
18
19namespace clang {
20namespace tidy {
Etienne Bergeron456177b2016-05-02 18:00:29 +000021namespace readability {
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +000022
23namespace {
24
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +000025AST_MATCHER(Stmt, isMacroExpansion) {
26 SourceManager &SM = Finder->getASTContext().getSourceManager();
27 SourceLocation Loc = Node.getLocStart();
28 return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
29}
30
31bool isNULLMacroExpansion(const Stmt *Statement, ASTContext &Context) {
32 SourceManager &SM = Context.getSourceManager();
33 const LangOptions &LO = Context.getLangOpts();
34 SourceLocation Loc = Statement->getLocStart();
35 return SM.isMacroBodyExpansion(Loc) &&
Alexander Kornienkocbe8d162017-05-04 15:34:23 +000036 Lexer::getImmediateMacroName(Loc, SM, LO) == "NULL";
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +000037}
38
39AST_MATCHER(Stmt, isNULLMacroExpansion) {
40 return isNULLMacroExpansion(&Node, Finder->getASTContext());
41}
42
Alexander Kornienkocbe8d162017-05-04 15:34:23 +000043StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
44 QualType Type,
45 ASTContext &Context) {
46 switch (CastExprKind) {
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +000047 case CK_IntegralToBoolean:
Alexander Kornienkocbe8d162017-05-04 15:34:23 +000048 return Type->isUnsignedIntegerType() ? "0u" : "0";
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +000049
50 case CK_FloatingToBoolean:
Alexander Kornienkocbe8d162017-05-04 15:34:23 +000051 return Context.hasSameType(Type, Context.FloatTy) ? "0.0f" : "0.0";
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +000052
53 case CK_PointerToBoolean:
54 case CK_MemberPointerToBoolean: // Fall-through on purpose.
55 return Context.getLangOpts().CPlusPlus11 ? "nullptr" : "0";
56
57 default:
Benjamin Kramer6d505c02015-10-25 22:03:00 +000058 llvm_unreachable("Unexpected cast kind");
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +000059 }
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +000060}
61
62bool isUnaryLogicalNotOperator(const Stmt *Statement) {
Alexander Kornienkocbe8d162017-05-04 15:34:23 +000063 const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);
64 return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +000065}
66
67bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
68 switch (OperatorKind) {
69 case OO_New:
70 case OO_Delete: // Fall-through on purpose.
71 case OO_Array_New:
72 case OO_Array_Delete:
73 case OO_ArrowStar:
74 case OO_Arrow:
75 case OO_Call:
76 case OO_Subscript:
77 return false;
78
79 default:
80 return true;
81 }
82}
83
84bool areParensNeededForStatement(const Stmt *Statement) {
Alexander Kornienkocbe8d162017-05-04 15:34:23 +000085 if (const auto *OperatorCall = dyn_cast<CXXOperatorCallExpr>(Statement)) {
86 return areParensNeededForOverloadedOperator(OperatorCall->getOperator());
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +000087 }
88
Alexander Kornienkocbe8d162017-05-04 15:34:23 +000089 return isa<BinaryOperator>(Statement) || isa<UnaryOperator>(Statement);
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +000090}
91
Alexander Kornienkocbe8d162017-05-04 15:34:23 +000092void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
93 const ImplicitCastExpr *Cast, const Stmt *Parent,
94 ASTContext &Context) {
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +000095 // In case of expressions like (! integer), we should remove the redundant not
96 // operator and use inverted comparison (integer == 0).
97 bool InvertComparison =
Alexander Kornienkocbe8d162017-05-04 15:34:23 +000098 Parent != nullptr && isUnaryLogicalNotOperator(Parent);
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +000099 if (InvertComparison) {
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000100 SourceLocation ParentStartLoc = Parent->getLocStart();
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000101 SourceLocation ParentEndLoc =
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000102 cast<UnaryOperator>(Parent)->getSubExpr()->getLocStart();
103 Diag << FixItHint::CreateRemoval(
104 CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc));
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000105
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000106 Parent = Context.getParents(*Parent)[0].get<Stmt>();
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000107 }
108
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000109 const Expr *SubExpr = Cast->getSubExpr();
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000110
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000111 bool NeedInnerParens = areParensNeededForStatement(SubExpr);
112 bool NeedOuterParens =
113 Parent != nullptr && areParensNeededForStatement(Parent);
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000114
115 std::string StartLocInsertion;
116
117 if (NeedOuterParens) {
118 StartLocInsertion += "(";
119 }
120 if (NeedInnerParens) {
121 StartLocInsertion += "(";
122 }
123
124 if (!StartLocInsertion.empty()) {
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000125 Diag << FixItHint::CreateInsertion(Cast->getLocStart(), StartLocInsertion);
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000126 }
127
128 std::string EndLocInsertion;
129
130 if (NeedInnerParens) {
131 EndLocInsertion += ")";
132 }
133
134 if (InvertComparison) {
135 EndLocInsertion += " == ";
136 } else {
137 EndLocInsertion += " != ";
138 }
139
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000140 EndLocInsertion += getZeroLiteralToCompareWithForType(
141 Cast->getCastKind(), SubExpr->getType(), Context);
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000142
143 if (NeedOuterParens) {
144 EndLocInsertion += ")";
145 }
146
147 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000148 Cast->getLocEnd(), 0, Context.getSourceManager(), Context.getLangOpts());
149 Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000150}
151
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000152StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression,
153 ASTContext &Context) {
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000154 if (isNULLMacroExpansion(Expression, Context)) {
155 return "false";
156 }
157
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000158 if (const auto *IntLit = dyn_cast<IntegerLiteral>(Expression)) {
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000159 return (IntLit->getValue() == 0) ? "false" : "true";
160 }
161
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000162 if (const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000163 llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
164 FloatLitAbsValue.clearSign();
165 return (FloatLitAbsValue.bitcastToAPInt() == 0) ? "false" : "true";
166 }
167
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000168 if (const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) {
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000169 return (CharLit->getValue() == 0) ? "false" : "true";
170 }
171
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000172 if (isa<StringLiteral>(Expression->IgnoreCasts())) {
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000173 return "true";
174 }
175
176 return StringRef();
177}
178
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000179void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
180 const ImplicitCastExpr *Cast,
181 ASTContext &Context, StringRef OtherType) {
182 const Expr *SubExpr = Cast->getSubExpr();
183 bool NeedParens = !isa<ParenExpr>(SubExpr);
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000184
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000185 Diag << FixItHint::CreateInsertion(
186 Cast->getLocStart(),
187 (Twine("static_cast<") + OtherType + ">" + (NeedParens ? "(" : ""))
188 .str());
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000189
190 if (NeedParens) {
191 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000192 Cast->getLocEnd(), 0, Context.getSourceManager(),
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000193 Context.getLangOpts());
194
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000195 Diag << FixItHint::CreateInsertion(EndLoc, ")");
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000196 }
197}
198
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000199StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,
200 QualType DestType, ASTContext &Context) {
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000201 // Prior to C++11, false literal could be implicitly converted to pointer.
202 if (!Context.getLangOpts().CPlusPlus11 &&
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000203 (DestType->isPointerType() || DestType->isMemberPointerType()) &&
204 BoolLiteral->getValue() == false) {
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000205 return "0";
206 }
207
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000208 if (DestType->isFloatingType()) {
209 if (Context.hasSameType(DestType, Context.FloatTy)) {
210 return BoolLiteral->getValue() ? "1.0f" : "0.0f";
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000211 }
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000212 return BoolLiteral->getValue() ? "1.0" : "0.0";
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000213 }
214
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000215 if (DestType->isUnsignedIntegerType()) {
216 return BoolLiteral->getValue() ? "1u" : "0u";
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000217 }
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000218 return BoolLiteral->getValue() ? "1" : "0";
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000219}
220
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000221bool isAllowedConditionalCast(const ImplicitCastExpr *Cast,
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000222 ASTContext &Context) {
Alexander Kornienko4e001b52017-04-29 12:06:45 +0000223 std::queue<const Stmt *> Q;
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000224 Q.push(Cast);
Alexander Kornienko4e001b52017-04-29 12:06:45 +0000225 while (!Q.empty()) {
226 for (const auto &N : Context.getParents(*Q.front())) {
227 const Stmt *S = N.get<Stmt>();
228 if (!S)
229 return false;
230 if (isa<IfStmt>(S) || isa<ConditionalOperator>(S))
231 return true;
232 if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000233 isUnaryLogicalNotOperator(S) ||
Alexander Kornienko4e001b52017-04-29 12:06:45 +0000234 (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {
235 Q.push(S);
236 } else {
237 return false;
238 }
239 }
240 Q.pop();
241 }
242 return false;
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000243}
244
245} // anonymous namespace
246
Haojian Wu80c1c9f2016-08-16 11:15:05 +0000247ImplicitBoolCastCheck::ImplicitBoolCastCheck(StringRef Name,
248 ClangTidyContext *Context)
249 : ClangTidyCheck(Name, Context),
250 AllowConditionalIntegerCasts(
251 Options.get("AllowConditionalIntegerCasts", false)),
252 AllowConditionalPointerCasts(
253 Options.get("AllowConditionalPointerCasts", false)) {}
254
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000255void ImplicitBoolCastCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Haojian Wu80c1c9f2016-08-16 11:15:05 +0000256 Options.store(Opts, "AllowConditionalIntegerCasts",
257 AllowConditionalIntegerCasts);
258 Options.store(Opts, "AllowConditionalPointerCasts",
259 AllowConditionalPointerCasts);
260}
261
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000262void ImplicitBoolCastCheck::registerMatchers(MatchFinder *Finder) {
263 // This check doesn't make much sense if we run it on language without
264 // built-in bool support.
265 if (!getLangOpts().Bool) {
266 return;
267 }
268
Alexander Kornienkob92cb072017-05-04 15:34:31 +0000269 auto exceptionCases = expr(
270 anyOf(hasParent(explicitCastExpr()),
271 allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
272 isInTemplateInstantiation(), hasAncestor(functionTemplateDecl())));
273
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000274 Finder->addMatcher(
275 implicitCastExpr(
276 // Exclude cases common to implicit cast to and from bool.
Alexander Kornienkob92cb072017-05-04 15:34:31 +0000277 unless(exceptionCases),
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000278 // Exclude case of using if or while statements with variable
279 // declaration, e.g.:
280 // if (int var = functionCall()) {}
281 unless(
282 hasParent(stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
283 anyOf(hasCastKind(CK_IntegralToBoolean),
284 hasCastKind(CK_FloatingToBoolean),
285 hasCastKind(CK_PointerToBoolean),
286 hasCastKind(CK_MemberPointerToBoolean)),
287 // Retrive also parent statement, to check if we need additional
288 // parens in replacement.
289 anyOf(hasParent(stmt().bind("parentStmt")), anything()))
290 .bind("implicitCastToBool"),
291 this);
292
Alexander Kornienkob92cb072017-05-04 15:34:31 +0000293 auto implicitCastFromBool = implicitCastExpr(
294 unless(exceptionCases),
295 anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
296 // Prior to C++11 cast from bool literal to pointer was allowed.
297 allOf(anyOf(hasCastKind(CK_NullToPointer),
298 hasCastKind(CK_NullToMemberPointer)),
299 hasSourceExpression(cxxBoolLiteral()))),
300 hasSourceExpression(expr(hasType(booleanType()))));
301
302 auto boolComparison = binaryOperator(
303 anyOf(hasOperatorName("=="), hasOperatorName("!=")),
304 hasLHS(implicitCastFromBool), hasRHS(implicitCastFromBool));
305 auto boolOpAssignment = binaryOperator(
306 anyOf(hasOperatorName("|="), hasOperatorName("&=")),
307 hasLHS(expr(hasType(booleanType()))), hasRHS(implicitCastFromBool));
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000308 Finder->addMatcher(
309 implicitCastExpr(
Alexander Kornienkob92cb072017-05-04 15:34:31 +0000310 implicitCastFromBool,
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000311 // Exclude comparisons of bools, as they are always cast to integers
312 // in such context:
313 // bool_expr_a == bool_expr_b
314 // bool_expr_a != bool_expr_b
Alexander Kornienkob92cb072017-05-04 15:34:31 +0000315 unless(hasParent(
316 binaryOperator(anyOf(boolComparison, boolOpAssignment)))),
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000317 // Check also for nested casts, for example: bool -> int -> float.
318 anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
319 anything()))
320 .bind("implicitCastFromBool"),
321 this);
322}
323
324void ImplicitBoolCastCheck::check(const MatchFinder::MatchResult &Result) {
325 if (const auto *CastToBool =
326 Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000327 const auto *Parent = Result.Nodes.getNodeAs<Stmt>("parentStmt");
328 return handleCastToBool(CastToBool, Parent, *Result.Context);
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000329 }
330
331 if (const auto *CastFromBool =
332 Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) {
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000333 const auto *NextImplicitCast =
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000334 Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast");
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000335 return handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000336 }
337}
338
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000339void ImplicitBoolCastCheck::handleCastToBool(const ImplicitCastExpr *Cast,
340 const Stmt *Parent,
341 ASTContext &Context) {
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000342 if (AllowConditionalPointerCasts &&
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000343 (Cast->getCastKind() == CK_PointerToBoolean ||
344 Cast->getCastKind() == CK_MemberPointerToBoolean) &&
345 isAllowedConditionalCast(Cast, Context)) {
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000346 return;
347 }
348
349 if (AllowConditionalIntegerCasts &&
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000350 Cast->getCastKind() == CK_IntegralToBoolean &&
351 isAllowedConditionalCast(Cast, Context)) {
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000352 return;
353 }
354
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000355 auto Diag = diag(Cast->getLocStart(), "implicit cast %0 -> bool")
356 << Cast->getSubExpr()->getType();
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000357
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000358 StringRef EquivalentLiteral =
359 getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context);
360 if (!EquivalentLiteral.empty()) {
361 Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000362 } else {
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000363 fixGenericExprCastToBool(Diag, Cast, Parent, Context);
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000364 }
365}
366
367void ImplicitBoolCastCheck::handleCastFromBool(
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000368 const ImplicitCastExpr *Cast, const ImplicitCastExpr *NextImplicitCast,
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000369 ASTContext &Context) {
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000370 QualType DestType =
371 NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
372 auto Diag = diag(Cast->getLocStart(), "implicit cast bool -> %0") << DestType;
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000373
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000374 if (const auto *BoolLiteral =
375 dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr())) {
376 Diag << tooling::fixit::createReplacement(
377 *Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context));
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000378 } else {
Alexander Kornienkocbe8d162017-05-04 15:34:23 +0000379 fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString());
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000380 }
381}
382
Etienne Bergeron456177b2016-05-02 18:00:29 +0000383} // namespace readability
Piotr Dziwinski7f1b5092015-10-25 15:31:25 +0000384} // namespace tidy
385} // namespace clang