2017-09-20 12:18:04 +02:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/*
|
|
|
|
* This file is part of the LibreOffice project.
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
2017-10-06 10:44:29 +02:00
|
|
|
#include "plugin.hxx"
|
2017-09-20 12:18:04 +02:00
|
|
|
#include <cassert>
|
|
|
|
#include <string>
|
|
|
|
#include <iostream>
|
|
|
|
#include <fstream>
|
|
|
|
#include <set>
|
2017-10-06 10:44:29 +02:00
|
|
|
#include <stack>
|
2017-09-20 12:18:04 +02:00
|
|
|
|
|
|
|
/**
|
2017-09-22 08:50:20 +02:00
|
|
|
Look for places where we can flatten the control flow in a method by returning early.
|
2017-09-20 12:18:04 +02:00
|
|
|
*/
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
class Flatten:
|
|
|
|
public RecursiveASTVisitor<Flatten>, public loplugin::RewritePlugin
|
|
|
|
{
|
|
|
|
public:
|
2017-11-07 11:50:47 +01:00
|
|
|
explicit Flatten(loplugin::InstantiationData const & data):
|
|
|
|
RewritePlugin(data) {}
|
2017-09-20 12:18:04 +02:00
|
|
|
|
|
|
|
virtual void run() override
|
|
|
|
{
|
|
|
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
|
|
|
}
|
|
|
|
|
2017-10-06 10:44:29 +02:00
|
|
|
bool TraverseIfStmt(IfStmt *);
|
2017-09-20 12:18:04 +02:00
|
|
|
bool TraverseCXXCatchStmt(CXXCatchStmt * );
|
2017-10-06 10:44:29 +02:00
|
|
|
bool TraverseCompoundStmt(CompoundStmt *);
|
2017-09-28 10:33:09 +02:00
|
|
|
bool VisitIfStmt(IfStmt const * );
|
2017-09-20 12:18:04 +02:00
|
|
|
private:
|
2017-09-28 10:33:09 +02:00
|
|
|
bool rewrite1(IfStmt const * );
|
|
|
|
bool rewrite2(IfStmt const * );
|
2017-09-20 12:18:04 +02:00
|
|
|
SourceRange ignoreMacroExpansions(SourceRange range);
|
2017-09-21 11:06:43 +02:00
|
|
|
SourceRange extendOverComments(SourceRange range);
|
2017-09-20 12:18:04 +02:00
|
|
|
std::string getSourceAsString(SourceRange range);
|
2017-09-22 10:53:31 +02:00
|
|
|
std::string invertCondition(Expr const * condExpr, SourceRange conditionRange);
|
2017-09-28 10:33:09 +02:00
|
|
|
bool checkOverlap(SourceRange);
|
|
|
|
|
2017-10-06 10:44:29 +02:00
|
|
|
std::stack<bool> mLastStmtInParentStack;
|
2017-09-28 10:33:09 +02:00
|
|
|
std::vector<std::pair<char const *, char const *>> mvModifiedRanges;
|
2017-09-20 12:18:04 +02:00
|
|
|
};
|
|
|
|
|
2017-09-28 10:33:09 +02:00
|
|
|
static Stmt const * containsSingleThrowExpr(Stmt const * stmt)
|
2017-09-20 12:18:04 +02:00
|
|
|
{
|
|
|
|
if (auto compoundStmt = dyn_cast<CompoundStmt>(stmt)) {
|
|
|
|
if (compoundStmt->size() != 1)
|
|
|
|
return nullptr;
|
|
|
|
stmt = *compoundStmt->body_begin();
|
|
|
|
}
|
|
|
|
if (auto exprWithCleanups = dyn_cast<ExprWithCleanups>(stmt)) {
|
|
|
|
stmt = exprWithCleanups->getSubExpr();
|
|
|
|
}
|
|
|
|
return dyn_cast<CXXThrowExpr>(stmt);
|
|
|
|
}
|
|
|
|
|
2017-09-28 10:33:09 +02:00
|
|
|
static bool containsVarDecl(Stmt const * stmt)
|
|
|
|
{
|
|
|
|
if (auto compoundStmt = dyn_cast<CompoundStmt>(stmt)) {
|
|
|
|
for (auto i = compoundStmt->body_begin(); i != compoundStmt->body_end(); ++i) {
|
|
|
|
auto declStmt = dyn_cast<DeclStmt>(*i);
|
|
|
|
if (declStmt && isa<VarDecl>(*declStmt->decl_begin()))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto declStmt = dyn_cast<DeclStmt>(stmt);
|
|
|
|
return declStmt && isa<VarDecl>(*declStmt->decl_begin());
|
|
|
|
}
|
|
|
|
|
2017-10-06 10:44:29 +02:00
|
|
|
bool Flatten::TraverseCXXCatchStmt(CXXCatchStmt* )
|
2017-09-28 10:33:09 +02:00
|
|
|
{
|
2017-10-06 10:44:29 +02:00
|
|
|
// ignore stuff inside catch statements, where doing a "if...else..throw" is more natural
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Flatten::TraverseIfStmt(IfStmt * ifStmt)
|
|
|
|
{
|
|
|
|
// ignore if we are part of an if/then/else/if chain
|
|
|
|
if (ifStmt->getElse() && isa<IfStmt>(ifStmt->getElse()))
|
2017-09-28 10:33:09 +02:00
|
|
|
return true;
|
2017-10-06 10:44:29 +02:00
|
|
|
return RecursiveASTVisitor<Flatten>::TraverseIfStmt(ifStmt);
|
2017-09-28 10:33:09 +02:00
|
|
|
}
|
|
|
|
|
2017-10-06 10:44:29 +02:00
|
|
|
bool Flatten::TraverseCompoundStmt(CompoundStmt * compoundStmt)
|
2017-09-20 12:18:04 +02:00
|
|
|
{
|
2017-10-06 10:44:29 +02:00
|
|
|
// if the "if" statement is not the last statement in its block, and it contains
|
|
|
|
// var decls in its then block, we cannot de-indent the then block without
|
|
|
|
// extending the lifetime of some variables, which may be problematic
|
|
|
|
// ignore if we are part of an if/then/else/if chain
|
|
|
|
mLastStmtInParentStack.push(compoundStmt->size() > 0 && isa<IfStmt>(*compoundStmt->body_back()));
|
|
|
|
bool rv = RecursiveASTVisitor<Flatten>::TraverseCompoundStmt(compoundStmt);
|
|
|
|
mLastStmtInParentStack.pop();
|
|
|
|
return rv;
|
2017-09-20 12:18:04 +02:00
|
|
|
}
|
|
|
|
|
2017-09-28 10:33:09 +02:00
|
|
|
bool Flatten::VisitIfStmt(IfStmt const * ifStmt)
|
2017-09-20 12:18:04 +02:00
|
|
|
{
|
|
|
|
if (ignoreLocation(ifStmt))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!ifStmt->getElse())
|
|
|
|
return true;
|
|
|
|
|
2017-09-30 22:39:33 +02:00
|
|
|
if (containsPreprocessingConditionalInclusion(ifStmt->getSourceRange())) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-20 12:18:04 +02:00
|
|
|
auto throwExpr = containsSingleThrowExpr(ifStmt->getElse());
|
2017-09-28 10:33:09 +02:00
|
|
|
if (throwExpr)
|
|
|
|
{
|
|
|
|
// if both the "if" and the "else" contain throws, no improvement
|
|
|
|
if (containsSingleThrowExpr(ifStmt->getThen()))
|
|
|
|
return true;
|
2017-10-06 10:44:29 +02:00
|
|
|
// if the "if" statement is not the last statement in its block, and it contains
|
|
|
|
// var decls in its then block, we cannot de-indent the then block without
|
2017-09-28 10:33:09 +02:00
|
|
|
// extending the lifetime of some variables, which may be problematic
|
2017-10-06 10:44:29 +02:00
|
|
|
if (!mLastStmtInParentStack.top() || containsVarDecl(ifStmt->getThen()))
|
2017-09-28 10:33:09 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!rewrite1(ifStmt))
|
|
|
|
{
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
"unconditional throw in else branch, rather invert the condition, throw early, and flatten the normal case",
|
|
|
|
throwExpr->getLocStart())
|
|
|
|
<< throwExpr->getSourceRange();
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Note,
|
|
|
|
"if condition here",
|
|
|
|
ifStmt->getLocStart())
|
|
|
|
<< ifStmt->getSourceRange();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throwExpr = containsSingleThrowExpr(ifStmt->getThen());
|
|
|
|
if (throwExpr)
|
2017-09-20 12:18:04 +02:00
|
|
|
{
|
2017-09-28 10:33:09 +02:00
|
|
|
// if both the "if" and the "else" contain throws, no improvement
|
|
|
|
if (containsSingleThrowExpr(ifStmt->getElse()))
|
|
|
|
return true;
|
|
|
|
// if the "if" statement is not the last statement in it's block, and it contains
|
|
|
|
// var decls in it's else block, we cannot de-indent the else block without
|
|
|
|
// extending the lifetime of some variables, which may be problematic
|
2017-10-06 10:44:29 +02:00
|
|
|
if (!mLastStmtInParentStack.top() || containsVarDecl(ifStmt->getElse()))
|
2017-09-28 10:33:09 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!rewrite2(ifStmt))
|
|
|
|
{
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
"unconditional throw in then branch, just flatten the else",
|
|
|
|
throwExpr->getLocStart())
|
|
|
|
<< throwExpr->getSourceRange();
|
|
|
|
}
|
2017-09-20 12:18:04 +02:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string stripOpenAndCloseBrace(std::string s);
|
2017-09-21 11:06:43 +02:00
|
|
|
static std::string deindent(std::string const & s);
|
|
|
|
static std::vector<std::string> split(std::string s);
|
2017-09-28 10:33:09 +02:00
|
|
|
static bool startswith(std::string const & rStr, char const * pSubStr);
|
2017-09-20 12:18:04 +02:00
|
|
|
|
2017-09-28 10:33:09 +02:00
|
|
|
bool Flatten::rewrite1(IfStmt const * ifStmt)
|
2017-09-20 12:18:04 +02:00
|
|
|
{
|
|
|
|
if (!rewriter)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
auto conditionRange = ignoreMacroExpansions(ifStmt->getCond()->getSourceRange());
|
|
|
|
if (!conditionRange.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto thenRange = ignoreMacroExpansions(ifStmt->getThen()->getSourceRange());
|
|
|
|
if (!thenRange.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto elseRange = ignoreMacroExpansions(ifStmt->getElse()->getSourceRange());
|
|
|
|
if (!elseRange.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-09-21 11:06:43 +02:00
|
|
|
SourceRange elseKeywordRange = ifStmt->getElseLoc();
|
|
|
|
|
2017-09-22 15:32:24 +02:00
|
|
|
// If we overlap with a previous area we modified, we cannot perform this change
|
|
|
|
// without corrupting the source
|
2017-09-28 10:33:09 +02:00
|
|
|
if (!checkOverlap(ifStmt->getSourceRange()))
|
|
|
|
return false;
|
2017-09-22 15:32:24 +02:00
|
|
|
|
2017-09-21 11:06:43 +02:00
|
|
|
thenRange = extendOverComments(thenRange);
|
|
|
|
elseRange = extendOverComments(elseRange);
|
|
|
|
elseKeywordRange = extendOverComments(elseKeywordRange);
|
2017-09-20 12:18:04 +02:00
|
|
|
|
|
|
|
// in adjusting the formatting I assume that "{" starts on a new line
|
|
|
|
|
2017-09-22 10:53:31 +02:00
|
|
|
std::string conditionString = invertCondition(ifStmt->getCond(), conditionRange);
|
2017-09-20 12:18:04 +02:00
|
|
|
|
|
|
|
std::string thenString = getSourceAsString(thenRange);
|
|
|
|
if (auto compoundStmt = dyn_cast<CompoundStmt>(ifStmt->getThen())) {
|
|
|
|
if (compoundStmt->getLBracLoc().isValid()) {
|
|
|
|
thenString = stripOpenAndCloseBrace(thenString);
|
|
|
|
}
|
|
|
|
}
|
2017-09-21 11:06:43 +02:00
|
|
|
thenString = deindent(thenString);
|
2017-09-20 12:18:04 +02:00
|
|
|
|
|
|
|
std::string elseString = getSourceAsString(elseRange);
|
|
|
|
|
|
|
|
if (!replaceText(elseRange, thenString)) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-09-28 10:33:09 +02:00
|
|
|
std::cout << "rewrite 3" << std::endl;
|
2017-09-21 15:42:49 +02:00
|
|
|
if (!removeText(elseKeywordRange)) {
|
2017-09-20 12:18:04 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!replaceText(thenRange, elseString)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!replaceText(conditionRange, conditionString)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-28 10:33:09 +02:00
|
|
|
bool Flatten::rewrite2(IfStmt const * ifStmt)
|
|
|
|
{
|
|
|
|
if (!rewriter)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
auto conditionRange = ignoreMacroExpansions(ifStmt->getCond()->getSourceRange());
|
|
|
|
if (!conditionRange.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto thenRange = ignoreMacroExpansions(ifStmt->getThen()->getSourceRange());
|
|
|
|
if (!thenRange.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto elseRange = ignoreMacroExpansions(ifStmt->getElse()->getSourceRange());
|
|
|
|
if (!elseRange.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
SourceRange elseKeywordRange = ifStmt->getElseLoc();
|
|
|
|
|
|
|
|
// If we overlap with a previous area we modified, we cannot perform this change
|
|
|
|
// without corrupting the source
|
|
|
|
if (!checkOverlap(ifStmt->getSourceRange()))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
elseRange = extendOverComments(elseRange);
|
|
|
|
elseKeywordRange = extendOverComments(elseKeywordRange);
|
|
|
|
|
|
|
|
// in adjusting the formatting I assume that "{" starts on a new line
|
|
|
|
|
|
|
|
std::string elseString = getSourceAsString(elseRange);
|
|
|
|
if (auto compoundStmt = dyn_cast<CompoundStmt>(ifStmt->getElse())) {
|
|
|
|
if (compoundStmt->getLBracLoc().isValid()) {
|
|
|
|
elseString = stripOpenAndCloseBrace(elseString);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
elseString = deindent(elseString);
|
|
|
|
|
|
|
|
if (!replaceText(elseRange, elseString)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!removeText(elseKeywordRange)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we overlap with a previous area we modified, we cannot perform this change
|
|
|
|
// without corrupting the source
|
|
|
|
bool Flatten::checkOverlap(SourceRange range)
|
|
|
|
{
|
|
|
|
SourceManager& SM = compiler.getSourceManager();
|
|
|
|
char const *p1 = SM.getCharacterData( range.getBegin() );
|
|
|
|
char const *p2 = SM.getCharacterData( range.getEnd() );
|
|
|
|
for (std::pair<char const *, char const *> const & rPair : mvModifiedRanges)
|
|
|
|
{
|
|
|
|
if (rPair.first <= p1 && p1 <= rPair.second)
|
|
|
|
return false;
|
|
|
|
if (p1 <= rPair.second && rPair.first <= p2)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
mvModifiedRanges.emplace_back(p1, p2);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-22 10:53:31 +02:00
|
|
|
std::string Flatten::invertCondition(Expr const * condExpr, SourceRange conditionRange)
|
|
|
|
{
|
|
|
|
std::string s = getSourceAsString(conditionRange);
|
|
|
|
|
|
|
|
condExpr = condExpr->IgnoreImpCasts();
|
|
|
|
|
|
|
|
if (auto exprWithCleanups = dyn_cast<ExprWithCleanups>(condExpr))
|
|
|
|
condExpr = exprWithCleanups->getSubExpr()->IgnoreImpCasts();
|
|
|
|
|
|
|
|
if (auto unaryOp = dyn_cast<UnaryOperator>(condExpr))
|
|
|
|
{
|
|
|
|
if (unaryOp->getOpcode() != UO_LNot)
|
|
|
|
return "!(" + s + ")";
|
|
|
|
auto i = s.find("!");
|
|
|
|
assert (i != std::string::npos);
|
|
|
|
s = s.substr(i+1);
|
|
|
|
}
|
|
|
|
else if (isa<CXXOperatorCallExpr>(condExpr))
|
|
|
|
s = "!(" + s + ")";
|
|
|
|
else if (isa<DeclRefExpr>(condExpr) || isa<CallExpr>(condExpr) || isa<MemberExpr>(condExpr))
|
|
|
|
s = "!" + s;
|
|
|
|
else
|
|
|
|
s = "!(" + s + ")";
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2017-09-20 12:18:04 +02:00
|
|
|
std::string stripOpenAndCloseBrace(std::string s)
|
|
|
|
{
|
2017-09-21 11:06:43 +02:00
|
|
|
size_t i = s.find("{");
|
2017-09-21 15:42:49 +02:00
|
|
|
if (i == std::string::npos)
|
|
|
|
throw "did not find {";
|
|
|
|
|
|
|
|
++i;
|
|
|
|
// strip to line end
|
|
|
|
while (s[i] == ' ')
|
2017-09-21 11:06:43 +02:00
|
|
|
++i;
|
2017-09-21 15:42:49 +02:00
|
|
|
if (s[i] == '\n')
|
|
|
|
++i;
|
|
|
|
s = s.substr(i);
|
|
|
|
|
2017-09-21 11:06:43 +02:00
|
|
|
i = s.rfind("}");
|
2017-09-21 15:42:49 +02:00
|
|
|
if (i == std::string::npos)
|
|
|
|
throw "did not find }";
|
|
|
|
--i;
|
|
|
|
while (s[i] == ' ')
|
2017-09-21 11:06:43 +02:00
|
|
|
--i;
|
2017-09-21 15:42:49 +02:00
|
|
|
s = s.substr(0,i);
|
2017-09-20 12:18:04 +02:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2017-09-21 11:06:43 +02:00
|
|
|
std::string deindent(std::string const & s)
|
2017-09-20 12:18:04 +02:00
|
|
|
{
|
|
|
|
std::vector<std::string> lines = split(s);
|
|
|
|
std::string rv;
|
|
|
|
for (auto s : lines) {
|
2017-09-21 11:06:43 +02:00
|
|
|
if (startswith(s, " "))
|
|
|
|
rv += s.substr(4);
|
|
|
|
else
|
|
|
|
rv += s;
|
2017-09-20 12:18:04 +02:00
|
|
|
rv += "\n";
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2017-09-21 11:06:43 +02:00
|
|
|
std::vector<std::string> split(std::string s)
|
2017-09-20 12:18:04 +02:00
|
|
|
{
|
2017-09-21 11:06:43 +02:00
|
|
|
if (s.back() == '\n')
|
|
|
|
s = s.substr(0, s.size()-1);
|
2017-09-20 12:18:04 +02:00
|
|
|
size_t next = -1;
|
|
|
|
std::vector<std::string> rv;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
size_t current = next + 1;
|
|
|
|
next = s.find_first_of( "\n", current );
|
|
|
|
rv.push_back(s.substr( current, next - current ));
|
|
|
|
}
|
|
|
|
while (next != std::string::npos);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2017-09-28 10:33:09 +02:00
|
|
|
static bool startswith(std::string const & rStr, char const * pSubStr)
|
2017-09-21 11:06:43 +02:00
|
|
|
{
|
|
|
|
return rStr.compare(0, strlen(pSubStr), pSubStr) == 0;
|
|
|
|
}
|
|
|
|
|
2017-09-20 12:18:04 +02:00
|
|
|
SourceRange Flatten::ignoreMacroExpansions(SourceRange range) {
|
|
|
|
while (compiler.getSourceManager().isMacroArgExpansion(range.getBegin())) {
|
|
|
|
range.setBegin(
|
|
|
|
compiler.getSourceManager().getImmediateMacroCallerLoc(
|
|
|
|
range.getBegin()));
|
|
|
|
}
|
|
|
|
if (range.getBegin().isMacroID()) {
|
|
|
|
SourceLocation loc;
|
|
|
|
if (Lexer::isAtStartOfMacroExpansion(
|
|
|
|
range.getBegin(), compiler.getSourceManager(),
|
|
|
|
compiler.getLangOpts(), &loc))
|
|
|
|
{
|
|
|
|
range.setBegin(loc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (compiler.getSourceManager().isMacroArgExpansion(range.getEnd())) {
|
|
|
|
range.setEnd(
|
|
|
|
compiler.getSourceManager().getImmediateMacroCallerLoc(
|
|
|
|
range.getEnd()));
|
|
|
|
}
|
|
|
|
if (range.getEnd().isMacroID()) {
|
|
|
|
SourceLocation loc;
|
|
|
|
if (Lexer::isAtEndOfMacroExpansion(
|
|
|
|
range.getEnd(), compiler.getSourceManager(),
|
|
|
|
compiler.getLangOpts(), &loc))
|
|
|
|
{
|
|
|
|
range.setEnd(loc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return range.getBegin().isMacroID() || range.getEnd().isMacroID()
|
|
|
|
? SourceRange() : range;
|
|
|
|
}
|
|
|
|
|
2017-09-21 11:06:43 +02:00
|
|
|
/**
|
|
|
|
* Extend the SourceRange to include any leading and trailing whitespace, and any comments.
|
|
|
|
*/
|
|
|
|
SourceRange Flatten::extendOverComments(SourceRange range)
|
|
|
|
{
|
|
|
|
SourceManager& SM = compiler.getSourceManager();
|
|
|
|
SourceLocation startLoc = range.getBegin();
|
|
|
|
SourceLocation endLoc = range.getEnd();
|
2017-09-28 10:33:09 +02:00
|
|
|
char const *p1 = SM.getCharacterData( startLoc );
|
|
|
|
char const *p2 = SM.getCharacterData( endLoc );
|
2017-09-21 11:06:43 +02:00
|
|
|
|
|
|
|
// scan backwards from the beginning to include any spaces on that line
|
|
|
|
while (*(p1-1) == ' ')
|
|
|
|
--p1;
|
|
|
|
startLoc = startLoc.getLocWithOffset(p1 - SM.getCharacterData( startLoc ));
|
|
|
|
|
|
|
|
// look for trailing ";"
|
2017-09-22 08:50:20 +02:00
|
|
|
while (*(p2+1) == ';')
|
2017-09-21 11:06:43 +02:00
|
|
|
++p2;
|
|
|
|
// look for trailing " "
|
2017-09-22 08:50:20 +02:00
|
|
|
while (*(p2+1) == ' ')
|
2017-09-21 11:06:43 +02:00
|
|
|
++p2;
|
|
|
|
// look for single line comments attached to the end of the statement
|
2017-09-22 08:50:20 +02:00
|
|
|
if (*(p2+1) == '/' && *(p2+2) == '/')
|
2017-09-21 11:06:43 +02:00
|
|
|
{
|
|
|
|
p2 += 2;
|
2017-09-22 08:50:20 +02:00
|
|
|
while (*(p2+1) && *(p2+1) != '\n')
|
2017-09-21 11:06:43 +02:00
|
|
|
++p2;
|
2017-09-22 08:50:20 +02:00
|
|
|
if (*(p2+1) == '\n')
|
2017-09-21 11:06:43 +02:00
|
|
|
++p2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// make the source code we extract include any trailing "\n"
|
2017-09-22 08:50:20 +02:00
|
|
|
if (*(p2+1) == '\n')
|
2017-09-21 11:06:43 +02:00
|
|
|
++p2;
|
|
|
|
}
|
|
|
|
endLoc = endLoc.getLocWithOffset(p2 - SM.getCharacterData( endLoc ));
|
|
|
|
|
|
|
|
return SourceRange(startLoc, endLoc);
|
|
|
|
}
|
|
|
|
|
2017-09-20 12:18:04 +02:00
|
|
|
std::string Flatten::getSourceAsString(SourceRange range)
|
|
|
|
{
|
|
|
|
SourceManager& SM = compiler.getSourceManager();
|
|
|
|
SourceLocation startLoc = range.getBegin();
|
|
|
|
SourceLocation endLoc = range.getEnd();
|
2017-09-28 10:33:09 +02:00
|
|
|
char const *p1 = SM.getCharacterData( startLoc );
|
|
|
|
char const *p2 = SM.getCharacterData( endLoc );
|
2017-09-21 11:06:43 +02:00
|
|
|
p2 += Lexer::MeasureTokenLength( endLoc, SM, compiler.getLangOpts());
|
|
|
|
return std::string( p1, p2 - p1);
|
2017-09-20 12:18:04 +02:00
|
|
|
}
|
|
|
|
|
2017-09-28 10:33:09 +02:00
|
|
|
loplugin::Plugin::Registration< Flatten > X("flatten", true);
|
2017-09-20 12:18:04 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|