cURL: follow redirects
[LibreOffice.git] / compilerplugins / clang / nullptr.cxx
blob3ab32031e6f2a156383af3a004d30821a0be445c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <cassert>
11 #include <cstdlib>
12 #include <limits>
13 #include <set>
15 #include "plugin.hxx"
17 namespace {
19 char const * kindName(Expr::NullPointerConstantKind kind) {
20 switch (kind) {
21 case Expr::NPCK_ZeroExpression:
22 return "ZeroExpression";
23 case Expr::NPCK_ZeroLiteral:
24 return "ZeroLiteral";
25 case Expr::NPCK_CXX11_nullptr:
26 return "CXX11_nullptr";
27 case Expr::NPCK_GNUNull:
28 return "GNUNull";
29 case Expr::NPCK_NotNull:
30 assert(false); // cannot happen
31 // fall through
32 default:
33 std::abort();
37 bool isAnyKindOfPointerType(QualType type) {
38 return type->isAnyPointerType() || type->isFunctionPointerType()
39 || type->isMemberPointerType();
42 bool isNullPointerCast(CastExpr const * expr) {
43 switch (expr->getCastKind()) {
44 case CK_NullToPointer:
45 case CK_NullToMemberPointer:
46 return true;
47 default:
48 return false;
52 class Nullptr:
53 public RecursiveASTVisitor<Nullptr>, public loplugin::RewritePlugin
55 public:
56 explicit Nullptr(InstantiationData const & data): RewritePlugin(data) {}
58 void run() override
59 { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
61 bool VisitImplicitCastExpr(CastExpr const * expr);
63 bool VisitGNUNullExpr(GNUNullExpr const * expr);
65 bool VisitBinaryOperator(BinaryOperator const * expr);
67 bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr const * expr);
69 bool VisitParmVarDecl(ParmVarDecl const * decl);
71 bool TraverseConstructorInitializer(CXXCtorInitializer * init);
73 bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
75 // bool shouldVisitTemplateInstantiations() const { return true; }
77 private:
78 bool isInLokIncludeFile(SourceLocation spellingLocation) const;
80 bool isFromCIncludeFile(SourceLocation spellingLocation) const;
82 bool isSharedCAndCppCode(SourceLocation location) const;
84 void visitCXXCtorInitializer(CXXCtorInitializer const * init);
86 void handleZero(Expr const * expr);
88 void handleNull(
89 Expr const * expr, char const * castKind,
90 Expr::NullPointerConstantKind nullPointerKind);
92 void rewriteOrWarn(
93 Expr const * expr, char const * castKind,
94 Expr::NullPointerConstantKind nullPointerKind,
95 char const * replacement);
97 std::set<Expr const *> gnuNulls_;
98 unsigned int externCContexts_ = 0;
101 bool Nullptr::VisitImplicitCastExpr(CastExpr const * expr) {
102 if (ignoreLocation(expr)) {
103 return true;
105 if (!isNullPointerCast(expr)) {
106 return true;
108 Expr::NullPointerConstantKind k = expr->isNullPointerConstant(
109 compiler.getASTContext(), Expr::NPC_ValueDependentIsNotNull);
110 switch (k) {
111 case Expr::NPCK_NotNull:
112 k = expr->isNullPointerConstant(
113 compiler.getASTContext(), Expr::NPC_ValueDependentIsNull);
114 switch (k) {
115 case Expr::NPCK_NotNull:
116 break;
117 case Expr::NPCK_ZeroExpression:
118 case Expr::NPCK_ZeroLiteral:
119 report(
120 DiagnosticsEngine::Warning,
121 "suspicious ValueDependentIsNull %0", expr->getLocStart())
122 << kindName(k) << expr->getSourceRange();
123 break;
124 default:
125 assert(false); // cannot happen
127 break;
128 case Expr::NPCK_CXX11_nullptr:
129 break;
130 default:
131 handleNull(expr->getSubExpr(), expr->getCastKindName(), k);
132 break;
134 return true;
137 bool Nullptr::VisitGNUNullExpr(GNUNullExpr const * expr) {
138 if (ignoreLocation(expr)) {
139 return true;
141 handleNull(expr, nullptr, Expr::NPCK_GNUNull);
142 return true;
145 bool Nullptr::VisitBinaryOperator(BinaryOperator const * expr) {
146 if (ignoreLocation(expr)) {
147 return true;
149 Expr const * e;
150 switch (expr->getOpcode()) {
151 case BO_EQ:
152 case BO_NE:
153 if (isAnyKindOfPointerType(expr->getRHS()->getType())) {
154 e = expr->getLHS();
155 break;
157 // fall through
158 case BO_Assign:
159 if (isAnyKindOfPointerType(expr->getLHS()->getType())) {
160 e = expr->getRHS();
161 break;
163 // fall through
164 default:
165 return true;
167 handleZero(e);
168 return true;
171 bool Nullptr::VisitCXXOperatorCallExpr(CXXOperatorCallExpr const * expr) {
172 if (ignoreLocation(expr)) {
173 return true;
175 Expr const * e;
176 switch (expr->getOperator()) {
177 case OO_EqualEqual:
178 case OO_ExclaimEqual:
179 if (isAnyKindOfPointerType(expr->getArg(1)->getType())) {
180 e = expr->getArg(0);
181 break;
183 // fall through
184 case OO_Equal:
185 if (isAnyKindOfPointerType(expr->getArg(0)->getType())) {
186 e = expr->getArg(1);
187 break;
189 // fall through
190 default:
191 return true;
193 handleZero(e);
194 return true;
197 bool Nullptr::VisitParmVarDecl(ParmVarDecl const * decl) {
198 if (ignoreLocation(decl)) {
199 return true;
201 if (!isAnyKindOfPointerType(decl->getType())) {
202 return true;
204 auto e = decl->getDefaultArg();
205 if (e == nullptr) {
206 return true;
208 handleZero(e);
209 return true;
212 bool Nullptr::TraverseConstructorInitializer(CXXCtorInitializer * init) {
213 visitCXXCtorInitializer(init);
214 return RecursiveASTVisitor::TraverseConstructorInitializer(init);
217 bool Nullptr::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
218 assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
219 ++externCContexts_;
220 bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
221 assert(externCContexts_ != 0);
222 --externCContexts_;
223 return ret;
226 bool Nullptr::isInLokIncludeFile(SourceLocation spellingLocation) const {
227 return compiler.getSourceManager().getFilename(spellingLocation)
228 .startswith(SRCDIR "/include/LibreOfficeKit/");
231 bool Nullptr::isFromCIncludeFile(SourceLocation spellingLocation) const {
232 return !compiler.getSourceManager().isInMainFile(spellingLocation)
233 && (StringRef(
234 compiler.getSourceManager().getPresumedLoc(spellingLocation)
235 .getFilename())
236 .endswith(".h"));
239 bool Nullptr::isSharedCAndCppCode(SourceLocation location) const {
240 // Assume that code is intended to be shared between C and C++ if it comes
241 // from an include file ending in .h, and is either in an extern "C" context
242 // or the body of a macro definition:
243 return
244 isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
245 && (externCContexts_ != 0
246 || compiler.getSourceManager().isMacroBodyExpansion(location));
249 void Nullptr::visitCXXCtorInitializer(CXXCtorInitializer const * init) {
250 if (!init->isWritten()) {
251 return;
253 auto e = init->getInit();
254 if (ignoreLocation(e)) {
255 return;
257 auto d = init->getAnyMember();
258 if (d == nullptr || !isAnyKindOfPointerType(d->getType())) {
259 return;
261 if (auto e2 = dyn_cast<ParenListExpr>(e)) {
262 if (e2->getNumExprs() != 1) {
263 return;
265 e = e2->getExpr(0);
266 } else if (auto e2 = dyn_cast<InitListExpr>(e)) {
267 if (e2->getNumInits() != 1) {
268 return;
270 e = e2->getInit(0);
272 handleZero(e);
275 void Nullptr::handleZero(Expr const * expr) {
276 //TODO: detect NPCK_ZeroExpression where appropriate
277 // Filter out ImplicitCastExpr that will be handled by
278 // VisitImplicitCastExpr:
279 if (auto ice = dyn_cast<ImplicitCastExpr>(expr)) {
280 if (isNullPointerCast(ice)) {
281 return;
284 auto const lit = dyn_cast<IntegerLiteral>(expr->IgnoreParenImpCasts());
285 if (lit != nullptr && !lit->getValue().getBoolValue()) {
286 handleNull(expr, nullptr, Expr::NPCK_ZeroLiteral);
290 void Nullptr::handleNull(
291 Expr const * expr, char const * castKind,
292 Expr::NullPointerConstantKind nullPointerKind)
294 auto e = expr;
295 SourceLocation loc;
296 for (;;) {
297 e = e->IgnoreImpCasts();
298 loc = e->getLocStart();
299 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
300 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
302 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
303 if (Lexer::getImmediateMacroName(
304 loc, compiler.getSourceManager(), compiler.getLangOpts())
305 == "NULL")
307 if (!compiler.getLangOpts().CPlusPlus) {
308 //TODO: if !castKind, warn if NULL is passed into fn call
309 // ellipsis, cast to void*
310 return;
312 loc = compiler.getSourceManager()
313 .getImmediateExpansionRange(loc).first;
314 if (ignoreLocation(
315 compiler.getSourceManager().getSpellingLoc(loc)))
317 return;
319 if (isInUnoIncludeFile(
320 compiler.getSourceManager().getSpellingLoc(loc))
321 || isInLokIncludeFile(
322 compiler.getSourceManager().getSpellingLoc(loc))
323 || isSharedCAndCppCode(loc))
325 //TODO: if !castKind, warn if NULL is passed into fn call
326 // ellipsis, cast to void*
327 return;
329 } else if (ignoreLocation(
330 compiler.getSourceManager().getSpellingLoc(loc)))
332 return;
335 ParenExpr const * pe = dyn_cast<ParenExpr>(e);
336 if (pe == nullptr) {
337 break;
339 e = pe->getSubExpr();
341 if (nullPointerKind == Expr::NPCK_GNUNull) {
342 if (castKind == nullptr) {
343 if (gnuNulls_.erase(expr) == 1) {
344 return;
346 } else {
347 auto const ok = gnuNulls_.insert(expr).second;
348 assert(ok); (void) ok;
351 auto const asMacro = !compiler.getLangOpts().CPlusPlus
352 || isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(loc))
353 || isInLokIncludeFile(compiler.getSourceManager().getSpellingLoc(loc))
354 || isSharedCAndCppCode(loc);
355 assert(!asMacro || nullPointerKind != Expr::NPCK_GNUNull);
356 rewriteOrWarn(e, castKind, nullPointerKind, asMacro ? "NULL" : "nullptr");
359 void Nullptr::rewriteOrWarn(
360 Expr const * expr, char const * castKind,
361 Expr::NullPointerConstantKind nullPointerKind, char const * replacement)
363 if (rewriter != nullptr) {
364 SourceLocation locStart(expr->getLocStart());
365 while (compiler.getSourceManager().isMacroArgExpansion(locStart)) {
366 locStart = compiler.getSourceManager()
367 .getImmediateMacroCallerLoc(locStart);
369 if (compiler.getLangOpts().CPlusPlus
370 && compiler.getSourceManager().isMacroBodyExpansion(locStart)
371 && (Lexer::getImmediateMacroName(
372 locStart, compiler.getSourceManager(),
373 compiler.getLangOpts())
374 == "NULL"))
376 locStart = compiler.getSourceManager().getImmediateExpansionRange(
377 locStart).first;
379 SourceLocation locEnd(expr->getLocEnd());
380 while (compiler.getSourceManager().isMacroArgExpansion(locEnd)) {
381 locEnd = compiler.getSourceManager()
382 .getImmediateMacroCallerLoc(locEnd);
384 if (compiler.getLangOpts().CPlusPlus
385 && compiler.getSourceManager().isMacroBodyExpansion(locEnd)
386 && (Lexer::getImmediateMacroName(
387 locEnd, compiler.getSourceManager(),
388 compiler.getLangOpts())
389 == "NULL"))
391 locEnd = compiler.getSourceManager().getImmediateExpansionRange(
392 locEnd).first;
394 if (replaceText(SourceRange(compiler.getSourceManager().getSpellingLoc(locStart), compiler.getSourceManager().getSpellingLoc(locEnd)), replacement)) {
395 return;
398 if (castKind == nullptr) {
399 report(DiagnosticsEngine::Warning, "%0 -> %1", expr->getLocStart())
400 << kindName(nullPointerKind) << replacement
401 << expr->getSourceRange();
402 } else {
403 report(
404 DiagnosticsEngine::Warning, "%0 ValueDependentIsNotNull %1 -> %2",
405 expr->getLocStart())
406 << castKind << kindName(nullPointerKind) << replacement
407 << expr->getSourceRange();
411 loplugin::Plugin::Registration<Nullptr> X("nullptr", true);
415 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */