Skip to content

Commit

Permalink
Added Fixer implementation and fix() interface in clang-format for re…
Browse files Browse the repository at this point in the history
…moving redundant code.

Summary:
After applying replacements, redundant code like extra commas or empty namespaces
might be introduced. Fixer can detect and remove any redundant code introduced by replacements.
The current implementation only handles redundant commas.

Reviewers: djasper, klimek

Subscribers: ioeric, mprobst, klimek, cfe-commits

Differential Revision: http://reviews.llvm.org/D18551

llvm-svn: 267416
  • Loading branch information
Eric Liu committed Apr 25, 2016
1 parent 06c14ec commit 4cfb88a
Show file tree
Hide file tree
Showing 9 changed files with 835 additions and 258 deletions.
22 changes: 22 additions & 0 deletions clang/include/clang/Format/Format.h
Expand Up @@ -771,6 +771,12 @@ tooling::Replacements formatReplacements(StringRef Code,
const tooling::Replacements &Replaces,
const FormatStyle &Style);

/// \brief Returns the replacements corresponding to applying \p Replaces and
/// cleaning up the code after that.
tooling::Replacements
cleanupAroundReplacements(StringRef Code, const tooling::Replacements &Replaces,
const FormatStyle &Style);

/// \brief Reformats the given \p Ranges in the file \p ID.
///
/// Each range is extended on either end to its next bigger logic unit, i.e.
Expand All @@ -796,6 +802,22 @@ tooling::Replacements reformat(const FormatStyle &Style, StringRef Code,
StringRef FileName = "<stdin>",
bool *IncompleteFormat = nullptr);

/// \brief Clean up any erroneous/redundant code in the given \p Ranges in the
/// file \p ID.
///
/// Returns the ``Replacements`` that clean up all \p Ranges in the file \p ID.
tooling::Replacements cleanup(const FormatStyle &Style,
SourceManager &SourceMgr, FileID ID,
ArrayRef<CharSourceRange> Ranges);

/// \brief Clean up any erroneous/redundant code in the given \p Ranges in \p
/// Code.
///
/// Otherwise identical to the cleanup() function using a file ID.
tooling::Replacements cleanup(const FormatStyle &Style, StringRef Code,
ArrayRef<tooling::Range> Ranges,
StringRef FileName = "<stdin>");

/// \brief Returns the ``LangOpts`` that the formatter expects you to set.
///
/// \param Style determines specific settings for lexing mode.
Expand Down
150 changes: 150 additions & 0 deletions clang/lib/Format/AffectedRangeManager.cpp
@@ -0,0 +1,150 @@
//===--- AffectedRangeManager.cpp - Format C++ code -----------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file implements AffectRangeManager class.
///
//===----------------------------------------------------------------------===//

#include "AffectedRangeManager.h"

#include "FormatToken.h"
#include "TokenAnnotator.h"

namespace clang {
namespace format {

bool AffectedRangeManager::computeAffectedLines(
SmallVectorImpl<AnnotatedLine *>::iterator I,
SmallVectorImpl<AnnotatedLine *>::iterator E) {
bool SomeLineAffected = false;
const AnnotatedLine *PreviousLine = nullptr;
while (I != E) {
AnnotatedLine *Line = *I;
Line->LeadingEmptyLinesAffected = affectsLeadingEmptyLines(*Line->First);

// If a line is part of a preprocessor directive, it needs to be formatted
// if any token within the directive is affected.
if (Line->InPPDirective) {
FormatToken *Last = Line->Last;
SmallVectorImpl<AnnotatedLine *>::iterator PPEnd = I + 1;
while (PPEnd != E && !(*PPEnd)->First->HasUnescapedNewline) {
Last = (*PPEnd)->Last;
++PPEnd;
}

if (affectsTokenRange(*Line->First, *Last,
/*IncludeLeadingNewlines=*/false)) {
SomeLineAffected = true;
markAllAsAffected(I, PPEnd);
}
I = PPEnd;
continue;
}

if (nonPPLineAffected(Line, PreviousLine))
SomeLineAffected = true;

PreviousLine = Line;
++I;
}
return SomeLineAffected;
}

bool AffectedRangeManager::affectsCharSourceRange(
const CharSourceRange &Range) {
for (SmallVectorImpl<CharSourceRange>::const_iterator I = Ranges.begin(),
E = Ranges.end();
I != E; ++I) {
if (!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), I->getBegin()) &&
!SourceMgr.isBeforeInTranslationUnit(I->getEnd(), Range.getBegin()))
return true;
}
return false;
}

bool AffectedRangeManager::affectsTokenRange(const FormatToken &First,
const FormatToken &Last,
bool IncludeLeadingNewlines) {
SourceLocation Start = First.WhitespaceRange.getBegin();
if (!IncludeLeadingNewlines)
Start = Start.getLocWithOffset(First.LastNewlineOffset);
SourceLocation End = Last.getStartOfNonWhitespace();
End = End.getLocWithOffset(Last.TokenText.size());
CharSourceRange Range = CharSourceRange::getCharRange(Start, End);
return affectsCharSourceRange(Range);
}

bool AffectedRangeManager::affectsLeadingEmptyLines(const FormatToken &Tok) {
CharSourceRange EmptyLineRange = CharSourceRange::getCharRange(
Tok.WhitespaceRange.getBegin(),
Tok.WhitespaceRange.getBegin().getLocWithOffset(Tok.LastNewlineOffset));
return affectsCharSourceRange(EmptyLineRange);
}

void AffectedRangeManager::markAllAsAffected(
SmallVectorImpl<AnnotatedLine *>::iterator I,
SmallVectorImpl<AnnotatedLine *>::iterator E) {
while (I != E) {
(*I)->Affected = true;
markAllAsAffected((*I)->Children.begin(), (*I)->Children.end());
++I;
}
}

bool AffectedRangeManager::nonPPLineAffected(
AnnotatedLine *Line, const AnnotatedLine *PreviousLine) {
bool SomeLineAffected = false;
Line->ChildrenAffected =
computeAffectedLines(Line->Children.begin(), Line->Children.end());
if (Line->ChildrenAffected)
SomeLineAffected = true;

// Stores whether one of the line's tokens is directly affected.
bool SomeTokenAffected = false;
// Stores whether we need to look at the leading newlines of the next token
// in order to determine whether it was affected.
bool IncludeLeadingNewlines = false;

// Stores whether the first child line of any of this line's tokens is
// affected.
bool SomeFirstChildAffected = false;

for (FormatToken *Tok = Line->First; Tok; Tok = Tok->Next) {
// Determine whether 'Tok' was affected.
if (affectsTokenRange(*Tok, *Tok, IncludeLeadingNewlines))
SomeTokenAffected = true;

// Determine whether the first child of 'Tok' was affected.
if (!Tok->Children.empty() && Tok->Children.front()->Affected)
SomeFirstChildAffected = true;

IncludeLeadingNewlines = Tok->Children.empty();
}

// Was this line moved, i.e. has it previously been on the same line as an
// affected line?
bool LineMoved = PreviousLine && PreviousLine->Affected &&
Line->First->NewlinesBefore == 0;

bool IsContinuedComment =
Line->First->is(tok::comment) && Line->First->Next == nullptr &&
Line->First->NewlinesBefore < 2 && PreviousLine &&
PreviousLine->Affected && PreviousLine->Last->is(tok::comment);

if (SomeTokenAffected || SomeFirstChildAffected || LineMoved ||
IsContinuedComment) {
Line->Affected = true;
SomeLineAffected = true;
}
return SomeLineAffected;
}

} // namespace format
} // namespace clang
66 changes: 66 additions & 0 deletions clang/lib/Format/AffectedRangeManager.h
@@ -0,0 +1,66 @@
//===--- AffectedRangeManager.h - Format C++ code ---------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief AffectedRangeManager class manages affected ranges in the code.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LIB_FORMAT_AFFECTEDRANGEMANAGER_H
#define LLVM_CLANG_LIB_FORMAT_AFFECTEDRANGEMANAGER_H

#include "clang/Basic/SourceManager.h"

namespace clang {
namespace format {

struct FormatToken;
class AnnotatedLine;

class AffectedRangeManager {
public:
AffectedRangeManager(SourceManager &SourceMgr,
const ArrayRef<CharSourceRange> Ranges)
: SourceMgr(SourceMgr), Ranges(Ranges.begin(), Ranges.end()) {}

// Determines which lines are affected by the SourceRanges given as input.
// Returns \c true if at least one line between I and E or one of their
// children is affected.
bool computeAffectedLines(SmallVectorImpl<AnnotatedLine *>::iterator I,
SmallVectorImpl<AnnotatedLine *>::iterator E);

// Returns true if 'Range' intersects with one of the input ranges.
bool affectsCharSourceRange(const CharSourceRange &Range);

private:
// Returns true if the range from 'First' to 'Last' intersects with one of the
// input ranges.
bool affectsTokenRange(const FormatToken &First, const FormatToken &Last,
bool IncludeLeadingNewlines);

// Returns true if one of the input ranges intersect the leading empty lines
// before 'Tok'.
bool affectsLeadingEmptyLines(const FormatToken &Tok);

// Marks all lines between I and E as well as all their children as affected.
void markAllAsAffected(SmallVectorImpl<AnnotatedLine *>::iterator I,
SmallVectorImpl<AnnotatedLine *>::iterator E);

// Determines whether 'Line' is affected by the SourceRanges given as input.
// Returns \c true if line or one if its children is affected.
bool nonPPLineAffected(AnnotatedLine *Line,
const AnnotatedLine *PreviousLine);
SourceManager &SourceMgr;
const SmallVector<CharSourceRange, 8> Ranges;
};

} // namespace format
} // namespace clang

#endif // LLVM_CLANG_LIB_FORMAT_WHITESPACEMANAGER_H
1 change: 1 addition & 0 deletions clang/lib/Format/CMakeLists.txt
@@ -1,6 +1,7 @@
set(LLVM_LINK_COMPONENTS support)

add_clang_library(clangFormat
AffectedRangeManager.cpp
BreakableToken.cpp
ContinuationIndenter.cpp
Format.cpp
Expand Down

0 comments on commit 4cfb88a

Please sign in to comment.