bump product version to 6.4.0.3
[LibreOffice.git] / compilerplugins / clang / nullptr.cxx
blobb4a40b408a6c735f0c9d5c4b1e0ee773b19c29ad
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 break; // cannot happen
34 llvm_unreachable("unknown null pointer kind");
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 loplugin::FilteringRewritePlugin<Nullptr>
55 public:
56 explicit Nullptr(loplugin::InstantiationData const & data):
57 FilteringRewritePlugin(data) {}
59 void run() override
60 { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
62 bool VisitImplicitCastExpr(CastExpr const * expr);
64 bool VisitGNUNullExpr(GNUNullExpr const * expr);
66 bool VisitBinaryOperator(BinaryOperator const * expr);
68 bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr const * expr);
70 bool VisitParmVarDecl(ParmVarDecl const * decl);
72 bool TraverseConstructorInitializer(CXXCtorInitializer * init);
74 bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
76 bool TraverseInitListExpr(InitListExpr * expr, DataRecursionQueue * queue = nullptr);
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", compat::getBeginLoc(expr))
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::TraverseInitListExpr(InitListExpr * expr, DataRecursionQueue * queue) {
235 return WalkUpFromInitListExpr(expr)
236 && TraverseSynOrSemInitListExpr(
237 expr->isSemanticForm() ? expr : expr->getSemanticForm(), queue);
240 bool Nullptr::isInLokIncludeFile(SourceLocation spellingLocation) const {
241 return loplugin::hasPathnamePrefix(
242 getFilenameOfLocation(spellingLocation),
243 SRCDIR "/include/LibreOfficeKit/");
246 bool Nullptr::isFromCIncludeFile(SourceLocation spellingLocation) const {
247 return !compiler.getSourceManager().isInMainFile(spellingLocation)
248 && (StringRef(
249 compiler.getSourceManager().getPresumedLoc(spellingLocation)
250 .getFilename())
251 .endswith(".h"));
254 bool Nullptr::isSharedCAndCppCode(SourceLocation location) const {
255 // Assume that code is intended to be shared between C and C++ if it comes
256 // from an include file ending in .h, and is either in an extern "C" context
257 // or the body of a macro definition:
258 return
259 isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
260 && (externCContexts_ != 0
261 || compiler.getSourceManager().isMacroBodyExpansion(location));
264 void Nullptr::visitCXXCtorInitializer(CXXCtorInitializer const * init) {
265 if (!init->isWritten()) {
266 return;
268 auto e = init->getInit();
269 if (ignoreLocation(e)) {
270 return;
272 auto d = init->getAnyMember();
273 if (d == nullptr || !isAnyKindOfPointerType(d->getType())) {
274 return;
276 if (auto e2 = dyn_cast<ParenListExpr>(e)) {
277 if (e2->getNumExprs() != 1) {
278 return;
280 e = e2->getExpr(0);
281 } else if (auto e2 = dyn_cast<InitListExpr>(e)) {
282 if (e2->getNumInits() != 1) {
283 return;
285 e = e2->getInit(0);
287 handleZero(e);
290 void Nullptr::handleZero(Expr const * expr) {
291 //TODO: detect NPCK_ZeroExpression where appropriate
292 // Filter out ImplicitCastExpr that will be handled by
293 // VisitImplicitCastExpr:
294 if (auto ice = dyn_cast<ImplicitCastExpr>(expr)) {
295 if (isNullPointerCast(ice)) {
296 return;
299 auto const lit = dyn_cast<IntegerLiteral>(expr->IgnoreParenImpCasts());
300 if (lit != nullptr && !lit->getValue().getBoolValue()) {
301 handleNull(expr, nullptr, Expr::NPCK_ZeroLiteral);
305 void Nullptr::handleNull(
306 Expr const * expr, char const * castKind,
307 Expr::NullPointerConstantKind nullPointerKind)
309 auto e = expr;
310 SourceLocation loc;
311 for (;;) {
312 e = e->IgnoreImpCasts();
313 loc = compat::getBeginLoc(e);
314 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
315 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
317 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
318 if (Lexer::getImmediateMacroName(
319 loc, compiler.getSourceManager(), compiler.getLangOpts())
320 == "NULL")
322 if (!compiler.getLangOpts().CPlusPlus) {
323 //TODO: if !castKind, warn if NULL is passed into fn call
324 // ellipsis, cast to void*
325 return;
327 loc = compat::getImmediateExpansionRange(compiler.getSourceManager(), loc).first;
328 if (ignoreLocation(
329 compiler.getSourceManager().getSpellingLoc(loc)))
331 return;
333 if (isInUnoIncludeFile(
334 compiler.getSourceManager().getSpellingLoc(loc))
335 || isInLokIncludeFile(
336 compiler.getSourceManager().getSpellingLoc(loc))
337 || isSharedCAndCppCode(loc))
339 //TODO: if !castKind, warn if NULL is passed into fn call
340 // ellipsis, cast to void*
341 return;
343 } else if (ignoreLocation(
344 compiler.getSourceManager().getSpellingLoc(loc)))
346 return;
349 ParenExpr const * pe = dyn_cast<ParenExpr>(e);
350 if (pe == nullptr) {
351 break;
353 e = pe->getSubExpr();
355 if (nullPointerKind == Expr::NPCK_GNUNull) {
356 if (castKind == nullptr) {
357 if (gnuNulls_.erase(expr) == 1) {
358 return;
360 } else {
361 auto const ok = gnuNulls_.insert(expr).second;
362 assert(ok); (void) ok;
365 auto const asMacro = !compiler.getLangOpts().CPlusPlus
366 || isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(loc))
367 || isInLokIncludeFile(compiler.getSourceManager().getSpellingLoc(loc))
368 || isSharedCAndCppCode(loc);
369 assert(!asMacro || nullPointerKind != Expr::NPCK_GNUNull);
370 rewriteOrWarn(e, castKind, nullPointerKind, asMacro ? "NULL" : "nullptr");
373 void Nullptr::rewriteOrWarn(
374 Expr const * expr, char const * castKind,
375 Expr::NullPointerConstantKind nullPointerKind, char const * replacement)
377 if (rewriter != nullptr) {
378 SourceLocation locStart(compat::getBeginLoc(expr));
379 while (compiler.getSourceManager().isMacroArgExpansion(locStart)) {
380 locStart = compiler.getSourceManager()
381 .getImmediateMacroCallerLoc(locStart);
383 if (compiler.getLangOpts().CPlusPlus
384 && compiler.getSourceManager().isMacroBodyExpansion(locStart)
385 && (Lexer::getImmediateMacroName(
386 locStart, compiler.getSourceManager(),
387 compiler.getLangOpts())
388 == "NULL"))
390 locStart = compat::getImmediateExpansionRange(compiler.getSourceManager(), locStart)
391 .first;
393 SourceLocation locEnd(compat::getEndLoc(expr));
394 while (compiler.getSourceManager().isMacroArgExpansion(locEnd)) {
395 locEnd = compiler.getSourceManager()
396 .getImmediateMacroCallerLoc(locEnd);
398 if (compiler.getLangOpts().CPlusPlus
399 && compiler.getSourceManager().isMacroBodyExpansion(locEnd)
400 && (Lexer::getImmediateMacroName(
401 locEnd, compiler.getSourceManager(),
402 compiler.getLangOpts())
403 == "NULL"))
405 locEnd = compat::getImmediateExpansionRange(compiler.getSourceManager(), locEnd).first;
407 if (replaceText(SourceRange(compiler.getSourceManager().getSpellingLoc(locStart), compiler.getSourceManager().getSpellingLoc(locEnd)), replacement)) {
408 return;
411 if (castKind == nullptr) {
412 report(DiagnosticsEngine::Warning, "%0 -> %1", compat::getBeginLoc(expr))
413 << kindName(nullPointerKind) << replacement
414 << expr->getSourceRange();
415 } else {
416 report(
417 DiagnosticsEngine::Warning, "%0 ValueDependentIsNotNull %1 -> %2",
418 compat::getBeginLoc(expr))
419 << castKind << kindName(nullPointerKind) << replacement
420 << expr->getSourceRange();
424 loplugin::Plugin::Registration<Nullptr> X("nullptr", true);
428 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */