Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / compilerplugins / clang / nullptr.cxx
blobbc1f7b505243da1ec214c2654ef2efb65fc0645a
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 "check.hxx"
16 #include "compat.hxx"
17 #include "plugin.hxx"
19 namespace {
21 char const * kindName(Expr::NullPointerConstantKind kind) {
22 switch (kind) {
23 case Expr::NPCK_ZeroExpression:
24 return "ZeroExpression";
25 case Expr::NPCK_ZeroLiteral:
26 return "ZeroLiteral";
27 case Expr::NPCK_CXX11_nullptr:
28 return "CXX11_nullptr";
29 case Expr::NPCK_GNUNull:
30 return "GNUNull";
31 case Expr::NPCK_NotNull:
32 assert(false); // cannot happen
33 // fall through
34 default:
35 std::abort();
39 bool isAnyKindOfPointerType(QualType type) {
40 return type->isAnyPointerType() || type->isFunctionPointerType()
41 || type->isMemberPointerType();
44 bool isNullPointerCast(CastExpr const * expr) {
45 switch (expr->getCastKind()) {
46 case CK_NullToPointer:
47 case CK_NullToMemberPointer:
48 return true;
49 default:
50 return false;
54 class Nullptr:
55 public RecursiveASTVisitor<Nullptr>, public loplugin::RewritePlugin
57 public:
58 explicit Nullptr(loplugin::InstantiationData const & data):
59 RewritePlugin(data) {}
61 void run() override
62 { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
64 bool VisitImplicitCastExpr(CastExpr const * expr);
66 bool VisitGNUNullExpr(GNUNullExpr const * expr);
68 bool VisitBinaryOperator(BinaryOperator const * expr);
70 bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr const * expr);
72 bool VisitParmVarDecl(ParmVarDecl const * decl);
74 bool TraverseConstructorInitializer(CXXCtorInitializer * init);
76 bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
78 // bool shouldVisitTemplateInstantiations() const { return true; }
80 private:
81 bool isInLokIncludeFile(SourceLocation spellingLocation) const;
83 bool isFromCIncludeFile(SourceLocation spellingLocation) const;
85 bool isSharedCAndCppCode(SourceLocation location) const;
87 void visitCXXCtorInitializer(CXXCtorInitializer const * init);
89 void handleZero(Expr const * expr);
91 void handleNull(
92 Expr const * expr, char const * castKind,
93 Expr::NullPointerConstantKind nullPointerKind);
95 void rewriteOrWarn(
96 Expr const * expr, char const * castKind,
97 Expr::NullPointerConstantKind nullPointerKind,
98 char const * replacement);
100 std::set<Expr const *> gnuNulls_;
101 unsigned int externCContexts_ = 0;
104 bool Nullptr::VisitImplicitCastExpr(CastExpr const * expr) {
105 if (ignoreLocation(expr)) {
106 return true;
108 if (!isNullPointerCast(expr)) {
109 return true;
111 Expr::NullPointerConstantKind k = expr->isNullPointerConstant(
112 compiler.getASTContext(), Expr::NPC_ValueDependentIsNotNull);
113 switch (k) {
114 case Expr::NPCK_NotNull:
115 k = expr->isNullPointerConstant(
116 compiler.getASTContext(), Expr::NPC_ValueDependentIsNull);
117 switch (k) {
118 case Expr::NPCK_NotNull:
119 break;
120 case Expr::NPCK_ZeroExpression:
121 case Expr::NPCK_ZeroLiteral:
122 report(
123 DiagnosticsEngine::Warning,
124 "suspicious ValueDependentIsNull %0", expr->getLocStart())
125 << kindName(k) << expr->getSourceRange();
126 break;
127 default:
128 assert(false); // cannot happen
130 break;
131 case Expr::NPCK_CXX11_nullptr:
132 break;
133 default:
134 if (loplugin::TypeCheck(expr->getType()).Typedef("locale_t")
135 .GlobalNamespace())
137 break; // POSIX locale_t is left unspecified
139 handleNull(expr->getSubExpr(), expr->getCastKindName(), k);
140 break;
142 return true;
145 bool Nullptr::VisitGNUNullExpr(GNUNullExpr const * expr) {
146 if (ignoreLocation(expr)) {
147 return true;
149 handleNull(expr, nullptr, Expr::NPCK_GNUNull);
150 return true;
153 bool Nullptr::VisitBinaryOperator(BinaryOperator const * expr) {
154 if (ignoreLocation(expr)) {
155 return true;
157 Expr const * e;
158 switch (expr->getOpcode()) {
159 case BO_EQ:
160 case BO_NE:
161 if (isAnyKindOfPointerType(expr->getRHS()->getType())) {
162 e = expr->getLHS();
163 break;
165 // fall through
166 case BO_Assign:
167 if (isAnyKindOfPointerType(expr->getLHS()->getType())) {
168 e = expr->getRHS();
169 break;
171 // fall through
172 default:
173 return true;
175 handleZero(e);
176 return true;
179 bool Nullptr::VisitCXXOperatorCallExpr(CXXOperatorCallExpr const * expr) {
180 if (ignoreLocation(expr)) {
181 return true;
183 Expr const * e;
184 switch (expr->getOperator()) {
185 case OO_EqualEqual:
186 case OO_ExclaimEqual:
187 if (isAnyKindOfPointerType(expr->getArg(1)->getType())) {
188 e = expr->getArg(0);
189 break;
191 // fall through
192 case OO_Equal:
193 if (isAnyKindOfPointerType(expr->getArg(0)->getType())) {
194 e = expr->getArg(1);
195 break;
197 // fall through
198 default:
199 return true;
201 handleZero(e);
202 return true;
205 bool Nullptr::VisitParmVarDecl(ParmVarDecl const * decl) {
206 if (ignoreLocation(decl)) {
207 return true;
209 if (!isAnyKindOfPointerType(decl->getType())) {
210 return true;
212 auto e = decl->getDefaultArg();
213 if (e == nullptr) {
214 return true;
216 handleZero(e);
217 return true;
220 bool Nullptr::TraverseConstructorInitializer(CXXCtorInitializer * init) {
221 visitCXXCtorInitializer(init);
222 return RecursiveASTVisitor::TraverseConstructorInitializer(init);
225 bool Nullptr::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
226 assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
227 ++externCContexts_;
228 bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
229 assert(externCContexts_ != 0);
230 --externCContexts_;
231 return ret;
234 bool Nullptr::isInLokIncludeFile(SourceLocation spellingLocation) const {
235 return loplugin::hasPathnamePrefix(
236 compiler.getSourceManager().getFilename(spellingLocation),
237 SRCDIR "/include/LibreOfficeKit/");
240 bool Nullptr::isFromCIncludeFile(SourceLocation spellingLocation) const {
241 return !compiler.getSourceManager().isInMainFile(spellingLocation)
242 && (StringRef(
243 compiler.getSourceManager().getPresumedLoc(spellingLocation)
244 .getFilename())
245 .endswith(".h"));
248 bool Nullptr::isSharedCAndCppCode(SourceLocation location) const {
249 // Assume that code is intended to be shared between C and C++ if it comes
250 // from an include file ending in .h, and is either in an extern "C" context
251 // or the body of a macro definition:
252 return
253 isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
254 && (externCContexts_ != 0
255 || compiler.getSourceManager().isMacroBodyExpansion(location));
258 void Nullptr::visitCXXCtorInitializer(CXXCtorInitializer const * init) {
259 if (!init->isWritten()) {
260 return;
262 auto e = init->getInit();
263 if (ignoreLocation(e)) {
264 return;
266 auto d = init->getAnyMember();
267 if (d == nullptr || !isAnyKindOfPointerType(d->getType())) {
268 return;
270 if (auto e2 = dyn_cast<ParenListExpr>(e)) {
271 if (e2->getNumExprs() != 1) {
272 return;
274 e = e2->getExpr(0);
275 } else if (auto e2 = dyn_cast<InitListExpr>(e)) {
276 if (e2->getNumInits() != 1) {
277 return;
279 e = e2->getInit(0);
281 handleZero(e);
284 void Nullptr::handleZero(Expr const * expr) {
285 //TODO: detect NPCK_ZeroExpression where appropriate
286 // Filter out ImplicitCastExpr that will be handled by
287 // VisitImplicitCastExpr:
288 if (auto ice = dyn_cast<ImplicitCastExpr>(expr)) {
289 if (isNullPointerCast(ice)) {
290 return;
293 auto const lit = dyn_cast<IntegerLiteral>(expr->IgnoreParenImpCasts());
294 if (lit != nullptr && !lit->getValue().getBoolValue()) {
295 handleNull(expr, nullptr, Expr::NPCK_ZeroLiteral);
299 void Nullptr::handleNull(
300 Expr const * expr, char const * castKind,
301 Expr::NullPointerConstantKind nullPointerKind)
303 auto e = expr;
304 SourceLocation loc;
305 for (;;) {
306 e = e->IgnoreImpCasts();
307 loc = e->getLocStart();
308 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
309 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
311 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
312 if (Lexer::getImmediateMacroName(
313 loc, compiler.getSourceManager(), compiler.getLangOpts())
314 == "NULL")
316 if (!compiler.getLangOpts().CPlusPlus) {
317 //TODO: if !castKind, warn if NULL is passed into fn call
318 // ellipsis, cast to void*
319 return;
321 loc = compat::getImmediateExpansionRange(compiler.getSourceManager(), loc).first;
322 if (ignoreLocation(
323 compiler.getSourceManager().getSpellingLoc(loc)))
325 return;
327 if (isInUnoIncludeFile(
328 compiler.getSourceManager().getSpellingLoc(loc))
329 || isInLokIncludeFile(
330 compiler.getSourceManager().getSpellingLoc(loc))
331 || isSharedCAndCppCode(loc))
333 //TODO: if !castKind, warn if NULL is passed into fn call
334 // ellipsis, cast to void*
335 return;
337 } else if (ignoreLocation(
338 compiler.getSourceManager().getSpellingLoc(loc)))
340 return;
343 ParenExpr const * pe = dyn_cast<ParenExpr>(e);
344 if (pe == nullptr) {
345 break;
347 e = pe->getSubExpr();
349 if (nullPointerKind == Expr::NPCK_GNUNull) {
350 if (castKind == nullptr) {
351 if (gnuNulls_.erase(expr) == 1) {
352 return;
354 } else {
355 auto const ok = gnuNulls_.insert(expr).second;
356 assert(ok); (void) ok;
359 auto const asMacro = !compiler.getLangOpts().CPlusPlus
360 || isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(loc))
361 || isInLokIncludeFile(compiler.getSourceManager().getSpellingLoc(loc))
362 || isSharedCAndCppCode(loc);
363 assert(!asMacro || nullPointerKind != Expr::NPCK_GNUNull);
364 rewriteOrWarn(e, castKind, nullPointerKind, asMacro ? "NULL" : "nullptr");
367 void Nullptr::rewriteOrWarn(
368 Expr const * expr, char const * castKind,
369 Expr::NullPointerConstantKind nullPointerKind, char const * replacement)
371 if (rewriter != nullptr) {
372 SourceLocation locStart(expr->getLocStart());
373 while (compiler.getSourceManager().isMacroArgExpansion(locStart)) {
374 locStart = compiler.getSourceManager()
375 .getImmediateMacroCallerLoc(locStart);
377 if (compiler.getLangOpts().CPlusPlus
378 && compiler.getSourceManager().isMacroBodyExpansion(locStart)
379 && (Lexer::getImmediateMacroName(
380 locStart, compiler.getSourceManager(),
381 compiler.getLangOpts())
382 == "NULL"))
384 locStart = compat::getImmediateExpansionRange(compiler.getSourceManager(), locStart)
385 .first;
387 SourceLocation locEnd(expr->getLocEnd());
388 while (compiler.getSourceManager().isMacroArgExpansion(locEnd)) {
389 locEnd = compiler.getSourceManager()
390 .getImmediateMacroCallerLoc(locEnd);
392 if (compiler.getLangOpts().CPlusPlus
393 && compiler.getSourceManager().isMacroBodyExpansion(locEnd)
394 && (Lexer::getImmediateMacroName(
395 locEnd, compiler.getSourceManager(),
396 compiler.getLangOpts())
397 == "NULL"))
399 locEnd = compat::getImmediateExpansionRange(compiler.getSourceManager(), locEnd).first;
401 if (replaceText(SourceRange(compiler.getSourceManager().getSpellingLoc(locStart), compiler.getSourceManager().getSpellingLoc(locEnd)), replacement)) {
402 return;
405 if (castKind == nullptr) {
406 report(DiagnosticsEngine::Warning, "%0 -> %1", expr->getLocStart())
407 << kindName(nullPointerKind) << replacement
408 << expr->getSourceRange();
409 } else {
410 report(
411 DiagnosticsEngine::Warning, "%0 ValueDependentIsNotNull %1 -> %2",
412 expr->getLocStart())
413 << castKind << kindName(nullPointerKind) << replacement
414 << expr->getSourceRange();
418 loplugin::Plugin::Registration<Nullptr> X("nullptr", true);
422 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */