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/.
19 char const * kindName(Expr::NullPointerConstantKind kind
) {
21 case Expr::NPCK_ZeroExpression
:
22 return "ZeroExpression";
23 case Expr::NPCK_ZeroLiteral
:
25 case Expr::NPCK_CXX11_nullptr
:
26 return "CXX11_nullptr";
27 case Expr::NPCK_GNUNull
:
29 case Expr::NPCK_NotNull
:
30 assert(false); // cannot happen
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 RecursiveASTVisitor
<Nullptr
>, public loplugin::RewritePlugin
56 explicit Nullptr(InstantiationData
const & data
): RewritePlugin(data
) {}
59 { TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl()); }
61 bool VisitImplicitCastExpr(CastExpr
const * expr
);
63 bool VisitGNUNullExpr(GNUNullExpr
const * expr
);
65 bool VisitBinaryOperator(BinaryOperator
const * expr
);
67 bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr
const * expr
);
69 bool VisitParmVarDecl(ParmVarDecl
const * decl
);
71 bool TraverseConstructorInitializer(CXXCtorInitializer
* init
);
73 bool TraverseLinkageSpecDecl(LinkageSpecDecl
* decl
);
75 // bool shouldVisitTemplateInstantiations() const { return true; }
78 bool isInLokIncludeFile(SourceLocation spellingLocation
) const;
80 bool isFromCIncludeFile(SourceLocation spellingLocation
) const;
82 bool isSharedCAndCppCode(SourceLocation location
) const;
84 void visitCXXCtorInitializer(CXXCtorInitializer
const * init
);
86 void handleZero(Expr
const * expr
);
89 Expr
const * expr
, char const * castKind
,
90 Expr::NullPointerConstantKind nullPointerKind
);
93 Expr
const * expr
, char const * castKind
,
94 Expr::NullPointerConstantKind nullPointerKind
,
95 char const * replacement
);
97 std::set
<Expr
const *> gnuNulls_
;
98 unsigned int externCContexts_
= 0;
101 bool Nullptr::VisitImplicitCastExpr(CastExpr
const * expr
) {
102 if (ignoreLocation(expr
)) {
105 if (!isNullPointerCast(expr
)) {
108 Expr::NullPointerConstantKind k
= expr
->isNullPointerConstant(
109 compiler
.getASTContext(), Expr::NPC_ValueDependentIsNotNull
);
111 case Expr::NPCK_NotNull
:
112 k
= expr
->isNullPointerConstant(
113 compiler
.getASTContext(), Expr::NPC_ValueDependentIsNull
);
115 case Expr::NPCK_NotNull
:
117 case Expr::NPCK_ZeroExpression
:
118 case Expr::NPCK_ZeroLiteral
:
120 DiagnosticsEngine::Warning
,
121 "suspicious ValueDependentIsNull %0", expr
->getLocStart())
122 << kindName(k
) << expr
->getSourceRange();
125 assert(false); // cannot happen
128 case Expr::NPCK_CXX11_nullptr
:
131 handleNull(expr
->getSubExpr(), expr
->getCastKindName(), k
);
137 bool Nullptr::VisitGNUNullExpr(GNUNullExpr
const * expr
) {
138 if (ignoreLocation(expr
)) {
141 handleNull(expr
, nullptr, Expr::NPCK_GNUNull
);
145 bool Nullptr::VisitBinaryOperator(BinaryOperator
const * expr
) {
146 if (ignoreLocation(expr
)) {
150 switch (expr
->getOpcode()) {
153 if (isAnyKindOfPointerType(expr
->getRHS()->getType())) {
159 if (isAnyKindOfPointerType(expr
->getLHS()->getType())) {
171 bool Nullptr::VisitCXXOperatorCallExpr(CXXOperatorCallExpr
const * expr
) {
172 if (ignoreLocation(expr
)) {
176 switch (expr
->getOperator()) {
178 case OO_ExclaimEqual
:
179 if (isAnyKindOfPointerType(expr
->getArg(1)->getType())) {
185 if (isAnyKindOfPointerType(expr
->getArg(0)->getType())) {
197 bool Nullptr::VisitParmVarDecl(ParmVarDecl
const * decl
) {
198 if (ignoreLocation(decl
)) {
201 if (!isAnyKindOfPointerType(decl
->getType())) {
204 auto e
= decl
->getDefaultArg();
212 bool Nullptr::TraverseConstructorInitializer(CXXCtorInitializer
* init
) {
213 visitCXXCtorInitializer(init
);
214 return RecursiveASTVisitor::TraverseConstructorInitializer(init
);
217 bool Nullptr::TraverseLinkageSpecDecl(LinkageSpecDecl
* decl
) {
218 assert(externCContexts_
!= std::numeric_limits
<unsigned int>::max()); //TODO
220 bool ret
= RecursiveASTVisitor::TraverseLinkageSpecDecl(decl
);
221 assert(externCContexts_
!= 0);
226 bool Nullptr::isInLokIncludeFile(SourceLocation spellingLocation
) const {
227 return compiler
.getSourceManager().getFilename(spellingLocation
)
228 .startswith(SRCDIR
"/include/LibreOfficeKit/");
231 bool Nullptr::isFromCIncludeFile(SourceLocation spellingLocation
) const {
232 return !compiler
.getSourceManager().isInMainFile(spellingLocation
)
234 compiler
.getSourceManager().getPresumedLoc(spellingLocation
)
239 bool Nullptr::isSharedCAndCppCode(SourceLocation location
) const {
240 // Assume that code is intended to be shared between C and C++ if it comes
241 // from an include file ending in .h, and is either in an extern "C" context
242 // or the body of a macro definition:
244 isFromCIncludeFile(compiler
.getSourceManager().getSpellingLoc(location
))
245 && (externCContexts_
!= 0
246 || compiler
.getSourceManager().isMacroBodyExpansion(location
));
249 void Nullptr::visitCXXCtorInitializer(CXXCtorInitializer
const * init
) {
250 if (!init
->isWritten()) {
253 auto e
= init
->getInit();
254 if (ignoreLocation(e
)) {
257 auto d
= init
->getAnyMember();
258 if (d
== nullptr || !isAnyKindOfPointerType(d
->getType())) {
261 if (auto e2
= dyn_cast
<ParenListExpr
>(e
)) {
262 if (e2
->getNumExprs() != 1) {
266 } else if (auto e2
= dyn_cast
<InitListExpr
>(e
)) {
267 if (e2
->getNumInits() != 1) {
275 void Nullptr::handleZero(Expr
const * expr
) {
276 //TODO: detect NPCK_ZeroExpression where appropriate
277 // Filter out ImplicitCastExpr that will be handled by
278 // VisitImplicitCastExpr:
279 if (auto ice
= dyn_cast
<ImplicitCastExpr
>(expr
)) {
280 if (isNullPointerCast(ice
)) {
284 auto const lit
= dyn_cast
<IntegerLiteral
>(expr
->IgnoreParenImpCasts());
285 if (lit
!= nullptr && !lit
->getValue().getBoolValue()) {
286 handleNull(expr
, nullptr, Expr::NPCK_ZeroLiteral
);
290 void Nullptr::handleNull(
291 Expr
const * expr
, char const * castKind
,
292 Expr::NullPointerConstantKind nullPointerKind
)
297 e
= e
->IgnoreImpCasts();
298 loc
= e
->getLocStart();
299 while (compiler
.getSourceManager().isMacroArgExpansion(loc
)) {
300 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(loc
);
302 if (compiler
.getSourceManager().isMacroBodyExpansion(loc
)) {
303 if (Lexer::getImmediateMacroName(
304 loc
, compiler
.getSourceManager(), compiler
.getLangOpts())
307 if (!compiler
.getLangOpts().CPlusPlus
) {
308 //TODO: if !castKind, warn if NULL is passed into fn call
309 // ellipsis, cast to void*
312 loc
= compiler
.getSourceManager()
313 .getImmediateExpansionRange(loc
).first
;
315 compiler
.getSourceManager().getSpellingLoc(loc
)))
319 if (isInUnoIncludeFile(
320 compiler
.getSourceManager().getSpellingLoc(loc
))
321 || isInLokIncludeFile(
322 compiler
.getSourceManager().getSpellingLoc(loc
))
323 || isSharedCAndCppCode(loc
))
325 //TODO: if !castKind, warn if NULL is passed into fn call
326 // ellipsis, cast to void*
329 } else if (ignoreLocation(
330 compiler
.getSourceManager().getSpellingLoc(loc
)))
335 ParenExpr
const * pe
= dyn_cast
<ParenExpr
>(e
);
339 e
= pe
->getSubExpr();
341 if (nullPointerKind
== Expr::NPCK_GNUNull
) {
342 if (castKind
== nullptr) {
343 if (gnuNulls_
.erase(expr
) == 1) {
347 auto const ok
= gnuNulls_
.insert(expr
).second
;
348 assert(ok
); (void) ok
;
351 auto const asMacro
= !compiler
.getLangOpts().CPlusPlus
352 || isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(loc
))
353 || isInLokIncludeFile(compiler
.getSourceManager().getSpellingLoc(loc
))
354 || isSharedCAndCppCode(loc
);
355 assert(!asMacro
|| nullPointerKind
!= Expr::NPCK_GNUNull
);
356 rewriteOrWarn(e
, castKind
, nullPointerKind
, asMacro
? "NULL" : "nullptr");
359 void Nullptr::rewriteOrWarn(
360 Expr
const * expr
, char const * castKind
,
361 Expr::NullPointerConstantKind nullPointerKind
, char const * replacement
)
363 if (rewriter
!= nullptr) {
364 SourceLocation
locStart(expr
->getLocStart());
365 while (compiler
.getSourceManager().isMacroArgExpansion(locStart
)) {
366 locStart
= compiler
.getSourceManager()
367 .getImmediateMacroCallerLoc(locStart
);
369 if (compiler
.getLangOpts().CPlusPlus
370 && compiler
.getSourceManager().isMacroBodyExpansion(locStart
)
371 && (Lexer::getImmediateMacroName(
372 locStart
, compiler
.getSourceManager(),
373 compiler
.getLangOpts())
376 locStart
= compiler
.getSourceManager().getImmediateExpansionRange(
379 SourceLocation
locEnd(expr
->getLocEnd());
380 while (compiler
.getSourceManager().isMacroArgExpansion(locEnd
)) {
381 locEnd
= compiler
.getSourceManager()
382 .getImmediateMacroCallerLoc(locEnd
);
384 if (compiler
.getLangOpts().CPlusPlus
385 && compiler
.getSourceManager().isMacroBodyExpansion(locEnd
)
386 && (Lexer::getImmediateMacroName(
387 locEnd
, compiler
.getSourceManager(),
388 compiler
.getLangOpts())
391 locEnd
= compiler
.getSourceManager().getImmediateExpansionRange(
394 if (replaceText(SourceRange(compiler
.getSourceManager().getSpellingLoc(locStart
), compiler
.getSourceManager().getSpellingLoc(locEnd
)), replacement
)) {
398 if (castKind
== nullptr) {
399 report(DiagnosticsEngine::Warning
, "%0 -> %1", expr
->getLocStart())
400 << kindName(nullPointerKind
) << replacement
401 << expr
->getSourceRange();
404 DiagnosticsEngine::Warning
, "%0 ValueDependentIsNotNull %1 -> %2",
406 << castKind
<< kindName(nullPointerKind
) << replacement
407 << expr
->getSourceRange();
411 loplugin::Plugin::Registration
<Nullptr
> X("nullptr", true);
415 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */