1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
21 char const * kindName(Expr::NullPointerConstantKind kind
) {
23 case Expr::NPCK_ZeroExpression
:
24 return "ZeroExpression";
25 case Expr::NPCK_ZeroLiteral
:
27 case Expr::NPCK_CXX11_nullptr
:
28 return "CXX11_nullptr";
29 case Expr::NPCK_GNUNull
:
31 case Expr::NPCK_NotNull
:
32 assert(false); // cannot happen
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
:
55 public RecursiveASTVisitor
<Nullptr
>, public loplugin::RewritePlugin
58 explicit Nullptr(loplugin::InstantiationData
const & data
):
59 RewritePlugin(data
) {}
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; }
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
);
92 Expr
const * expr
, char const * castKind
,
93 Expr::NullPointerConstantKind nullPointerKind
);
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
)) {
108 if (!isNullPointerCast(expr
)) {
111 Expr::NullPointerConstantKind k
= expr
->isNullPointerConstant(
112 compiler
.getASTContext(), Expr::NPC_ValueDependentIsNotNull
);
114 case Expr::NPCK_NotNull
:
115 k
= expr
->isNullPointerConstant(
116 compiler
.getASTContext(), Expr::NPC_ValueDependentIsNull
);
118 case Expr::NPCK_NotNull
:
120 case Expr::NPCK_ZeroExpression
:
121 case Expr::NPCK_ZeroLiteral
:
123 DiagnosticsEngine::Warning
,
124 "suspicious ValueDependentIsNull %0", expr
->getLocStart())
125 << kindName(k
) << expr
->getSourceRange();
128 assert(false); // cannot happen
131 case Expr::NPCK_CXX11_nullptr
:
134 if (loplugin::TypeCheck(expr
->getType()).Typedef("locale_t")
137 break; // POSIX locale_t is left unspecified
139 handleNull(expr
->getSubExpr(), expr
->getCastKindName(), k
);
145 bool Nullptr::VisitGNUNullExpr(GNUNullExpr
const * expr
) {
146 if (ignoreLocation(expr
)) {
149 handleNull(expr
, nullptr, Expr::NPCK_GNUNull
);
153 bool Nullptr::VisitBinaryOperator(BinaryOperator
const * expr
) {
154 if (ignoreLocation(expr
)) {
158 switch (expr
->getOpcode()) {
161 if (isAnyKindOfPointerType(expr
->getRHS()->getType())) {
167 if (isAnyKindOfPointerType(expr
->getLHS()->getType())) {
179 bool Nullptr::VisitCXXOperatorCallExpr(CXXOperatorCallExpr
const * expr
) {
180 if (ignoreLocation(expr
)) {
184 switch (expr
->getOperator()) {
186 case OO_ExclaimEqual
:
187 if (isAnyKindOfPointerType(expr
->getArg(1)->getType())) {
193 if (isAnyKindOfPointerType(expr
->getArg(0)->getType())) {
205 bool Nullptr::VisitParmVarDecl(ParmVarDecl
const * decl
) {
206 if (ignoreLocation(decl
)) {
209 if (!isAnyKindOfPointerType(decl
->getType())) {
212 auto e
= decl
->getDefaultArg();
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
228 bool ret
= RecursiveASTVisitor::TraverseLinkageSpecDecl(decl
);
229 assert(externCContexts_
!= 0);
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
)
243 compiler
.getSourceManager().getPresumedLoc(spellingLocation
)
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:
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()) {
262 auto e
= init
->getInit();
263 if (ignoreLocation(e
)) {
266 auto d
= init
->getAnyMember();
267 if (d
== nullptr || !isAnyKindOfPointerType(d
->getType())) {
270 if (auto e2
= dyn_cast
<ParenListExpr
>(e
)) {
271 if (e2
->getNumExprs() != 1) {
275 } else if (auto e2
= dyn_cast
<InitListExpr
>(e
)) {
276 if (e2
->getNumInits() != 1) {
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
)) {
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
)
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())
316 if (!compiler
.getLangOpts().CPlusPlus
) {
317 //TODO: if !castKind, warn if NULL is passed into fn call
318 // ellipsis, cast to void*
321 loc
= compat::getImmediateExpansionRange(compiler
.getSourceManager(), loc
).first
;
323 compiler
.getSourceManager().getSpellingLoc(loc
)))
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*
337 } else if (ignoreLocation(
338 compiler
.getSourceManager().getSpellingLoc(loc
)))
343 ParenExpr
const * pe
= dyn_cast
<ParenExpr
>(e
);
347 e
= pe
->getSubExpr();
349 if (nullPointerKind
== Expr::NPCK_GNUNull
) {
350 if (castKind
== nullptr) {
351 if (gnuNulls_
.erase(expr
) == 1) {
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())
384 locStart
= compat::getImmediateExpansionRange(compiler
.getSourceManager(), locStart
)
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())
399 locEnd
= compat::getImmediateExpansionRange(compiler
.getSourceManager(), locEnd
).first
;
401 if (replaceText(SourceRange(compiler
.getSourceManager().getSpellingLoc(locStart
), compiler
.getSourceManager().getSpellingLoc(locEnd
)), replacement
)) {
405 if (castKind
== nullptr) {
406 report(DiagnosticsEngine::Warning
, "%0 -> %1", expr
->getLocStart())
407 << kindName(nullPointerKind
) << replacement
408 << expr
->getSourceRange();
411 DiagnosticsEngine::Warning
, "%0 ValueDependentIsNotNull %1 -> %2",
413 << castKind
<< kindName(nullPointerKind
) << replacement
414 << expr
->getSourceRange();
418 loplugin::Plugin::Registration
<Nullptr
> X("nullptr", true);
422 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */