123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- //===--- DefinitionBlockSeparator.cpp ---------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- ///
- /// \file
- /// This file implements DefinitionBlockSeparator, a TokenAnalyzer that inserts
- /// or removes empty lines separating definition blocks like classes, structs,
- /// functions, enums, and namespaces in between.
- ///
- //===----------------------------------------------------------------------===//
- #include "DefinitionBlockSeparator.h"
- #include "llvm/Support/Debug.h"
- #define DEBUG_TYPE "definition-block-separator"
- namespace clang {
- namespace format {
- std::pair<tooling::Replacements, unsigned> DefinitionBlockSeparator::analyze(
- TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
- FormatTokenLexer &Tokens) {
- assert(Style.SeparateDefinitionBlocks != FormatStyle::SDS_Leave);
- AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
- tooling::Replacements Result;
- separateBlocks(AnnotatedLines, Result, Tokens);
- return {Result, 0};
- }
- void DefinitionBlockSeparator::separateBlocks(
- SmallVectorImpl<AnnotatedLine *> &Lines, tooling::Replacements &Result,
- FormatTokenLexer &Tokens) {
- const bool IsNeverStyle =
- Style.SeparateDefinitionBlocks == FormatStyle::SDS_Never;
- const AdditionalKeywords &ExtraKeywords = Tokens.getKeywords();
- auto GetBracketLevelChange = [](const FormatToken *Tok) {
- if (Tok->isOneOf(tok::l_brace, tok::l_paren, tok::l_square))
- return 1;
- if (Tok->isOneOf(tok::r_brace, tok::r_paren, tok::r_square))
- return -1;
- return 0;
- };
- auto LikelyDefinition = [&](const AnnotatedLine *Line,
- bool ExcludeEnum = false) {
- if ((Line->MightBeFunctionDecl && Line->mightBeFunctionDefinition()) ||
- Line->startsWithNamespace())
- return true;
- int BracketLevel = 0;
- for (const FormatToken *CurrentToken = Line->First; CurrentToken;
- CurrentToken = CurrentToken->Next) {
- if (BracketLevel == 0) {
- if ((CurrentToken->isOneOf(tok::kw_class, tok::kw_struct,
- tok::kw_union) ||
- (Style.isJavaScript() &&
- CurrentToken->is(ExtraKeywords.kw_function))))
- return true;
- if (!ExcludeEnum && CurrentToken->is(tok::kw_enum))
- return true;
- }
- BracketLevel += GetBracketLevelChange(CurrentToken);
- }
- return false;
- };
- unsigned NewlineCount =
- (Style.SeparateDefinitionBlocks == FormatStyle::SDS_Always ? 1 : 0) + 1;
- WhitespaceManager Whitespaces(
- Env.getSourceManager(), Style,
- Style.DeriveLineEnding
- ? WhitespaceManager::inputUsesCRLF(
- Env.getSourceManager().getBufferData(Env.getFileID()),
- Style.UseCRLF)
- : Style.UseCRLF);
- for (unsigned I = 0; I < Lines.size(); ++I) {
- const auto &CurrentLine = Lines[I];
- if (CurrentLine->InPPDirective)
- continue;
- FormatToken *TargetToken = nullptr;
- AnnotatedLine *TargetLine;
- auto OpeningLineIndex = CurrentLine->MatchingOpeningBlockLineIndex;
- AnnotatedLine *OpeningLine = nullptr;
- const auto IsAccessSpecifierToken = [](const FormatToken *Token) {
- return Token->isAccessSpecifier() || Token->isObjCAccessSpecifier();
- };
- const auto InsertReplacement = [&](const int NewlineToInsert) {
- assert(TargetLine);
- assert(TargetToken);
- // Do not handle EOF newlines.
- if (TargetToken->is(tok::eof))
- return;
- if (IsAccessSpecifierToken(TargetToken) ||
- (OpeningLineIndex > 0 &&
- IsAccessSpecifierToken(Lines[OpeningLineIndex - 1]->First)))
- return;
- if (!TargetLine->Affected)
- return;
- Whitespaces.replaceWhitespace(*TargetToken, NewlineToInsert,
- TargetToken->OriginalColumn,
- TargetToken->OriginalColumn);
- };
- const auto IsPPConditional = [&](const size_t LineIndex) {
- const auto &Line = Lines[LineIndex];
- return Line->First->is(tok::hash) && Line->First->Next &&
- Line->First->Next->isOneOf(tok::pp_if, tok::pp_ifdef, tok::pp_else,
- tok::pp_ifndef, tok::pp_elifndef,
- tok::pp_elifdef, tok::pp_elif,
- tok::pp_endif);
- };
- const auto FollowingOtherOpening = [&]() {
- return OpeningLineIndex == 0 ||
- Lines[OpeningLineIndex - 1]->Last->opensScope() ||
- IsPPConditional(OpeningLineIndex - 1);
- };
- const auto HasEnumOnLine = [&]() {
- bool FoundEnumKeyword = false;
- int BracketLevel = 0;
- for (const FormatToken *CurrentToken = CurrentLine->First; CurrentToken;
- CurrentToken = CurrentToken->Next) {
- if (BracketLevel == 0) {
- if (CurrentToken->is(tok::kw_enum))
- FoundEnumKeyword = true;
- else if (FoundEnumKeyword && CurrentToken->is(tok::l_brace))
- return true;
- }
- BracketLevel += GetBracketLevelChange(CurrentToken);
- }
- return FoundEnumKeyword && I + 1 < Lines.size() &&
- Lines[I + 1]->First->is(tok::l_brace);
- };
- bool IsDefBlock = false;
- const auto MayPrecedeDefinition = [&](const int Direction = -1) {
- assert(Direction >= -1);
- assert(Direction <= 1);
- const size_t OperateIndex = OpeningLineIndex + Direction;
- assert(OperateIndex < Lines.size());
- const auto &OperateLine = Lines[OperateIndex];
- if (LikelyDefinition(OperateLine))
- return false;
- if (OperateLine->First->is(tok::comment))
- return true;
- // A single line identifier that is not in the last line.
- if (OperateLine->First->is(tok::identifier) &&
- OperateLine->First == OperateLine->Last &&
- OperateIndex + 1 < Lines.size()) {
- // UnwrappedLineParser's recognition of free-standing macro like
- // Q_OBJECT may also recognize some uppercased type names that may be
- // used as return type as that kind of macros, which is a bit hard to
- // distinguish one from another purely from token patterns. Here, we
- // try not to add new lines below those identifiers.
- AnnotatedLine *NextLine = Lines[OperateIndex + 1];
- if (NextLine->MightBeFunctionDecl &&
- NextLine->mightBeFunctionDefinition() &&
- NextLine->First->NewlinesBefore == 1 &&
- OperateLine->First->is(TT_FunctionLikeOrFreestandingMacro))
- return true;
- }
- if ((Style.isCSharp() && OperateLine->First->is(TT_AttributeSquare)))
- return true;
- return false;
- };
- if (HasEnumOnLine() &&
- !LikelyDefinition(CurrentLine, /*ExcludeEnum=*/true)) {
- // We have no scope opening/closing information for enum.
- IsDefBlock = true;
- OpeningLineIndex = I;
- while (OpeningLineIndex > 0 && MayPrecedeDefinition())
- --OpeningLineIndex;
- OpeningLine = Lines[OpeningLineIndex];
- TargetLine = OpeningLine;
- TargetToken = TargetLine->First;
- if (!FollowingOtherOpening())
- InsertReplacement(NewlineCount);
- else if (IsNeverStyle)
- InsertReplacement(OpeningLineIndex != 0);
- TargetLine = CurrentLine;
- TargetToken = TargetLine->First;
- while (TargetToken && !TargetToken->is(tok::r_brace))
- TargetToken = TargetToken->Next;
- if (!TargetToken) {
- while (I < Lines.size() && !Lines[I]->First->is(tok::r_brace))
- ++I;
- }
- } else if (CurrentLine->First->closesScope()) {
- if (OpeningLineIndex > Lines.size())
- continue;
- // Handling the case that opening brace has its own line, with checking
- // whether the last line already had an opening brace to guard against
- // misrecognition.
- if (OpeningLineIndex > 0 &&
- Lines[OpeningLineIndex]->First->is(tok::l_brace) &&
- Lines[OpeningLineIndex - 1]->Last->isNot(tok::l_brace))
- --OpeningLineIndex;
- OpeningLine = Lines[OpeningLineIndex];
- // Closing a function definition.
- if (LikelyDefinition(OpeningLine)) {
- IsDefBlock = true;
- while (OpeningLineIndex > 0 && MayPrecedeDefinition())
- --OpeningLineIndex;
- OpeningLine = Lines[OpeningLineIndex];
- TargetLine = OpeningLine;
- TargetToken = TargetLine->First;
- if (!FollowingOtherOpening()) {
- // Avoid duplicated replacement.
- if (TargetToken->isNot(tok::l_brace))
- InsertReplacement(NewlineCount);
- } else if (IsNeverStyle)
- InsertReplacement(OpeningLineIndex != 0);
- }
- }
- // Not the last token.
- if (IsDefBlock && I + 1 < Lines.size()) {
- OpeningLineIndex = I + 1;
- TargetLine = Lines[OpeningLineIndex];
- TargetToken = TargetLine->First;
- // No empty line for continuously closing scopes. The token will be
- // handled in another case if the line following is opening a
- // definition.
- if (!TargetToken->closesScope() && !IsPPConditional(OpeningLineIndex)) {
- // Check whether current line may precede a definition line.
- while (OpeningLineIndex + 1 < Lines.size() &&
- MayPrecedeDefinition(/*Direction=*/0))
- ++OpeningLineIndex;
- TargetLine = Lines[OpeningLineIndex];
- if (!LikelyDefinition(TargetLine)) {
- OpeningLineIndex = I + 1;
- TargetLine = Lines[I + 1];
- TargetToken = TargetLine->First;
- InsertReplacement(NewlineCount);
- }
- } else if (IsNeverStyle)
- InsertReplacement(/*NewlineToInsert=*/1);
- }
- }
- for (const auto &R : Whitespaces.generateReplacements())
- // The add method returns an Error instance which simulates program exit
- // code through overloading boolean operator, thus false here indicates
- // success.
- if (Result.add(R))
- return;
- }
- } // namespace format
- } // namespace clang
|