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/.
13 #include <unordered_map>
14 #include <unordered_set>
18 #include "clang/AST/CXXInheritance.h"
19 #include "clang/AST/StmtVisitor.h"
22 Look for nested if statements with relatively small conditions, where they can be collapsed into
27 class CollapseIf
: public loplugin::FilteringPlugin
<CollapseIf
>
30 explicit CollapseIf(loplugin::InstantiationData
const& data
)
31 : FilteringPlugin(data
)
35 virtual void run() override
{ TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl()); }
37 bool VisitIfStmt(IfStmt
const*);
40 int getNoCharsInSourceCodeOfExpr(IfStmt
const*);
41 bool containsComment(Stmt
const* stmt
);
44 bool CollapseIf::VisitIfStmt(IfStmt
const* ifStmt
)
46 if (ignoreLocation(ifStmt
))
48 if (ifStmt
->getElse())
51 IfStmt
const* secondIfStmt
= nullptr;
52 if (auto compoundStmt
= dyn_cast
<CompoundStmt
>(ifStmt
->getThen()))
54 if (compoundStmt
->size() != 1)
56 secondIfStmt
= dyn_cast
<IfStmt
>(*compoundStmt
->body_begin());
59 if (secondIfStmt
->getElse())
64 secondIfStmt
= dyn_cast
<IfStmt
>(ifStmt
->getThen());
69 int noChars1
= getNoCharsInSourceCodeOfExpr(ifStmt
);
70 int noChars2
= getNoCharsInSourceCodeOfExpr(secondIfStmt
);
71 if (noChars1
+ noChars2
> 40)
74 // Sometimes there is a comment between the first and second if, so
75 // merging them would make the comment more awkward to write.
76 if (containsComment(ifStmt
))
79 report(DiagnosticsEngine::Warning
, "nested if should be collapsed into one statement %0 %1",
80 compat::getBeginLoc(ifStmt
))
81 << noChars1
<< noChars2
<< ifStmt
->getSourceRange();
85 int CollapseIf::getNoCharsInSourceCodeOfExpr(IfStmt
const* ifStmt
)
87 // Measure the space between the "if" the beginning of the "then" block because
88 // measuring the size of the condition expression is unreliable, because clang
89 // does not report the location of the last token accurately.
90 SourceManager
& SM
= compiler
.getSourceManager();
91 SourceLocation startLoc
= compat::getBeginLoc(ifStmt
);
92 SourceLocation endLoc
= compat::getBeginLoc(ifStmt
->getThen());
93 char const* p1
= SM
.getCharacterData(startLoc
);
94 char const* p2
= SM
.getCharacterData(endLoc
);
97 for (auto p
= p1
; p
< p2
; ++p
)
104 bool CollapseIf::containsComment(Stmt
const* stmt
)
106 SourceManager
& SM
= compiler
.getSourceManager();
107 auto range
= stmt
->getSourceRange();
108 SourceLocation startLoc
= range
.getBegin();
109 SourceLocation endLoc
= range
.getEnd();
110 char const* p1
= SM
.getCharacterData(startLoc
);
111 char const* p2
= SM
.getCharacterData(endLoc
);
112 p2
+= Lexer::MeasureTokenLength(endLoc
, SM
, compiler
.getLangOpts());
114 // check for comments
115 constexpr char const comment1
[] = "/*";
116 constexpr char const comment2
[] = "//";
117 if (std::search(p1
, p2
, comment1
, comment1
+ strlen(comment1
)) != p2
)
119 if (std::search(p1
, p2
, comment2
, comment2
+ strlen(comment2
)) != p2
)
125 /** Off by default because some places are a judgement call if it should be collapsed or not. */
126 loplugin::Plugin::Registration
<CollapseIf
> X("collapseif", false);
129 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */