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 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
:
53 public loplugin::FilteringRewritePlugin
<Nullptr
>
56 explicit Nullptr(loplugin::InstantiationData
const & data
):
57 FilteringRewritePlugin(data
) {}
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; }
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
->getBeginLoc())
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 if (decl
->hasUninstantiatedDefaultArg()) {
215 auto e
= decl
->getDefaultArg();
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
231 bool ret
= RecursiveASTVisitor::TraverseLinkageSpecDecl(decl
);
232 assert(externCContexts_
!= 0);
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
)
252 compiler
.getSourceManager().getPresumedLoc(spellingLocation
)
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:
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()) {
271 auto e
= init
->getInit();
272 if (ignoreLocation(e
)) {
275 auto d
= init
->getAnyMember();
276 if (d
== nullptr || !isAnyKindOfPointerType(d
->getType())) {
279 if (auto e2
= dyn_cast
<ParenListExpr
>(e
)) {
280 if (e2
->getNumExprs() != 1) {
284 } else if (auto e2
= dyn_cast
<InitListExpr
>(e
)) {
285 if (e2
->getNumInits() != 1) {
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
)) {
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
)
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())
325 if (!compiler
.getLangOpts().CPlusPlus
) {
326 //TODO: if !castKind, warn if NULL is passed into fn call
327 // ellipsis, cast to void*
330 loc
= compat::getImmediateExpansionRange(compiler
.getSourceManager(), loc
).first
;
332 compiler
.getSourceManager().getSpellingLoc(loc
)))
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*
346 } else if (ignoreLocation(
347 compiler
.getSourceManager().getSpellingLoc(loc
)))
352 ParenExpr
const * pe
= dyn_cast
<ParenExpr
>(e
);
356 e
= pe
->getSubExpr();
358 if (nullPointerKind
== Expr::NPCK_GNUNull
) {
359 if (castKind
== nullptr) {
360 if (gnuNulls_
.erase(expr
) == 1) {
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())
393 locStart
= compat::getImmediateExpansionRange(compiler
.getSourceManager(), locStart
)
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())
408 locEnd
= compat::getImmediateExpansionRange(compiler
.getSourceManager(), locEnd
).first
;
410 if (replaceText(SourceRange(compiler
.getSourceManager().getSpellingLoc(locStart
), compiler
.getSourceManager().getSpellingLoc(locEnd
)), replacement
)) {
414 if (castKind
== nullptr) {
415 report(DiagnosticsEngine::Warning
, "%0 -> %1", expr
->getBeginLoc())
416 << kindName(nullPointerKind
) << replacement
417 << expr
->getSourceRange();
420 DiagnosticsEngine::Warning
, "%0 ValueDependentIsNotNull %1 -> %2",
422 << castKind
<< kindName(nullPointerKind
) << replacement
423 << expr
->getSourceRange();
427 loplugin::Plugin::Registration
<Nullptr
> X("nullptr", true);
431 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */