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", compat::getBeginLoc(expr
))
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::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
)
249 compiler
.getSourceManager().getPresumedLoc(spellingLocation
)
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:
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()) {
268 auto e
= init
->getInit();
269 if (ignoreLocation(e
)) {
272 auto d
= init
->getAnyMember();
273 if (d
== nullptr || !isAnyKindOfPointerType(d
->getType())) {
276 if (auto e2
= dyn_cast
<ParenListExpr
>(e
)) {
277 if (e2
->getNumExprs() != 1) {
281 } else if (auto e2
= dyn_cast
<InitListExpr
>(e
)) {
282 if (e2
->getNumInits() != 1) {
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
)) {
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
)
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())
322 if (!compiler
.getLangOpts().CPlusPlus
) {
323 //TODO: if !castKind, warn if NULL is passed into fn call
324 // ellipsis, cast to void*
327 loc
= compat::getImmediateExpansionRange(compiler
.getSourceManager(), loc
).first
;
329 compiler
.getSourceManager().getSpellingLoc(loc
)))
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*
343 } else if (ignoreLocation(
344 compiler
.getSourceManager().getSpellingLoc(loc
)))
349 ParenExpr
const * pe
= dyn_cast
<ParenExpr
>(e
);
353 e
= pe
->getSubExpr();
355 if (nullPointerKind
== Expr::NPCK_GNUNull
) {
356 if (castKind
== nullptr) {
357 if (gnuNulls_
.erase(expr
) == 1) {
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())
390 locStart
= compat::getImmediateExpansionRange(compiler
.getSourceManager(), locStart
)
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())
405 locEnd
= compat::getImmediateExpansionRange(compiler
.getSourceManager(), locEnd
).first
;
407 if (replaceText(SourceRange(compiler
.getSourceManager().getSpellingLoc(locStart
), compiler
.getSourceManager().getSpellingLoc(locEnd
)), replacement
)) {
411 if (castKind
== nullptr) {
412 report(DiagnosticsEngine::Warning
, "%0 -> %1", compat::getBeginLoc(expr
))
413 << kindName(nullPointerKind
) << replacement
414 << expr
->getSourceRange();
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: */