Version 24.2.2.2, tag libreoffice-24.2.2.2
[LibreOffice.git] / compilerplugins / clang / nullptr.cxx
blobdcacb1bd2112a7a73e7e53fcb2a60b68a404e039
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", expr->getBeginLoc())
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 if (decl->hasUninstantiatedDefaultArg()) {
213 return true; //TODO
215 auto e = decl->getDefaultArg();
216 if (e == nullptr) {
217 return true;
219 handleZero(e);
220 return true;
223 bool Nullptr::TraverseConstructorInitializer(CXXCtorInitializer * init) {
224 visitCXXCtorInitializer(init);
225 return RecursiveASTVisitor::TraverseConstructorInitializer(init);
228 bool Nullptr::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
229 assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
230 ++externCContexts_;
231 bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
232 assert(externCContexts_ != 0);
233 --externCContexts_;
234 return ret;
237 bool Nullptr::TraverseInitListExpr(InitListExpr * expr, DataRecursionQueue * queue) {
238 return WalkUpFromInitListExpr(expr)
239 && TraverseSynOrSemInitListExpr(
240 expr->isSemanticForm() ? expr : expr->getSemanticForm(), queue);
243 bool Nullptr::isInLokIncludeFile(SourceLocation spellingLocation) const {
244 return loplugin::hasPathnamePrefix(
245 getFilenameOfLocation(spellingLocation),
246 SRCDIR "/include/LibreOfficeKit/");
249 bool Nullptr::isFromCIncludeFile(SourceLocation spellingLocation) const {
250 return !compiler.getSourceManager().isInMainFile(spellingLocation)
251 && (StringRef(
252 compiler.getSourceManager().getPresumedLoc(spellingLocation)
253 .getFilename())
254 .endswith(".h"));
257 bool Nullptr::isSharedCAndCppCode(SourceLocation location) const {
258 // Assume that code is intended to be shared between C and C++ if it comes
259 // from an include file ending in .h, and is either in an extern "C" context
260 // or the body of a macro definition:
261 return
262 isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
263 && (externCContexts_ != 0
264 || compiler.getSourceManager().isMacroBodyExpansion(location));
267 void Nullptr::visitCXXCtorInitializer(CXXCtorInitializer const * init) {
268 if (!init->isWritten()) {
269 return;
271 auto e = init->getInit();
272 if (ignoreLocation(e)) {
273 return;
275 auto d = init->getAnyMember();
276 if (d == nullptr || !isAnyKindOfPointerType(d->getType())) {
277 return;
279 if (auto e2 = dyn_cast<ParenListExpr>(e)) {
280 if (e2->getNumExprs() != 1) {
281 return;
283 e = e2->getExpr(0);
284 } else if (auto e2 = dyn_cast<InitListExpr>(e)) {
285 if (e2->getNumInits() != 1) {
286 return;
288 e = e2->getInit(0);
290 handleZero(e);
293 void Nullptr::handleZero(Expr const * expr) {
294 //TODO: detect NPCK_ZeroExpression where appropriate
295 // Filter out ImplicitCastExpr that will be handled by
296 // VisitImplicitCastExpr:
297 if (auto ice = dyn_cast<ImplicitCastExpr>(expr)) {
298 if (isNullPointerCast(ice)) {
299 return;
302 auto const lit = dyn_cast<IntegerLiteral>(expr->IgnoreParenImpCasts());
303 if (lit != nullptr && !lit->getValue().getBoolValue()) {
304 handleNull(expr, nullptr, Expr::NPCK_ZeroLiteral);
308 void Nullptr::handleNull(
309 Expr const * expr, char const * castKind,
310 Expr::NullPointerConstantKind nullPointerKind)
312 auto e = expr;
313 SourceLocation loc;
314 for (;;) {
315 e = e->IgnoreImpCasts();
316 loc = e->getBeginLoc();
317 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
318 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
320 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
321 if (Lexer::getImmediateMacroName(
322 loc, compiler.getSourceManager(), compiler.getLangOpts())
323 == "NULL")
325 if (!compiler.getLangOpts().CPlusPlus) {
326 //TODO: if !castKind, warn if NULL is passed into fn call
327 // ellipsis, cast to void*
328 return;
330 loc = compat::getImmediateExpansionRange(compiler.getSourceManager(), loc).first;
331 if (ignoreLocation(
332 compiler.getSourceManager().getSpellingLoc(loc)))
334 return;
336 if (isInUnoIncludeFile(
337 compiler.getSourceManager().getSpellingLoc(loc))
338 || isInLokIncludeFile(
339 compiler.getSourceManager().getSpellingLoc(loc))
340 || isSharedCAndCppCode(loc))
342 //TODO: if !castKind, warn if NULL is passed into fn call
343 // ellipsis, cast to void*
344 return;
346 } else if (ignoreLocation(
347 compiler.getSourceManager().getSpellingLoc(loc)))
349 return;
352 ParenExpr const * pe = dyn_cast<ParenExpr>(e);
353 if (pe == nullptr) {
354 break;
356 e = pe->getSubExpr();
358 if (nullPointerKind == Expr::NPCK_GNUNull) {
359 if (castKind == nullptr) {
360 if (gnuNulls_.erase(expr) == 1) {
361 return;
363 } else {
364 auto const ok = gnuNulls_.insert(expr).second;
365 assert(ok); (void) ok;
368 auto const asMacro = !compiler.getLangOpts().CPlusPlus
369 || isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(loc))
370 || isInLokIncludeFile(compiler.getSourceManager().getSpellingLoc(loc))
371 || isSharedCAndCppCode(loc);
372 assert(!asMacro || nullPointerKind != Expr::NPCK_GNUNull);
373 rewriteOrWarn(e, castKind, nullPointerKind, asMacro ? "NULL" : "nullptr");
376 void Nullptr::rewriteOrWarn(
377 Expr const * expr, char const * castKind,
378 Expr::NullPointerConstantKind nullPointerKind, char const * replacement)
380 if (rewriter != nullptr) {
381 SourceLocation locStart(expr->getBeginLoc());
382 while (compiler.getSourceManager().isMacroArgExpansion(locStart)) {
383 locStart = compiler.getSourceManager()
384 .getImmediateMacroCallerLoc(locStart);
386 if (compiler.getLangOpts().CPlusPlus
387 && compiler.getSourceManager().isMacroBodyExpansion(locStart)
388 && (Lexer::getImmediateMacroName(
389 locStart, compiler.getSourceManager(),
390 compiler.getLangOpts())
391 == "NULL"))
393 locStart = compat::getImmediateExpansionRange(compiler.getSourceManager(), locStart)
394 .first;
396 SourceLocation locEnd(expr->getEndLoc());
397 while (compiler.getSourceManager().isMacroArgExpansion(locEnd)) {
398 locEnd = compiler.getSourceManager()
399 .getImmediateMacroCallerLoc(locEnd);
401 if (compiler.getLangOpts().CPlusPlus
402 && compiler.getSourceManager().isMacroBodyExpansion(locEnd)
403 && (Lexer::getImmediateMacroName(
404 locEnd, compiler.getSourceManager(),
405 compiler.getLangOpts())
406 == "NULL"))
408 locEnd = compat::getImmediateExpansionRange(compiler.getSourceManager(), locEnd).first;
410 if (replaceText(SourceRange(compiler.getSourceManager().getSpellingLoc(locStart), compiler.getSourceManager().getSpellingLoc(locEnd)), replacement)) {
411 return;
414 if (castKind == nullptr) {
415 report(DiagnosticsEngine::Warning, "%0 -> %1", expr->getBeginLoc())
416 << kindName(nullPointerKind) << replacement
417 << expr->getSourceRange();
418 } else {
419 report(
420 DiagnosticsEngine::Warning, "%0 ValueDependentIsNotNull %1 -> %2",
421 expr->getBeginLoc())
422 << castKind << kindName(nullPointerKind) << replacement
423 << expr->getSourceRange();
427 loplugin::Plugin::Registration<Nullptr> X("nullptr", true);
431 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */